feat(java/core): add fluent ingest API - #4466
Conversation
amoeba
left a comment
There was a problem hiding this comment.
This looks really nice. I left some comments and questions.
| import org.apache.arrow.vector.ipc.ArrowReader; | ||
|
|
||
| /** A fluent builder-style interface for configuring and executing a bulk ingest. */ | ||
| public interface BulkIngestBuilder extends AutoCloseable { |
There was a problem hiding this comment.
It would be good to add javadoc comments to these interface methods, something like,
diff --git a/java/core/src/main/java/org/apache/arrow/adbc/core/BulkIngestBuilder.java b/java/core/src/main/java/org/apache/arrow/adbc/core/BulkIngestBuilder.java
index d8b52f815..6a96f31e9 100644
--- a/java/core/src/main/java/org/apache/arrow/adbc/core/BulkIngestBuilder.java
+++ b/java/core/src/main/java/org/apache/arrow/adbc/core/BulkIngestBuilder.java
@@ -21,38 +21,128 @@ import org.apache.arrow.vector.ipc.ArrowReader;
/** A fluent builder-style interface for configuring and executing a bulk ingest. */
public interface BulkIngestBuilder extends AutoCloseable {
+ /**
+ * Set the ingest mode to {@link BulkIngestMode#CREATE}: create the table and insert data, error
+ * if the table already exists.
+ */
BulkIngestBuilder create() throws AdbcException;
+ /**
+ * Set the ingest mode to {@link BulkIngestMode#APPEND}: append data to an existing table, error
+ * if the table does not exist or its schema does not match.
+ */
BulkIngestBuilder append() throws AdbcException;
+ /**
+ * Set the ingest mode to {@link BulkIngestMode#REPLACE}: drop the existing table (if any) and
+ * create a new one, then insert data.
+ *
+ * @since ADBC API revision 1.1.0
+ */
BulkIngestBuilder replace() throws AdbcException;
+ /**
+ * Set the ingest mode to {@link BulkIngestMode#CREATE_APPEND}: insert data, creating the table
+ * if it does not exist; error if the table exists but the schema does not match.
+ */
BulkIngestBuilder createAppend() throws AdbcException;
+ /**
+ * Set the ingest mode explicitly.
+ *
+ * <p>Prefer the convenience methods {@link #create()}, {@link #append()}, {@link #replace()}, and
+ * {@link #createAppend()} when possible.
+ *
+ * @param mode the ingest mode to use
+ */
BulkIngestBuilder mode(BulkIngestMode mode) throws AdbcException;
+ /**
+ * Bind a {@link VectorSchemaRoot} as the data source for ingestion.
+ *
+ * <p>The builder will NOT close the root after use.
+ *
+ * @param root the data to ingest
+ */
BulkIngestBuilder bind(VectorSchemaRoot root) throws AdbcException;
+ /**
+ * Bind an {@link ArrowReader} as the data source for ingestion.
+ *
+ * <p>The underlying statement will close the reader after use.
+ *
+ * @param stream the data to ingest
+ */
BulkIngestBuilder bind(ArrowReader stream) throws AdbcException;
+ /**
+ * Set the name of the target table to ingest into.
+ *
+ * @param table the table name (required)
+ */
BulkIngestBuilder targetTable(String table) throws AdbcException;
+ /**
+ * Set the schema that contains the target table.
+ *
+ * @param schema the schema name, or {@code null} to use the connection default
+ */
BulkIngestBuilder targetSchema(String schema) throws AdbcException;
+ /**
+ * Set the catalog that contains the target table.
+ *
+ * @param catalog the catalog name, or {@code null} to use the connection default
+ */
BulkIngestBuilder targetCatalog(String catalog) throws AdbcException;
+ /**
+ * Mark the target table as temporary.
+ *
+ * <p>Equivalent to calling {@link #temporary(boolean) temporary(true)}.
+ */
default BulkIngestBuilder temporary() throws AdbcException {
return this.temporary(true);
}
+ /**
+ * Control whether the target table should be created as a temporary table.
+ *
+ * @param isTemporary {@code true} to create a temporary table, {@code false} for a persistent one
+ */
BulkIngestBuilder temporary(boolean isTemporary) throws AdbcException;
+ /**
+ * Set a driver-specific ingest option.
+ *
+ * @param option the option to set
+ */
BulkIngestBuilder option(IngestOption option) throws AdbcException;
+ /**
+ * Set a typed driver-specific ingest option.
+ *
+ * @param key the option key
+ * @param value the option value
+ */
<T> BulkIngestBuilder option(TypedKey<T> key, T value) throws AdbcException;
+ /**
+ * Build and return the configured {@link AdbcStatement} without executing it.
+ *
+ * <p>The caller is responsible for closing the returned statement. Prefer {@link #ingest()} when
+ * you do not need to inspect or reuse the statement.
+ */
AdbcStatement toStatement() throws AdbcException;
+ /**
+ * Execute the ingest and return the number of affected rows.
+ *
+ * <p>This is a convenience method that calls {@link #toStatement()}, executes it, closes it, and
+ * returns the {@link AdbcStatement.UpdateResult}.
+ *
+ * @return the result of the update, including the number of rows affected
+ */
default AdbcStatement.UpdateResult ingest() throws AdbcException {
try (var statement = toStatement()) {
return statement.executeUpdate();
| BulkIngestBuilder create() throws AdbcException; | ||
|
|
||
| BulkIngestBuilder append() throws AdbcException; | ||
|
|
||
| BulkIngestBuilder replace() throws AdbcException; | ||
|
|
||
| BulkIngestBuilder createAppend() throws AdbcException; |
There was a problem hiding this comment.
These helpers that are specific to each ingest mode are nice but each one is kinda verbish so the user ends up writing,
...
.create()
.ingest()That is, a method like .create() kinda feels like it should take action.
Maybe an alternative would be to have a finalize method on the builder like,
...
.create()
.finish()
b.ingest()Just thinking out loud here.
There was a problem hiding this comment.
I changed this to say createMode etc. instead.
|
|
||
| <T> BulkIngestBuilder option(TypedKey<T> key, T value) throws AdbcException; | ||
|
|
||
| AdbcStatement toStatement() throws AdbcException; |
There was a problem hiding this comment.
I think this would be the way for users to cancel their ingest after they run .ingest() right? Would be good to document in the javadoc comment that goes here.
Add a more natural API for bulk ingest that (by default) hides the statement within, making for a clearer API.