This is an automated email from the ASF dual-hosted git repository.
curth 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 ddb1bcf7d feat(csharp): redefine C# APIs to prioritize full async
support (#1865)
ddb1bcf7d is described below
commit ddb1bcf7dfb249dc3e958678bf4d65bd205ca58f
Author: Curt Hagenlocher <[email protected]>
AuthorDate: Sun May 19 13:49:22 2024 -0700
feat(csharp): redefine C# APIs to prioritize full async support (#1865)
For #1843, redefines the C# APIs to prioritize full async support. As
this is making a number of breaking changes already, it also takes the
opportunity to do some general cleanup.
Async methods that are generally expected to run locally (e.g.
GetOption/SetOption) are defined to return ValueTask and have their
default implementation be synchronous. Async methods that are generally
expected to run remotely are defined to return Task and have their
default implementations be asynchronous.
My mental model for the four ADBC "object" types is as follows:
The driver is analogous to the ODBC or JDBC driver, or ADO.NET provider.
In JDBC, this type is represented by the java.sql.Driver interface. In
ADO.NET, it's represented by the DbProviderFactory class. Because this
object is strictly about code, it's not expected to do any IO other than
that potentially required by any code e.g. to bring in pages of a binary
image from disk.
The database is analogous to the JDBC DataSource, the ODBC "DSN" or the
ADO.NET connection string. It represents the information and capability
required to create a database connection but does not itself do IO until
it tries to create a connection. (This would imply that parameter
validation which requires network access -- e.g. to validate a host name
-- is deferred until the connection is created. Perhaps that's too
limiting?)
Because neither the driver nor the database is doing IO, neither of them
need to have async methods other than Connect, including async cleanup.
The connection represents an actual session with a database. This
matches an ODBC connection, a JDBC java.sql.Connection or an ADO.NET
DbConnection. Opening a connection, closing it or using it to fetch
information about the data source are all operations likely to require
IO so these all require async implementations.
The statement is a unit of bookkeeping related to certain types of
database operations. In some cases, a connection can only have a single
active statement running against it, but it can be useful to have
multiple statements even then if, for instance, each one is a prepared
statement that represents both client-side and server-side resources.
The statement is analogous to an ODBC statement, a JDBC
java.sql.Statement or an ADO.NET DbCommand. Due to the need to clean up
an in progress operation or to release server-side resources, the
cleanup of a statement might do IO and should therefore support
asynchrony. But when the statement is first created, it only represents
the potential for future work and so creation is always synchronous.
I'd be curious to hear how well this aligns with others' points of view.
lidavidm? davidhcoe? ("ping" removed)
---
csharp/src/Apache.Arrow.Adbc/AdbcConnection11.cs | 426 +++++++++++++++++++++++
csharp/src/Apache.Arrow.Adbc/AdbcDatabase11.cs | 85 +++++
csharp/src/Apache.Arrow.Adbc/AdbcDriver11.cs | 54 +++
csharp/src/Apache.Arrow.Adbc/AdbcStatement11.cs | 276 +++++++++++++++
csharp/src/Apache.Arrow.Adbc/Results.cs | 2 +-
5 files changed, 842 insertions(+), 1 deletion(-)
diff --git a/csharp/src/Apache.Arrow.Adbc/AdbcConnection11.cs
b/csharp/src/Apache.Arrow.Adbc/AdbcConnection11.cs
new file mode 100644
index 000000000..e4f79cbbc
--- /dev/null
+++ b/csharp/src/Apache.Arrow.Adbc/AdbcConnection11.cs
@@ -0,0 +1,426 @@
+/*
+ * 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.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Apache.Arrow.Ipc;
+
+namespace Apache.Arrow.Adbc
+{
+ /// <summary>
+ /// Provides methods for query execution, managing prepared statements,
+ /// using transactions, and so on.
+ /// </summary>
+ public abstract class AdbcConnection11 : IDisposable
+#if NET5_0_OR_GREATER
+ , IAsyncDisposable
+#endif
+ {
+ ~AdbcConnection11() => Dispose(false);
+
+ /// <summary>
+ /// Attempts to cancel an in-progress operation on a connection.
+ /// </summary>
+ /// <remarks>
+ /// This can be called during a method like GetObjects or while
consuming an ArrowArrayStream
+ /// returned from such. Calling this function should make the other
function throw a cancellation exception.
+ ///
+ /// This must always be thread-safe.
+ /// </remarks>
+ public virtual void Cancel()
+ {
+ throw AdbcException.NotImplemented("Connection does not support
cancellation");
+ }
+
+ /// <summary>
+ /// Starts a new transaction with the given isolation level
+ /// </summary>
+ /// <param name="isolationLevel">The isolation level for the new
transaction.</param>
+ public virtual void BeginTransaction(IsolationLevel? isolationLevel =
default)
+ {
+ Task.Run(() =>
BeginTransactionAsync(isolationLevel)).GetAwaiter().GetResult();
+ }
+
+ /// <summary>
+ /// Starts a new transactionwith the given isolation level
+ /// </summary>
+ /// <param name="isolationLevel">The isolation level for the new
transaction.</param>
+ /// <param name="cancellationToken">An optional cancellation
token.</param>
+ /// <returns>A task representing the asynchronous operation.</returns>
+ public virtual Task BeginTransactionAsync(IsolationLevel?
isolationLevel = default, CancellationToken cancellationToken = default)
+ {
+ throw AdbcException.NotImplemented("Connection does not support
transactions");
+ }
+
+ /// <summary>
+ /// Create a new statement to bulk insert into a table.
+ /// </summary>
+ /// <param name="targetCatalog">The catalog name, or null to use the
current catalog</param>
+ /// <param name="targetDbSchema">The schema name, or null to use the
current schema</param>
+ /// <param name="targetTableName">The table name</param>
+ /// <param name="mode">The ingest mode</param>
+ /// <param name="isTemporary">True for a temporary table. Catalog and
Schema must be null when true.</param>
+ /// <returns>A statement object which can be used to bind the data to
be inserted and then executed using
+ /// <see cref="AdbcStatement11.ExecuteQuery" /> or <see
cref="AdbcStatement11.ExecuteUpdate" /></returns>
+ public virtual AdbcStatement11 BulkIngest(string? targetCatalog,
string? targetDbSchema, string targetTableName, BulkIngestMode mode, bool
isTemporary)
+ {
+ throw AdbcException.NotImplemented("Connection does not support
BulkIngest");
+ }
+
+ /// <summary>
+ /// Commit the pending transaction.
+ /// </summary>
+ public virtual void Commit()
+ {
+ Task.Run(() => CommitAsync()).GetAwaiter().GetResult();
+ }
+
+ /// <summary>
+ /// Commit the pending transaction.
+ /// </summary>
+ /// <param name="cancellationToken">An optional cancellation
token.</param>
+ /// <returns>A task representing the asynchronous operation.</returns>
+ public virtual Task CommitAsync(CancellationToken cancellationToken =
default)
+ {
+ throw AdbcException.NotImplemented("Connection does not support
transactions");
+ }
+
+ /// <summary>
+ /// Create a new statement that can be executed.
+ /// </summary>
+ public abstract AdbcStatement11
CreateStatement(IReadOnlyDictionary<string, object>? options = default);
+
+ /// <summary>
+ /// Get metadata about the driver/database.
+ /// </summary>
+ /// <param name="codes">The metadata items to fetch.</param>
+ /// <returns>Metadata about the driver and/or database.</returns>
+ public virtual IArrowArrayStream GetInfo(ReadOnlySpan<AdbcInfoCode>
codes)
+ {
+ var codesArray = codes.ToArray();
+ return Task.Run(() =>
GetInfoAsync(codesArray)).GetAwaiter().GetResult();
+ }
+
+ /// <summary>
+ /// Get metadata about the driver/database.
+ /// </summary>
+ /// <param name="codes">The metadata items to fetch.</param>
+ /// <param name="cancellationToken">An optional cancellation
token.</param>
+ /// <returns>A task whose Result property contains metadata about the
driver and/or database.</returns>
+ public virtual Task<IArrowArrayStream>
GetInfoAsync(ReadOnlySpan<AdbcInfoCode> codes, CancellationToken
cancellationToken = default)
+ {
+ throw AdbcException.NotImplemented("Connection does not support
GetInfo");
+ }
+
+ /// <summary>
+ /// Get a hierarchical view of all catalogs, database schemas, tables,
+ /// and columns.
+ /// </summary>
+ /// <param name="depth">
+ /// The level of nesting to display.
+ /// If ALL, display all levels (up through columns).
+ /// If CATALOGS, display only catalogs (i.e., catalog_schemas will be
+ /// null), and so on. May be a search pattern.
+ /// </param>
+ /// <param name="catalogPattern">
+ /// Only show tables in the given catalog.
+ /// If null, do not filter by catalog.If an empty string, only show
tables
+ /// without a catalog. May be a search pattern.
+ /// </param>
+ /// <param name="dbSchemaPattern">
+ /// Only show tables in the given database schema. If null, do not
+ /// filter by database schema.If an empty string, only show tables
+ /// without a database schema. May be a search pattern.
+ /// </param>
+ /// <param name="tableNamePattern">
+ /// Only show tables with the given name. If an empty string, only
+ /// show tables without a catalog. May be a search pattern.
+ /// </param>
+ /// <param name="tableTypes">
+ /// Only show tables matching one of the given table types.
+ /// If null, show tables of any type. Valid table types can be
+ /// fetched from <see cref="GetTableTypes"/>}.
+ /// </param>
+ /// <param name="columnNamePattern">
+ /// Only show columns with the given name.
+ /// If null, do not filter by name.May be a search pattern.
+ /// </param>
+ /// <returns>A table containing the requested information.</returns>
+ public virtual IArrowArrayStream GetObjects(
+ AdbcConnection.GetObjectsDepth depth,
+ string? catalogPattern,
+ string? dbSchemaPattern,
+ string? tableNamePattern,
+ IReadOnlyList<string>? tableTypes,
+ string? columnNamePattern)
+ {
+ return Task.Run(() => GetObjectsAsync(depth, catalogPattern,
dbSchemaPattern, tableNamePattern, tableTypes,
columnNamePattern)).GetAwaiter().GetResult();
+ }
+
+ /// <summary>
+ /// Get a hierarchical view of all catalogs, database schemas, tables,
+ /// and columns.
+ /// </summary>
+ /// <param name="depth">
+ /// The level of nesting to display.
+ /// If ALL, display all levels (up through columns).
+ /// If CATALOGS, display only catalogs (i.e., catalog_schemas will be
+ /// null), and so on. May be a search pattern.
+ /// </param>
+ /// <param name="catalogPattern">
+ /// Only show tables in the given catalog.
+ /// If null, do not filter by catalog.If an empty string, only show
tables
+ /// without a catalog. May be a search pattern.
+ /// </param>
+ /// <param name="dbSchemaPattern">
+ /// Only show tables in the given database schema. If null, do not
+ /// filter by database schema.If an empty string, only show tables
+ /// without a database schema. May be a search pattern.
+ /// </param>
+ /// <param name="tableNamePattern">
+ /// Only show tables with the given name. If an empty string, only
+ /// show tables without a catalog. May be a search pattern.
+ /// </param>
+ /// <param name="tableTypes">
+ /// Only show tables matching one of the given table types.
+ /// If null, show tables of any type. Valid table types can be
+ /// fetched from <see cref="GetTableTypes"/>}.
+ /// </param>
+ /// <param name="columnNamePattern">
+ /// Only show columns with the given name.
+ /// If null, do not filter by name.May be a search pattern.
+ /// </param>
+ /// <param name="cancellationToken">An optional cancellation
token.</param>
+ /// <returns>A task whose Result property is a table containing the
requested information.</returns>
+ public virtual Task<IArrowArrayStream> GetObjectsAsync(
+ AdbcConnection.GetObjectsDepth depth,
+ string? catalogPattern,
+ string? dbSchemaPattern,
+ string? tableNamePattern,
+ IReadOnlyList<string>? tableTypes,
+ string? columnNamePattern,
+ CancellationToken cancellationToken = default)
+ {
+ throw AdbcException.NotImplemented("Connection does not support
GetObjects");
+ }
+
+
+ /// <summary>
+ /// Gets an option from a statement.
+ /// </summary>
+ /// <param name="key">Option name</param>
+ /// <returns>The option value</returns>
+ public virtual object GetOption(string key)
+ {
+ throw AdbcException.NotImplemented("Connection does not support
getting options");
+ }
+
+ /// <summary>
+ /// Gets an option from a statement.
+ /// </summary>
+ /// <param name="key">Option name</param>
+ /// <returns>The option value</returns>
+ /// <param name="cancellationToken">An optional cancellation
token.</param>
+ /// <returns>A task whose Result property is the requested
value.</returns>
+ public virtual ValueTask<object> GetOptionAsync(string key,
CancellationToken cancellationToken = default)
+ {
+ return new ValueTask<object>(GetOption(key));
+ }
+ /// <summary>
+ /// Get the Arrow schema of a database table.
+ /// </summary>
+ /// <param name="catalog">The catalog of the table (or null).</param>
+ /// <param name="dbSchema">The database schema of the table (or
null).</param>
+ /// <param name="tableName">The table name.</param>
+ /// <returns>The requested table schema.</returns>
+ public virtual Schema GetTableSchema(string? catalog, string?
dbSchema, string tableName)
+ {
+ return Task.Run(() => GetTableSchemaAsync(catalog, dbSchema,
tableName)).GetAwaiter().GetResult();
+ }
+
+ /// <summary>
+ /// Get the Arrow schema of a database table.
+ /// </summary>
+ /// <param name="catalog">The catalog of the table (or null).</param>
+ /// <param name="dbSchema">The database schema of the table (or
null).</param>
+ /// <param name="tableName">The table name.</param>
+ /// <param name="cancellationToken">An optional cancellation
token.</param>
+ /// <returns>A task whose Result property is the requested
schema.</returns>
+ public virtual Task<Schema> GetTableSchemaAsync(string? catalog,
string? dbSchema, string tableName, CancellationToken cancellationToken =
default)
+ {
+ throw AdbcException.NotImplemented("Connection does not support
GetTableSchema");
+ }
+
+ /// <summary>
+ /// Get a list of table types supported by the database.
+ /// </summary>
+ /// <returns>The list of table types.</returns>
+ public virtual IArrowArrayStream GetTableTypes()
+ {
+ return Task.Run(() =>
GetTableTypesAsync()).GetAwaiter().GetResult();
+ }
+
+ /// <summary>
+ /// Get a list of table types supported by the database.
+ /// </summary>
+ /// <param name="cancellationToken">An optional cancellation
token.</param>
+ /// <returns>A task whose Result property is the list of table
types.</returns>
+ public virtual Task<IArrowArrayStream>
GetTableTypesAsync(CancellationToken cancellationToken = default)
+ {
+ throw AdbcException.NotImplemented("Connection does not support
GetTableTypes");
+ }
+
+ /// <summary>
+ /// Options may be set before AdbcConnectionInit. Some drivers may
+ /// support setting options after initialization as well.
+ /// </summary>
+ /// <param name="key">Option name</param>
+ /// <param name="value">Option value</param>
+ public virtual void SetOption(string key, object value)
+ {
+ throw AdbcException.NotImplemented("Connection does not support
setting options");
+ }
+
+ /// <summary>
+ /// Options may be set before AdbcConnectionInit. Some drivers may
+ /// support setting options after initialization as well.
+ /// </summary>
+ /// <param name="key">Option name</param>
+ /// <param name="value">Option value</param>
+ /// <param name="cancellationToken">An optional cancellation
token.</param>
+ /// <returns>A task representing the asynchronous operation.</returns>
+ public virtual ValueTask SetOptionAsync(string key, object value,
CancellationToken cancellationToken = default)
+ {
+ SetOption(key, value);
+ return default;
+ }
+
+ /// <summary>
+ /// Create a result set from a serialized PartitionDescriptor.
+ /// </summary>
+ /// <param name="partition">The partition descriptor.</param>
+ public virtual IArrowArrayStream ReadPartition(PartitionDescriptor
partition)
+ {
+ return Task.Run(() =>
ReadPartitionAsync(partition)).GetAwaiter().GetResult();
+ }
+
+ /// <summary>
+ /// Create a result set from a serialized PartitionDescriptor.
+ /// </summary>
+ /// <param name="partition">The partition descriptor.</param>
+ /// <param name="cancellationToken">An optional cancellation
token.</param>
+ /// <returns>A task whose Result property is the requested
data.</returns>
+ public virtual Task<IArrowArrayStream>
ReadPartitionAsync(PartitionDescriptor partition, CancellationToken
cancellationToken = default)
+ {
+ throw AdbcException.NotImplemented("Connection does not support
partitions");
+ }
+
+ /// <summary>
+ /// Rollback the pending transaction.
+ /// </summary>
+ public virtual void Rollback()
+ {
+ Task.Run(() => RollbackAsync()).GetAwaiter().GetResult();
+ }
+
+ /// <summary>
+ /// Rollback the pending transaction.
+ /// </summary>
+ /// <param name="cancellationToken">An optional cancellation
token.</param>
+ /// <returns>A task representing the asynchronous operation.</returns>
+ public virtual Task RollbackAsync(CancellationToken cancellationToken
= default)
+ {
+ throw AdbcException.NotImplemented("Connection does not support
transactions");
+ }
+
+ /// <summary>
+ /// Gets the names of the statistics returned by this driver.
+ /// </summary>
+ /// <returns>The names of the statistcs.</returns>
+ public virtual IArrowArrayStream GetStatisticsNames()
+ {
+ throw AdbcException.NotImplemented("Connection does not support
statistics");
+ }
+
+ /// <summary>
+ /// Gets the names of the statistics returned by this driver.
+ /// </summary>
+ /// <param name="cancellationToken">An optional cancellation
token.</param>
+ /// <returns>A task whose Result property is the names of the
statistics.</returns>
+ public virtual ValueTask<IArrowArrayStream>
GetStatisticsNamesAsync(CancellationToken cancellationToken = default)
+ {
+ return new ValueTask<IArrowArrayStream>(GetStatisticsNames());
+ }
+
+ /// <summary>
+ /// Gets statistics about the data distribution of table(s)
+ /// </summary>
+ /// <param name="catalogPattern">The catalog or null. May be a search
pattern.</param>
+ /// <param name="schemaPattern">The schema or null. May be a search
pattern.</param>
+ /// <param name="tableName">The table name or null. May be a search
pattern.</param>
+ /// <param name="approximate">If false, consumer desires exact
statistics regardless of cost</param>
+ /// <returns>A table describing the requested statistics.</returns>
+ public virtual IArrowArrayStream GetStatistics(string? catalogPattern,
string? schemaPattern, string tableNamePattern, bool approximate)
+ {
+ return Task.Run(() => GetStatisticsAsync(catalogPattern,
schemaPattern, tableNamePattern, approximate)).GetAwaiter().GetResult();
+ }
+
+ /// <summary>
+ /// Gets statistics about the data distribution of table(s)
+ /// </summary>
+ /// <param name="catalogPattern">The catalog or null. May be a search
pattern.</param>
+ /// <param name="schemaPattern">The schema or null. May be a search
pattern.</param>
+ /// <param name="tableName">The table name or null. May be a search
pattern.</param>
+ /// <param name="approximate">If false, consumer desires exact
statistics regardless of cost</param>
+ /// <returns>A result describing the statistics for the
table(s)</returns>
+ /// <param name="cancellationToken">An optional cancellation
token.</param>
+ /// <returns>A task whose Result property is a table describing the
requested statistics.</returns>
+ public virtual Task<IArrowArrayStream> GetStatisticsAsync(string?
catalogPattern, string? schemaPattern, string tableNamePattern, bool
approximate)
+ {
+ throw AdbcException.NotImplemented("Connection does not support
statistics");
+ }
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ }
+
+ /// <summary>
+ /// Performs application-defined tasks associated with freeing,
releasing, or resetting
+ /// unmanaged resources asynchronously.
+ /// </summary>
+ /// <returns>A task that represents the asynchronous dispose
operation.</returns>
+ public ValueTask DisposeAsync()
+ {
+ return DisposeAsyncCore();
+ }
+
+ protected virtual ValueTask DisposeAsyncCore()
+ {
+ Dispose();
+ return default;
+ }
+ }
+}
diff --git a/csharp/src/Apache.Arrow.Adbc/AdbcDatabase11.cs
b/csharp/src/Apache.Arrow.Adbc/AdbcDatabase11.cs
new file mode 100644
index 000000000..5d8ae6219
--- /dev/null
+++ b/csharp/src/Apache.Arrow.Adbc/AdbcDatabase11.cs
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Apache.Arrow.Adbc
+{
+ /// <summary>
+ /// Clients first initialize a database, then create a connection.
+ /// This gives the implementation a place to initialize and own any
+ /// common connection state.
+ /// For example, in-memory databases can place ownership of the actual
+ /// database in this object.
+ /// </summary>
+ public abstract class AdbcDatabase11 : IDisposable
+ {
+ ~AdbcDatabase11() => Dispose(false);
+
+ /// <summary>
+ /// Opens a new connection to the database.
+ /// </summary>
+ /// <param name="options">Additional options to use when
connecting.</param>
+ /// <returns>The database connection.</returns>
+ public virtual AdbcConnection11 Connect(IReadOnlyDictionary<string,
object>? options = default)
+ {
+ return Task.Run(() => Connect(options)).GetAwaiter().GetResult();
+ }
+
+ /// <summary>
+ /// Opens a new connection to the database
+ /// </summary>
+ /// <param name="options">Additional options to use when
connecting.</param>
+ /// <returns>The database connection.</returns>
+ public abstract Task<AdbcConnection11>
ConnectAsync(IReadOnlyDictionary<string, object>? options = default);
+
+ /// <summary>
+ /// Options are generally set before opening a database.
+ /// Throws an exception for unsupported options.
+ /// </summary>
+ /// <param name="key">Option name</param>
+ /// <returns>Option value.</returns>
+ public virtual object GetOption(string key)
+ {
+ throw AdbcException.NotImplemented("Connection does not support
getting options");
+ }
+
+ /// <summary>
+ /// Options are generally set before opening a database. Some drivers
may
+ /// support setting options after opening as well.
+ /// </summary>
+ /// <param name="key">Option name</param>
+ /// <param name="value">Option value</param>
+ public virtual void SetOption(string key, string value)
+ {
+ throw AdbcException.NotImplemented("Connection does not support
setting options");
+ }
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ }
+ }
+}
diff --git a/csharp/src/Apache.Arrow.Adbc/AdbcDriver11.cs
b/csharp/src/Apache.Arrow.Adbc/AdbcDriver11.cs
new file mode 100644
index 000000000..a36d61826
--- /dev/null
+++ b/csharp/src/Apache.Arrow.Adbc/AdbcDriver11.cs
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+using System;
+using System.Collections.Generic;
+
+namespace Apache.Arrow.Adbc
+{
+ /// <summary>
+ /// This provides a common interface for vendor-specific driver
+ /// initialization routines.
+ /// </summary>
+ public abstract class AdbcDriver11 : IDisposable
+ {
+ ~AdbcDriver11() => Dispose(false);
+
+ /// <summary>
+ /// Returns the version of the ADBC spec supported by this driver.
+ /// </summary>
+ public virtual int AdbcVersion => Adbc.AdbcVersion.Version_1_0_0;
+
+ /// <summary>
+ /// Creates a database reference via this driver.
+ /// </summary>
+ /// <param name="parameters">Driver-specific parameters.</param>
+ /// <returns>An object representing a reference to a specific
database.</returns>
+ public abstract AdbcDatabase11 Create(IReadOnlyDictionary<string,
string> parameters);
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ }
+ }
+}
diff --git a/csharp/src/Apache.Arrow.Adbc/AdbcStatement11.cs
b/csharp/src/Apache.Arrow.Adbc/AdbcStatement11.cs
new file mode 100644
index 000000000..1286ba244
--- /dev/null
+++ b/csharp/src/Apache.Arrow.Adbc/AdbcStatement11.cs
@@ -0,0 +1,276 @@
+/*
+ * 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.
+ */
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Apache.Arrow.Ipc;
+
+namespace Apache.Arrow.Adbc
+{
+ /// <summary>
+ /// Statements may represent queries or prepared statements. Statements
+ /// may be used multiple times and can be reconfigured (e.g. they can
+ /// be reused to execute multiple different queries).
+ /// </summary>
+ public abstract class AdbcStatement11 : IDisposable
+#if NET5_0_OR_GREATER
+ , IAsyncDisposable
+#endif
+ {
+ ~AdbcStatement11() => Dispose(false);
+
+ /// <summary>
+ /// Gets or sets a SQL query to be executed on this statement.
+ /// </summary>
+ public virtual string? SqlQuery { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Substrait plan.
+ /// </summary>
+ public virtual byte[]? SubstraitPlan
+ {
+ get { throw AdbcException.NotImplemented("Statement does not
support SubstraitPlan"); }
+ set { throw AdbcException.NotImplemented("Statement does not
support SubstraitPlan"); }
+ }
+
+ /// <summary>
+ /// Binds this statement to a <see cref="RecordBatch"/> to provide
parameter values or bulk data ingestion.
+ /// </summary>
+ /// <param name="batch">the RecordBatch to bind</param>
+ /// <param name="schema">the schema of the RecordBatch</param>
+ public virtual void Bind(RecordBatch batch, Schema schema)
+ {
+ throw AdbcException.NotImplemented("Statement does not support
Bind");
+ }
+
+ /// <summary>
+ /// Binds this statement to an <see cref="IArrowArrayStream"/> to
provide parameter values or bulk data ingestion.
+ /// </summary>
+ /// <param name="stream"></param>
+ public virtual void BindStream(IArrowArrayStream stream)
+ {
+ throw AdbcException.NotImplemented("Statement does not support
BindStream");
+ }
+
+ /// <summary>
+ /// Executes the statement and returns a structure containing the
number
+ /// of records and the <see cref="IArrowArrayStream"/>.
+ /// </summary>
+ /// <returns>A <see cref="QueryResult"/>.</returns>
+ public virtual QueryResult ExecuteQuery()
+ {
+ return Task.Run(() =>
ExecuteQueryAsync()).GetAwaiter().GetResult();
+ }
+
+ /// <summary>
+ /// Executes the statement and returns a structure containing the
number
+ /// of records and the <see cref="IArrowArrayStream"/>.
+ /// </summary>
+ /// <param name="cancellationToken">An optional cancellation
token.</param>
+ /// <returns>A task whose Result property is a <see
cref="QueryResult"/>.</returns>
+ public abstract Task<QueryResult> ExecuteQueryAsync(CancellationToken
cancellationToken = default);
+
+ /// <summary>
+ /// Analyzes the statement and returns the schema of the result set
that would
+ /// be expected if the statement were to be executed.
+ /// </summary>
+ /// <returns>An Arrow <see cref="Schema"/> describing the result
set.</returns>
+ public virtual Schema ExecuteSchema()
+ {
+ return Task.Run(() =>
ExecuteSchemaAsync()).GetAwaiter().GetResult();
+ }
+
+ /// <summary>
+ /// Analyzes the statement and returns the schema of the result set
that would
+ /// be expected if the statement were to be executed.
+ /// </summary>
+ /// <param name="cancellationToken">An optional cancellation
token.</param>
+ /// <returns>A task whose Result property is an Arrow <see
cref="Schema"/> describing the result set.</returns>
+ public virtual Task<Schema> ExecuteSchemaAsync(CancellationToken
cancellationToken = default)
+ {
+ throw AdbcException.NotImplemented("Statement does not support
ExecuteSchema");
+ }
+
+ /// <summary>
+ /// Executes an update command and returns the number of
+ /// records effected.
+ /// </summary>
+ /// <returns>An <see cref="UpdateResult"/>.</returns>
+ public virtual UpdateResult ExecuteUpdate()
+ {
+ return Task.Run(() =>
ExecuteUpdateAsync()).GetAwaiter().GetResult();
+ }
+
+ /// <summary>
+ /// Executes an update command and returns the number of
+ /// records effected.
+ /// </summary>
+ /// <param name="cancellationToken">An optional cancellation
token.</param>
+ /// <returns>A task whose Result property is a <see
cref="QueryResult"/>.</returns>
+ public virtual Task<UpdateResult> ExecuteUpdateAsync(CancellationToken
cancellationToken = default)
+ {
+ throw AdbcException.NotImplemented("Statement does not support
ExecuteUpdate");
+ }
+
+ /// <summary>
+ /// Execute a result set-generating query and get a list of
+ /// partitions of the result set.
+ /// </summary>
+ /// <returns>A list of partitions of the result set. These can be
fetched using <seealso cref="AdbcConnection11.ReadPartition"/></returns>
+ public virtual PartitionedResult ExecutePartitioned()
+ {
+ return Task.Run(() =>
ExecutePartitionedAsync()).GetAwaiter().GetResult();
+ }
+
+ /// <summary>
+ /// Execute a result set-generating query and get a list of
+ /// partitions of the result set.
+ /// </summary>
+ /// <param name="cancellationToken">An optional cancellation
token.</param>
+ /// <returns>A task whose Result property is a list of partitions of
the result set. These can be
+ /// fetched using <seealso
cref="AdbcConnection11.ReadPartitionAsync"/></returns>
+ public virtual Task<PartitionedResult>
ExecutePartitionedAsync(CancellationToken cancellationToken = default)
+ {
+ throw AdbcException.NotImplemented("Statement does not support
ExecutePartitioned");
+ }
+
+ /// <summary>
+ /// Gets an option from a statement.
+ /// </summary>
+ /// <param name="key">Option name</param>
+ /// <returns>The option value</returns>
+ public virtual object GetOption(string key)
+ {
+ throw AdbcException.NotImplemented("Statement does not support
getting options");
+ }
+
+ /// <summary>
+ /// Gets an option from a statement.
+ /// </summary>
+ /// <param name="key">Option name</param>
+ /// <param name="cancellationToken">An optional cancellation
token.</param>
+ /// <returns>A task whose Result property is the requested
value.</returns>
+ public virtual ValueTask<object> GetOptionAsync(string key,
CancellationToken cancellationToken = default)
+ {
+ return new ValueTask<object>(GetOption(key));
+ }
+
+ /// <summary>
+ /// Gets the schema for bound parameters.
+ /// </summary>
+ /// <returns>The schema for parameters bound to the
statement.</returns>
+ public virtual Schema GetParameterSchema()
+ {
+ return Task.Run(() =>
GetParameterSchemaAsync()).GetAwaiter().GetResult();
+ }
+
+ /// <summary>
+ /// Gets the schema for bound parameters.
+ /// </summary>
+ /// <param name="cancellationToken">An optional cancellation
token.</param>
+ /// <returns>A task whose Result property is the schema for parameters
bound to the statement.</returns>
+ public virtual Task<Schema> GetParameterSchemaAsync(CancellationToken
cancellationToken = default)
+ {
+ throw AdbcException.NotImplemented("Statement does not support
GetParameterSchema");
+ }
+
+ /// <summary>
+ /// Turn this statement into a prepared statement to be
+ /// executed multiple times.
+ /// </summary>
+ public virtual void Prepare()
+ {
+ Task.Run(() => PrepareAsync()).GetAwaiter().GetResult();
+ }
+
+ /// <summary>
+ /// Turn this statement into a prepared statement to be
+ /// executed multiple times.
+ /// </summary>
+ /// <param name="cancellationToken">An optional cancellation
token.</param>
+ /// <returns>A task representing the asynchronous operation.</returns>
+ public virtual Task PrepareAsync(CancellationToken cancellationToken =
default)
+ {
+ throw AdbcException.NotImplemented("Statement does not support
Prepare");
+ }
+
+ /// <summary>
+ /// Set an option on a statement.
+ /// </summary>
+ /// <param name="key">Option name</param>
+ /// <param name="value">Option value</param>
+ public virtual void SetOption(string key, object value)
+ {
+ throw AdbcException.NotImplemented("Statement does not support
setting options");
+ }
+
+ /// <summary>
+ /// Gets an option from a statement.
+ /// </summary>
+ /// <param name="key">Option name</param>
+ /// <param name="value">Option value</param>
+ /// <param name="cancellationToken">An optional cancellation
token.</param>
+ /// <returns>A task representing the asynchronous operation.</returns>
+ public virtual ValueTask SetOptionAsync(string key, object value,
CancellationToken cancellationToken = default)
+ {
+ SetOption(key, value);
+ return default;
+ }
+
+ /// <summary>
+ /// Attempts to cancel an in-progress operation on a connection.
+ /// </summary>
+ /// <remarks>
+ /// This can be called during a method like ExecuteQuery or while
consuming an ArrowArrayStream
+ /// returned from such. Calling this function should make the other
function throw a cancellation exception.
+ ///
+ /// This must always be thread-safe.
+ /// </remarks>
+ public virtual void Cancel()
+ {
+ throw AdbcException.NotImplemented("Statement does not support
cancellation");
+ }
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ }
+
+ /// <summary>
+ /// Performs application-defined tasks associated with freeing,
releasing, or resetting
+ /// unmanaged resources asynchronously.
+ /// </summary>
+ /// <returns>A task that represents the asynchronous dispose
operation.</returns>
+ public ValueTask DisposeAsync()
+ {
+ return DisposeAsyncCore();
+ }
+
+ protected virtual ValueTask DisposeAsyncCore()
+ {
+ Dispose();
+ return default;
+ }
+ }
+}
diff --git a/csharp/src/Apache.Arrow.Adbc/Results.cs
b/csharp/src/Apache.Arrow.Adbc/Results.cs
index b86be819c..33a22df6f 100644
--- a/csharp/src/Apache.Arrow.Adbc/Results.cs
+++ b/csharp/src/Apache.Arrow.Adbc/Results.cs
@@ -43,7 +43,7 @@ namespace Apache.Arrow.Adbc
/// <summary>
/// The number of records in the result.
/// </summary>
- public long RowCount { get; set; }
+ public long RowCount { get; }
/// <summary>
/// The <see cref="IArrowArrayStream"/> for reading.