eerhardt commented on code in PR #697:
URL: https://github.com/apache/arrow-adbc/pull/697#discussion_r1214703073


##########
csharp/test/Apache.Arrow.Adbc.FlightSql.Tests/ConnectionTests.cs:
##########
@@ -0,0 +1,138 @@
+/*
+* 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.IO;
+using System.Linq;
+using Apache.Arrow.Adbc.Core;
+using Apache.Arrow.Adbc.Tests;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using Newtonsoft.Json;

Review Comment:
   (minor request) Can we use `System.Text.Json` instead of `Newtonsoft.Json`? 
I know this is just tests, but it would be good to use the stuff in the box, if 
possible.



##########
csharp/test/Apache.Arrow.Adbc.FlightSql.Tests/.gitignore:
##########
@@ -0,0 +1 @@
+*.pass

Review Comment:
   Do we need this? It is in the csharp/.gitignore.



##########
csharp/src/Apache.Arrow.Adbc/Apache.Arrow.Adbc.csproj:
##########
@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <Version>0.1.0</Version>

Review Comment:
   ```suggestion
   ```
   
   We will want to read this from the root Directory.Build.props.



##########
csharp/src/Apache.Arrow.Adbc.FlightSql/Apache.Arrow.Adbc.FlightSql.csproj:
##########
@@ -0,0 +1,20 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
+    <AssemblyName>$(MSBuildProjectName)</AssemblyName>
+    <RootNamespace>Apache.Arrow.Adbc.FlightSql</RootNamespace>
+    <Version>0.1.0</Version>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Apache.Arrow.Flight" Version="12.0.0" />
+    <PackageReference Include="Grpc.Net.Client.Web" Version="2.53.0" />

Review Comment:
   ```suggestion
       <PackageReference Include="Grpc.Net.Client" Version="2.53.0" />
   ```
   
   I assume you don't really need the `.Web` package. If you do, why?



##########
csharp/src/Apache.Arrow.Adbc.FlightSql/Apache.Arrow.Adbc.FlightSql.csproj:
##########
@@ -0,0 +1,20 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
+    <AssemblyName>$(MSBuildProjectName)</AssemblyName>
+    <RootNamespace>Apache.Arrow.Adbc.FlightSql</RootNamespace>

Review Comment:
   ```suggestion
   ```
   
   These aren't needed - they are by default set to the project name.



##########
csharp/src/Apache.Arrow.Adbc/Core/Interop.cs:
##########
@@ -0,0 +1,694 @@
+/*
+ * 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.Buffers;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using Apache.Arrow.Adbc.Interop;
+using Apache.Arrow.C;
+using static System.Net.Mime.MediaTypeNames;
+
+#if NETSTANDARD
+using Apache.Arrow.Adbc.Extensions;
+#endif
+
+namespace Apache.Arrow.Adbc.Core
+{
+    internal static class AdbcInterop
+    {
+        private unsafe static readonly NativeDelegate<ErrorRelease> 
releaseError = new NativeDelegate<ErrorRelease>(ReleaseError);
+        private unsafe static readonly NativeDelegate<DriverRelease> 
releaseDriver = new NativeDelegate<DriverRelease>(ReleaseDriver);
+
+        private unsafe static readonly NativeDelegate<DatabaseFn> databaseInit 
= new NativeDelegate<DatabaseFn>(InitDatabase);
+        private unsafe static readonly NativeDelegate<DatabaseFn> 
databaseRelease = new NativeDelegate<DatabaseFn>(ReleaseDatabase);
+        private unsafe static readonly NativeDelegate<DatabaseSetOption> 
databaseSetOption = new NativeDelegate<DatabaseSetOption>(SetDatabaseOption);
+
+        private unsafe static readonly NativeDelegate<ConnectionInit> 
connectionInit = new NativeDelegate<ConnectionInit>(InitConnection);
+        private unsafe static readonly NativeDelegate<ConnectionFn> 
connectionRelease = new NativeDelegate<ConnectionFn>(ReleaseConnection);
+        private unsafe static readonly NativeDelegate<ConnectionGetInfo> 
connectionGetInfo = new NativeDelegate<ConnectionGetInfo>(GetConnectionInfo);
+        private unsafe static readonly NativeDelegate<ConnectionSetOption> 
connectionSetOption = new 
NativeDelegate<ConnectionSetOption>(SetConnectionOption);
+        
+        private unsafe static readonly NativeDelegate<StatementExecuteQuery> 
statementExecuteQuery = new 
NativeDelegate<StatementExecuteQuery>(ExecuteStatementQuery);
+        private unsafe static readonly NativeDelegate<StatementNew> 
statementNew = new NativeDelegate<StatementNew>(NewStatement);
+        private unsafe static readonly NativeDelegate<StatementFn> 
statementRelease = new NativeDelegate<StatementFn>(ReleaseStatement);
+        private unsafe static readonly NativeDelegate<StatementSetSqlQuery> 
statementSetSqlQuery = new 
NativeDelegate<StatementSetSqlQuery>(SetStatementSqlQuery);
+
+        public unsafe static AdbcStatusCode AdbcDriverInit(int version, 
NativeAdbcDriver* nativeDriver, NativeAdbcError* error, AdbcDriver driver)
+        {
+            DriverStub stub = new DriverStub(driver);
+            GCHandle handle = GCHandle.Alloc(stub);
+            nativeDriver->private_data = (void*)GCHandle.ToIntPtr(handle);
+            nativeDriver->release = (delegate* 
unmanaged[Stdcall]<NativeAdbcDriver*, NativeAdbcError*, 
AdbcStatusCode>)releaseDriver.Pointer;
+
+            nativeDriver->DatabaseInit = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, NativeAdbcError*, 
AdbcStatusCode>)databaseInit.Pointer;
+            nativeDriver->DatabaseNew = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, NativeAdbcError*, 
AdbcStatusCode>)stub.newDatabase.Pointer;
+            nativeDriver->DatabaseSetOption = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, byte*, byte*, NativeAdbcError*, 
AdbcStatusCode>) databaseSetOption.Pointer;
+            nativeDriver->DatabaseRelease = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, NativeAdbcError*, 
AdbcStatusCode>)databaseRelease.Pointer;
+
+            nativeDriver->ConnectionCommit = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcError*, 
AdbcStatusCode>)connectionRelease.Pointer;
+            //nativeDriver->ConnectionGetInfo = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection *, int*, int, CArrowArrayStream*, 
NativeAdbcError*, AdbcStatusCode>)connectionGetInfo.Pointer;
+            //nativeDriver->ConnectionGetTableSchema = null;
+            //nativeDriver->ConnectionGetTableTypes = null;
+            nativeDriver->ConnectionInit = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcDatabase*, 
NativeAdbcError*, AdbcStatusCode>)connectionInit.Pointer;
+            nativeDriver->ConnectionNew = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcError*, 
AdbcStatusCode>)stub.newConnection.Pointer;
+            nativeDriver->ConnectionSetOption = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, byte*, byte*, NativeAdbcError*, 
AdbcStatusCode>)connectionSetOption.Pointer;
+            //nativeDriver->ConnectionReadPartition = null;
+            nativeDriver->ConnectionRelease = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcError*, 
AdbcStatusCode>)connectionRelease.Pointer;
+            //nativeDriver->ConnectionRollback = null;
+
+           // nativeDriver->StatementBind = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, CArrowArray*, CArrowSchema*, 
NativeAdbcError*, AdbcStatusCode>)
+            nativeDriver->StatementNew = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcStatement*, 
NativeAdbcError*, AdbcStatusCode>)statementNew.Pointer;
+            nativeDriver->StatementSetSqlQuery = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, byte*, NativeAdbcError *, 
AdbcStatusCode >)statementSetSqlQuery.Pointer;
+            nativeDriver->StatementExecuteQuery = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, CArrowArrayStream*, long*, 
NativeAdbcError*, AdbcStatusCode>)statementExecuteQuery.Pointer;
+            nativeDriver->StatementPrepare = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, NativeAdbcError*, 
AdbcStatusCode>)statementRelease.Pointer;
+            nativeDriver->StatementRelease = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, NativeAdbcError*, 
AdbcStatusCode>)statementRelease.Pointer;
+            
+            return 0;
+        }
+
+        private unsafe static void ReleaseError(NativeAdbcError* error)
+        {
+            if (error != null && ((IntPtr)error->message) != IntPtr.Zero)
+            {
+                Marshal.FreeCoTaskMem((IntPtr)error->message);
+            }
+        }
+
+        private unsafe static AdbcStatusCode SetError(NativeAdbcError* error, 
Exception exception)
+        {
+            ReleaseError(error);
+
+            #if NETSTANDARD
+                error->message = 
(char*)MarshalExtensions.StringToCoTaskMemUTF8(exception.Message);
+            #else
+                error->message = 
(char*)Marshal.StringToCoTaskMemUTF8(exception.Message);
+            #endif
+
+            error->sqlstate0 = (char)0;
+            error->sqlstate1 = (char)0;
+            error->sqlstate2 = (char)0;
+            error->sqlstate3 = (char)0;
+            error->sqlstate4 = (char)0;
+            error->vendor_code = 0;
+            error->vendor_code = 0;
+            error->release = (delegate* unmanaged[Stdcall]<NativeAdbcError*, 
void>)releaseError.Pointer;
+            
+            return AdbcStatusCode.UnknownError;
+        }
+
+        private sealed class PinnedArray : IDisposable
+        {
+            IArrowArray _array;
+            MemoryHandle[] pinnedHandles;
+
+            public PinnedArray(IArrowArray array)
+            {
+                _array = array;
+                pinnedHandles = new MemoryHandle[GetHandleCount(array.Data)];
+                int index = 0;
+                PinBuffers(array.Data, pinnedHandles, ref index);
+                Debug.Assert(index == pinnedHandles.Length);
+            }
+
+            public void Dispose()
+            {
+                if (_array != null)
+                {
+                    _array.Dispose();
+                    foreach (MemoryHandle handle in pinnedHandles)
+                    {
+                        handle.Dispose();
+                    }
+                    _array = null;
+                }
+            }
+
+            static int GetHandleCount(ArrayData data)
+            {
+                int handleCount = data.Buffers.Length;
+                foreach (ArrayData child in data.Children)
+                {
+                    handleCount += GetHandleCount(child);
+                }
+                if (data.Dictionary != null)
+                {
+                    handleCount += GetHandleCount(data.Dictionary);
+                }
+                return handleCount;
+            }
+
+            static void PinBuffers(ArrayData data, MemoryHandle[] handles, ref 
int index)
+            {
+                foreach (ArrowBuffer buffer in data.Buffers)
+                {
+                    handles[index++] = buffer.Memory.Pin();
+                }
+                foreach (ArrayData child in data.Children)
+                {
+                    PinBuffers(child, handles, ref index);
+                }
+                if (data.Dictionary != null)
+                {
+                    PinBuffers(data.Dictionary, handles, ref index);
+                }
+            }
+        }
+
+        private static IntPtr FromDisposable(IDisposable d)
+        {
+            GCHandle gch = GCHandle.Alloc(d);
+            return GCHandle.ToIntPtr(gch);
+        }
+
+        private static void Dispose(ref IntPtr p)
+        {
+            GCHandle gch = GCHandle.FromIntPtr(p);
+            ((IDisposable)gch.Target).Dispose();
+            gch.Free();
+            p = IntPtr.Zero;
+        }
+
+        private unsafe static AdbcStatusCode ReleaseDriver(NativeAdbcDriver* 
nativeDriver, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeDriver->private_data);
+            DriverStub stub = (DriverStub)gch.Target;
+            stub.Dispose();
+            gch.Free();
+            nativeDriver->private_data = null;
+            return 0;
+        }
+
+        private unsafe static AdbcStatusCode InitDatabase(NativeAdbcDatabase* 
nativeDatabase, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeDatabase->private_data);
+            DatabaseStub stub = (DatabaseStub)gch.Target;
+            return stub.Init(ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
ReleaseDatabase(NativeAdbcDatabase* nativeDatabase, NativeAdbcError* error)
+        {
+            if (nativeDatabase->private_data == null)
+            {
+                return AdbcStatusCode.UnknownError;
+            }
+
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeDatabase->private_data);
+            DatabaseStub stub = (DatabaseStub)gch.Target;
+            stub.Dispose();
+            gch.Free();
+            nativeDatabase->private_data = null;
+            return AdbcStatusCode.Success;
+        }
+
+        private unsafe static AdbcStatusCode 
SetConnectionOption(NativeAdbcConnection* nativeConnection, byte* name, byte* 
value, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            return stub.SetOption(name, value, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
SetDatabaseOption(NativeAdbcDatabase* nativeDatabase, byte* name, byte* value, 
NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeDatabase->private_data);
+            DatabaseStub stub = (DatabaseStub)gch.Target;
+
+            return stub.SetOption(name, value, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
InitConnection(NativeAdbcConnection* nativeConnection, NativeAdbcDatabase* 
database, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            return stub.InitConnection(ref *database, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
ReleaseConnection(NativeAdbcConnection* nativeConnection, NativeAdbcError* 
error)
+        {
+            if (nativeConnection->private_data == null)
+            {
+                return AdbcStatusCode.UnknownError;
+            }
+
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            stub.Dispose();
+            gch.Free();
+            nativeConnection->private_data = null;
+            return AdbcStatusCode.Success;
+        }
+
+        private unsafe static AdbcStatusCode 
GetConnectionInfo(NativeAdbcConnection* nativeConnection, uint* info_codes, int 
info_codes_length, CArrowArrayStream* stream, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            return stub.GetConnectionInfo(ref *nativeConnection, *info_codes, 
info_codes_length, ref *stream, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
SetStatementSqlQuery(NativeAdbcStatement* nativeStatement, byte* text, 
NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeStatement->private_data);
+            AdbcStatement stub = (AdbcStatement)gch.Target;
+
+            #if NETSTANDARD
+                stub.SqlQuery = 
MarshalExtensions.PtrToStringUTF8((IntPtr)text);
+            #else
+                stub.SqlQuery = Marshal.PtrToStringUTF8((IntPtr)text);
+            #endif
+
+            return AdbcStatusCode.Success;
+        }
+
+        private unsafe static AdbcStatusCode 
ExecuteStatementQuery(NativeAdbcStatement* nativeStatement, CArrowArrayStream* 
stream, long* rows, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeStatement->private_data);
+            AdbcStatement stub = (AdbcStatement)gch.Target;
+            var result = stub.ExecuteQuery();
+            if (rows != null)
+            {
+                *rows = result.RowCount;
+            }
+
+            GCHandle streamHandle = GCHandle.Alloc(result.Stream);
+            stream->private_data = (void*)GCHandle.ToIntPtr(streamHandle);
+
+            return 0;
+        }
+
+        private unsafe static AdbcStatusCode 
NewStatement(NativeAdbcConnection* nativeConnection, NativeAdbcStatement* 
nativeStatement, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            return stub.NewStatement(ref *nativeStatement, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
ReleaseStatement(NativeAdbcStatement* nativeStatement, NativeAdbcError* error)
+        {
+            if (nativeStatement->private_data == null)
+            {
+                return AdbcStatusCode.UnknownError;
+            }
+
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeStatement->private_data);
+            AdbcStatement stub = (AdbcStatement)gch.Target;
+            stub.Dispose();
+            gch.Free();
+            nativeStatement->private_data = null;
+            return AdbcStatusCode.Success;
+        }
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcDatabase
+    {
+        public void* private_data;
+        public NativeAdbcDriver* private_driver;
+
+        public static NativeAdbcDatabase* Create()
+        {
+            var ptr = 
(NativeAdbcDatabase*)Marshal.AllocHGlobal(sizeof(NativeAdbcDatabase));
+
+            ptr->private_data = null;
+            ptr->private_driver = null;
+            
+            return ptr;
+        }
+
+        /// <summary>
+        /// Free a pointer that was allocated in <see cref="Create"/>.
+        /// </summary>
+        /// <remarks>
+        /// Do not call this on a pointer that was allocated elsewhere.
+        /// </remarks>
+        public static void Free(NativeAdbcDatabase* database)
+        {
+            Marshal.FreeHGlobal((IntPtr)database);
+        }
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcConnection
+    {
+        public void* private_data;
+        public NativeAdbcDriver* private_driver;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcStatement
+    {
+        public void* private_data;
+        public NativeAdbcDriver* private_driver;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcPartitions
+    {
+        /// <summary>
+        /// The number of partitions.
+        /// </summary>
+        public int num_partitions;
+
+        /// <summary>
+        /// The partitions of the result set, where each entry (up to
+        /// num_partitions entries) is an opaque identifier that can be
+        /// passed to AdbcConnectionReadPartition.
+        /// </summary>
+        public sbyte** partitions;
+
+        /// <summary>
+        /// The length of each corresponding entry in partitions.
+        /// </summary>
+        public int* partition_lengths;

Review Comment:
   ```suggestion
           public nuint* partition_lengths;
   
   ```



##########
csharp/src/Apache.Arrow.Adbc/Core/Interop.cs:
##########
@@ -0,0 +1,694 @@
+/*
+ * 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.Buffers;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using Apache.Arrow.Adbc.Interop;
+using Apache.Arrow.C;
+using static System.Net.Mime.MediaTypeNames;
+
+#if NETSTANDARD
+using Apache.Arrow.Adbc.Extensions;
+#endif
+
+namespace Apache.Arrow.Adbc.Core
+{
+    internal static class AdbcInterop
+    {
+        private unsafe static readonly NativeDelegate<ErrorRelease> 
releaseError = new NativeDelegate<ErrorRelease>(ReleaseError);
+        private unsafe static readonly NativeDelegate<DriverRelease> 
releaseDriver = new NativeDelegate<DriverRelease>(ReleaseDriver);
+
+        private unsafe static readonly NativeDelegate<DatabaseFn> databaseInit 
= new NativeDelegate<DatabaseFn>(InitDatabase);
+        private unsafe static readonly NativeDelegate<DatabaseFn> 
databaseRelease = new NativeDelegate<DatabaseFn>(ReleaseDatabase);
+        private unsafe static readonly NativeDelegate<DatabaseSetOption> 
databaseSetOption = new NativeDelegate<DatabaseSetOption>(SetDatabaseOption);
+
+        private unsafe static readonly NativeDelegate<ConnectionInit> 
connectionInit = new NativeDelegate<ConnectionInit>(InitConnection);
+        private unsafe static readonly NativeDelegate<ConnectionFn> 
connectionRelease = new NativeDelegate<ConnectionFn>(ReleaseConnection);
+        private unsafe static readonly NativeDelegate<ConnectionGetInfo> 
connectionGetInfo = new NativeDelegate<ConnectionGetInfo>(GetConnectionInfo);
+        private unsafe static readonly NativeDelegate<ConnectionSetOption> 
connectionSetOption = new 
NativeDelegate<ConnectionSetOption>(SetConnectionOption);
+        
+        private unsafe static readonly NativeDelegate<StatementExecuteQuery> 
statementExecuteQuery = new 
NativeDelegate<StatementExecuteQuery>(ExecuteStatementQuery);
+        private unsafe static readonly NativeDelegate<StatementNew> 
statementNew = new NativeDelegate<StatementNew>(NewStatement);
+        private unsafe static readonly NativeDelegate<StatementFn> 
statementRelease = new NativeDelegate<StatementFn>(ReleaseStatement);
+        private unsafe static readonly NativeDelegate<StatementSetSqlQuery> 
statementSetSqlQuery = new 
NativeDelegate<StatementSetSqlQuery>(SetStatementSqlQuery);
+
+        public unsafe static AdbcStatusCode AdbcDriverInit(int version, 
NativeAdbcDriver* nativeDriver, NativeAdbcError* error, AdbcDriver driver)
+        {
+            DriverStub stub = new DriverStub(driver);
+            GCHandle handle = GCHandle.Alloc(stub);
+            nativeDriver->private_data = (void*)GCHandle.ToIntPtr(handle);
+            nativeDriver->release = (delegate* 
unmanaged[Stdcall]<NativeAdbcDriver*, NativeAdbcError*, 
AdbcStatusCode>)releaseDriver.Pointer;
+
+            nativeDriver->DatabaseInit = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, NativeAdbcError*, 
AdbcStatusCode>)databaseInit.Pointer;
+            nativeDriver->DatabaseNew = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, NativeAdbcError*, 
AdbcStatusCode>)stub.newDatabase.Pointer;
+            nativeDriver->DatabaseSetOption = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, byte*, byte*, NativeAdbcError*, 
AdbcStatusCode>) databaseSetOption.Pointer;
+            nativeDriver->DatabaseRelease = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, NativeAdbcError*, 
AdbcStatusCode>)databaseRelease.Pointer;
+
+            nativeDriver->ConnectionCommit = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcError*, 
AdbcStatusCode>)connectionRelease.Pointer;
+            //nativeDriver->ConnectionGetInfo = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection *, int*, int, CArrowArrayStream*, 
NativeAdbcError*, AdbcStatusCode>)connectionGetInfo.Pointer;
+            //nativeDriver->ConnectionGetTableSchema = null;
+            //nativeDriver->ConnectionGetTableTypes = null;
+            nativeDriver->ConnectionInit = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcDatabase*, 
NativeAdbcError*, AdbcStatusCode>)connectionInit.Pointer;
+            nativeDriver->ConnectionNew = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcError*, 
AdbcStatusCode>)stub.newConnection.Pointer;
+            nativeDriver->ConnectionSetOption = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, byte*, byte*, NativeAdbcError*, 
AdbcStatusCode>)connectionSetOption.Pointer;
+            //nativeDriver->ConnectionReadPartition = null;
+            nativeDriver->ConnectionRelease = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcError*, 
AdbcStatusCode>)connectionRelease.Pointer;
+            //nativeDriver->ConnectionRollback = null;
+
+           // nativeDriver->StatementBind = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, CArrowArray*, CArrowSchema*, 
NativeAdbcError*, AdbcStatusCode>)
+            nativeDriver->StatementNew = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcStatement*, 
NativeAdbcError*, AdbcStatusCode>)statementNew.Pointer;
+            nativeDriver->StatementSetSqlQuery = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, byte*, NativeAdbcError *, 
AdbcStatusCode >)statementSetSqlQuery.Pointer;
+            nativeDriver->StatementExecuteQuery = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, CArrowArrayStream*, long*, 
NativeAdbcError*, AdbcStatusCode>)statementExecuteQuery.Pointer;
+            nativeDriver->StatementPrepare = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, NativeAdbcError*, 
AdbcStatusCode>)statementRelease.Pointer;
+            nativeDriver->StatementRelease = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, NativeAdbcError*, 
AdbcStatusCode>)statementRelease.Pointer;
+            
+            return 0;
+        }
+
+        private unsafe static void ReleaseError(NativeAdbcError* error)
+        {
+            if (error != null && ((IntPtr)error->message) != IntPtr.Zero)
+            {
+                Marshal.FreeCoTaskMem((IntPtr)error->message);
+            }
+        }
+
+        private unsafe static AdbcStatusCode SetError(NativeAdbcError* error, 
Exception exception)
+        {
+            ReleaseError(error);
+
+            #if NETSTANDARD
+                error->message = 
(char*)MarshalExtensions.StringToCoTaskMemUTF8(exception.Message);
+            #else
+                error->message = 
(char*)Marshal.StringToCoTaskMemUTF8(exception.Message);
+            #endif
+
+            error->sqlstate0 = (char)0;
+            error->sqlstate1 = (char)0;
+            error->sqlstate2 = (char)0;
+            error->sqlstate3 = (char)0;
+            error->sqlstate4 = (char)0;
+            error->vendor_code = 0;
+            error->vendor_code = 0;
+            error->release = (delegate* unmanaged[Stdcall]<NativeAdbcError*, 
void>)releaseError.Pointer;
+            
+            return AdbcStatusCode.UnknownError;
+        }
+
+        private sealed class PinnedArray : IDisposable
+        {
+            IArrowArray _array;
+            MemoryHandle[] pinnedHandles;
+
+            public PinnedArray(IArrowArray array)
+            {
+                _array = array;
+                pinnedHandles = new MemoryHandle[GetHandleCount(array.Data)];
+                int index = 0;
+                PinBuffers(array.Data, pinnedHandles, ref index);
+                Debug.Assert(index == pinnedHandles.Length);
+            }
+
+            public void Dispose()
+            {
+                if (_array != null)
+                {
+                    _array.Dispose();
+                    foreach (MemoryHandle handle in pinnedHandles)
+                    {
+                        handle.Dispose();
+                    }
+                    _array = null;
+                }
+            }
+
+            static int GetHandleCount(ArrayData data)
+            {
+                int handleCount = data.Buffers.Length;
+                foreach (ArrayData child in data.Children)
+                {
+                    handleCount += GetHandleCount(child);
+                }
+                if (data.Dictionary != null)
+                {
+                    handleCount += GetHandleCount(data.Dictionary);
+                }
+                return handleCount;
+            }
+
+            static void PinBuffers(ArrayData data, MemoryHandle[] handles, ref 
int index)
+            {
+                foreach (ArrowBuffer buffer in data.Buffers)
+                {
+                    handles[index++] = buffer.Memory.Pin();
+                }
+                foreach (ArrayData child in data.Children)
+                {
+                    PinBuffers(child, handles, ref index);
+                }
+                if (data.Dictionary != null)
+                {
+                    PinBuffers(data.Dictionary, handles, ref index);
+                }
+            }
+        }
+
+        private static IntPtr FromDisposable(IDisposable d)
+        {
+            GCHandle gch = GCHandle.Alloc(d);
+            return GCHandle.ToIntPtr(gch);
+        }
+
+        private static void Dispose(ref IntPtr p)
+        {
+            GCHandle gch = GCHandle.FromIntPtr(p);
+            ((IDisposable)gch.Target).Dispose();
+            gch.Free();
+            p = IntPtr.Zero;
+        }
+
+        private unsafe static AdbcStatusCode ReleaseDriver(NativeAdbcDriver* 
nativeDriver, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeDriver->private_data);
+            DriverStub stub = (DriverStub)gch.Target;
+            stub.Dispose();
+            gch.Free();
+            nativeDriver->private_data = null;
+            return 0;
+        }
+
+        private unsafe static AdbcStatusCode InitDatabase(NativeAdbcDatabase* 
nativeDatabase, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeDatabase->private_data);
+            DatabaseStub stub = (DatabaseStub)gch.Target;
+            return stub.Init(ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
ReleaseDatabase(NativeAdbcDatabase* nativeDatabase, NativeAdbcError* error)
+        {
+            if (nativeDatabase->private_data == null)
+            {
+                return AdbcStatusCode.UnknownError;
+            }
+
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeDatabase->private_data);
+            DatabaseStub stub = (DatabaseStub)gch.Target;
+            stub.Dispose();
+            gch.Free();
+            nativeDatabase->private_data = null;
+            return AdbcStatusCode.Success;
+        }
+
+        private unsafe static AdbcStatusCode 
SetConnectionOption(NativeAdbcConnection* nativeConnection, byte* name, byte* 
value, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            return stub.SetOption(name, value, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
SetDatabaseOption(NativeAdbcDatabase* nativeDatabase, byte* name, byte* value, 
NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeDatabase->private_data);
+            DatabaseStub stub = (DatabaseStub)gch.Target;
+
+            return stub.SetOption(name, value, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
InitConnection(NativeAdbcConnection* nativeConnection, NativeAdbcDatabase* 
database, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            return stub.InitConnection(ref *database, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
ReleaseConnection(NativeAdbcConnection* nativeConnection, NativeAdbcError* 
error)
+        {
+            if (nativeConnection->private_data == null)
+            {
+                return AdbcStatusCode.UnknownError;
+            }
+
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            stub.Dispose();
+            gch.Free();
+            nativeConnection->private_data = null;
+            return AdbcStatusCode.Success;
+        }
+
+        private unsafe static AdbcStatusCode 
GetConnectionInfo(NativeAdbcConnection* nativeConnection, uint* info_codes, int 
info_codes_length, CArrowArrayStream* stream, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            return stub.GetConnectionInfo(ref *nativeConnection, *info_codes, 
info_codes_length, ref *stream, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
SetStatementSqlQuery(NativeAdbcStatement* nativeStatement, byte* text, 
NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeStatement->private_data);
+            AdbcStatement stub = (AdbcStatement)gch.Target;
+
+            #if NETSTANDARD
+                stub.SqlQuery = 
MarshalExtensions.PtrToStringUTF8((IntPtr)text);
+            #else
+                stub.SqlQuery = Marshal.PtrToStringUTF8((IntPtr)text);
+            #endif
+
+            return AdbcStatusCode.Success;
+        }
+
+        private unsafe static AdbcStatusCode 
ExecuteStatementQuery(NativeAdbcStatement* nativeStatement, CArrowArrayStream* 
stream, long* rows, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeStatement->private_data);
+            AdbcStatement stub = (AdbcStatement)gch.Target;
+            var result = stub.ExecuteQuery();
+            if (rows != null)
+            {
+                *rows = result.RowCount;
+            }
+
+            GCHandle streamHandle = GCHandle.Alloc(result.Stream);
+            stream->private_data = (void*)GCHandle.ToIntPtr(streamHandle);
+
+            return 0;
+        }
+
+        private unsafe static AdbcStatusCode 
NewStatement(NativeAdbcConnection* nativeConnection, NativeAdbcStatement* 
nativeStatement, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            return stub.NewStatement(ref *nativeStatement, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
ReleaseStatement(NativeAdbcStatement* nativeStatement, NativeAdbcError* error)
+        {
+            if (nativeStatement->private_data == null)
+            {
+                return AdbcStatusCode.UnknownError;
+            }
+
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeStatement->private_data);
+            AdbcStatement stub = (AdbcStatement)gch.Target;
+            stub.Dispose();
+            gch.Free();
+            nativeStatement->private_data = null;
+            return AdbcStatusCode.Success;
+        }
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcDatabase
+    {
+        public void* private_data;
+        public NativeAdbcDriver* private_driver;
+
+        public static NativeAdbcDatabase* Create()
+        {
+            var ptr = 
(NativeAdbcDatabase*)Marshal.AllocHGlobal(sizeof(NativeAdbcDatabase));
+
+            ptr->private_data = null;
+            ptr->private_driver = null;
+            
+            return ptr;
+        }
+
+        /// <summary>
+        /// Free a pointer that was allocated in <see cref="Create"/>.
+        /// </summary>
+        /// <remarks>
+        /// Do not call this on a pointer that was allocated elsewhere.
+        /// </remarks>
+        public static void Free(NativeAdbcDatabase* database)
+        {
+            Marshal.FreeHGlobal((IntPtr)database);
+        }
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcConnection
+    {
+        public void* private_data;
+        public NativeAdbcDriver* private_driver;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcStatement
+    {
+        public void* private_data;
+        public NativeAdbcDriver* private_driver;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcPartitions
+    {
+        /// <summary>
+        /// The number of partitions.
+        /// </summary>
+        public int num_partitions;
+
+        /// <summary>
+        /// The partitions of the result set, where each entry (up to
+        /// num_partitions entries) is an opaque identifier that can be
+        /// passed to AdbcConnectionReadPartition.
+        /// </summary>
+        public sbyte** partitions;
+
+        /// <summary>
+        /// The length of each corresponding entry in partitions.
+        /// </summary>
+        public int* partition_lengths;
+
+        /// <summary>
+        /// Opaque implementation-defined state.
+        /// This field is NULLPTR iff the connection is unintialized/freed.
+        /// </summary>
+        public void* private_data;
+
+        /// <summary>
+        /// Release the contained partitions.
+        ///
+        /// Unlike other structures, this is an embedded callback to make it
+        /// easier for the driver manager and driver to cooperate.
+        /// </summary>
+        public delegate* unmanaged[Stdcall]<NativeAdbcPartitions*, void> 
release; 
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcError
+    {
+        /// <summary>
+        /// The error message.
+        /// </summary>
+        public char* message;

Review Comment:
   ```suggestion
           public byte* message;
   ```



##########
csharp/src/Apache.Arrow.Adbc.FlightSql/Apache.Arrow.Adbc.FlightSql.csproj:
##########
@@ -0,0 +1,20 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
+    <AssemblyName>$(MSBuildProjectName)</AssemblyName>
+    <RootNamespace>Apache.Arrow.Adbc.FlightSql</RootNamespace>
+    <Version>0.1.0</Version>

Review Comment:
   ```suggestion
   ```



##########
csharp/src/Apache.Arrow.Adbc/Core/Interop.cs:
##########
@@ -0,0 +1,694 @@
+/*
+ * 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.Buffers;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using Apache.Arrow.Adbc.Interop;
+using Apache.Arrow.C;
+using static System.Net.Mime.MediaTypeNames;
+
+#if NETSTANDARD
+using Apache.Arrow.Adbc.Extensions;
+#endif
+
+namespace Apache.Arrow.Adbc.Core
+{
+    internal static class AdbcInterop
+    {
+        private unsafe static readonly NativeDelegate<ErrorRelease> 
releaseError = new NativeDelegate<ErrorRelease>(ReleaseError);
+        private unsafe static readonly NativeDelegate<DriverRelease> 
releaseDriver = new NativeDelegate<DriverRelease>(ReleaseDriver);
+
+        private unsafe static readonly NativeDelegate<DatabaseFn> databaseInit 
= new NativeDelegate<DatabaseFn>(InitDatabase);
+        private unsafe static readonly NativeDelegate<DatabaseFn> 
databaseRelease = new NativeDelegate<DatabaseFn>(ReleaseDatabase);
+        private unsafe static readonly NativeDelegate<DatabaseSetOption> 
databaseSetOption = new NativeDelegate<DatabaseSetOption>(SetDatabaseOption);
+
+        private unsafe static readonly NativeDelegate<ConnectionInit> 
connectionInit = new NativeDelegate<ConnectionInit>(InitConnection);
+        private unsafe static readonly NativeDelegate<ConnectionFn> 
connectionRelease = new NativeDelegate<ConnectionFn>(ReleaseConnection);
+        private unsafe static readonly NativeDelegate<ConnectionGetInfo> 
connectionGetInfo = new NativeDelegate<ConnectionGetInfo>(GetConnectionInfo);
+        private unsafe static readonly NativeDelegate<ConnectionSetOption> 
connectionSetOption = new 
NativeDelegate<ConnectionSetOption>(SetConnectionOption);
+        
+        private unsafe static readonly NativeDelegate<StatementExecuteQuery> 
statementExecuteQuery = new 
NativeDelegate<StatementExecuteQuery>(ExecuteStatementQuery);
+        private unsafe static readonly NativeDelegate<StatementNew> 
statementNew = new NativeDelegate<StatementNew>(NewStatement);
+        private unsafe static readonly NativeDelegate<StatementFn> 
statementRelease = new NativeDelegate<StatementFn>(ReleaseStatement);
+        private unsafe static readonly NativeDelegate<StatementSetSqlQuery> 
statementSetSqlQuery = new 
NativeDelegate<StatementSetSqlQuery>(SetStatementSqlQuery);
+
+        public unsafe static AdbcStatusCode AdbcDriverInit(int version, 
NativeAdbcDriver* nativeDriver, NativeAdbcError* error, AdbcDriver driver)
+        {
+            DriverStub stub = new DriverStub(driver);
+            GCHandle handle = GCHandle.Alloc(stub);
+            nativeDriver->private_data = (void*)GCHandle.ToIntPtr(handle);
+            nativeDriver->release = (delegate* 
unmanaged[Stdcall]<NativeAdbcDriver*, NativeAdbcError*, 
AdbcStatusCode>)releaseDriver.Pointer;
+
+            nativeDriver->DatabaseInit = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, NativeAdbcError*, 
AdbcStatusCode>)databaseInit.Pointer;
+            nativeDriver->DatabaseNew = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, NativeAdbcError*, 
AdbcStatusCode>)stub.newDatabase.Pointer;
+            nativeDriver->DatabaseSetOption = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, byte*, byte*, NativeAdbcError*, 
AdbcStatusCode>) databaseSetOption.Pointer;
+            nativeDriver->DatabaseRelease = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, NativeAdbcError*, 
AdbcStatusCode>)databaseRelease.Pointer;
+
+            nativeDriver->ConnectionCommit = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcError*, 
AdbcStatusCode>)connectionRelease.Pointer;
+            //nativeDriver->ConnectionGetInfo = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection *, int*, int, CArrowArrayStream*, 
NativeAdbcError*, AdbcStatusCode>)connectionGetInfo.Pointer;
+            //nativeDriver->ConnectionGetTableSchema = null;
+            //nativeDriver->ConnectionGetTableTypes = null;
+            nativeDriver->ConnectionInit = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcDatabase*, 
NativeAdbcError*, AdbcStatusCode>)connectionInit.Pointer;
+            nativeDriver->ConnectionNew = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcError*, 
AdbcStatusCode>)stub.newConnection.Pointer;
+            nativeDriver->ConnectionSetOption = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, byte*, byte*, NativeAdbcError*, 
AdbcStatusCode>)connectionSetOption.Pointer;
+            //nativeDriver->ConnectionReadPartition = null;
+            nativeDriver->ConnectionRelease = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcError*, 
AdbcStatusCode>)connectionRelease.Pointer;
+            //nativeDriver->ConnectionRollback = null;
+
+           // nativeDriver->StatementBind = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, CArrowArray*, CArrowSchema*, 
NativeAdbcError*, AdbcStatusCode>)
+            nativeDriver->StatementNew = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcStatement*, 
NativeAdbcError*, AdbcStatusCode>)statementNew.Pointer;
+            nativeDriver->StatementSetSqlQuery = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, byte*, NativeAdbcError *, 
AdbcStatusCode >)statementSetSqlQuery.Pointer;
+            nativeDriver->StatementExecuteQuery = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, CArrowArrayStream*, long*, 
NativeAdbcError*, AdbcStatusCode>)statementExecuteQuery.Pointer;
+            nativeDriver->StatementPrepare = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, NativeAdbcError*, 
AdbcStatusCode>)statementRelease.Pointer;
+            nativeDriver->StatementRelease = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, NativeAdbcError*, 
AdbcStatusCode>)statementRelease.Pointer;
+            
+            return 0;
+        }
+
+        private unsafe static void ReleaseError(NativeAdbcError* error)
+        {
+            if (error != null && ((IntPtr)error->message) != IntPtr.Zero)
+            {
+                Marshal.FreeCoTaskMem((IntPtr)error->message);

Review Comment:
   Is this right? Why are we using COM memory? We should be using 
`Marshal.AllocHGlobal` and `Marshal.FreeHGlobal`.



##########
csharp/src/Apache.Arrow.Adbc.FlightSql/Apache.Arrow.Adbc.FlightSql.csproj:
##########
@@ -0,0 +1,20 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
+    <AssemblyName>$(MSBuildProjectName)</AssemblyName>
+    <RootNamespace>Apache.Arrow.Adbc.FlightSql</RootNamespace>
+    <Version>0.1.0</Version>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Apache.Arrow.Flight" Version="12.0.0" />
+    <PackageReference Include="Grpc.Net.Client.Web" Version="2.53.0" />
+    <PackageReference Include="System.Net.Http.WinHttpHandler" Version="7.0.0" 
/>

Review Comment:
   ```suggestion
       <PackageReference Include="System.Net.Http.WinHttpHandler" 
Version="7.0.0" Condition="'$(TargetFrameworkIdentifier)' == '.NETStandard'" />
   ```
   
   We don't want to bring this dependency in on `net6.0`+.



##########
csharp/src/Apache.Arrow.Adbc/Extensions/MarshalExtensions.netstandard.cs:
##########
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+#if NETSTANDARD
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Apache.Arrow.Adbc.Extensions
+{
+    public static class MarshalExtensions
+    {
+        public static unsafe string PtrToStringUTF8(IntPtr intPtr)
+        {
+            if (intPtr == null) 
+            {
+                return null;
+            }
+
+            unsafe
+            {
+                byte* source = (byte*)intPtr;
+                int nbBytes =  source[0];

Review Comment:
   Is this right? 
   
   1. These strings have the length at the beginning? Is there some guarantee 
about that?
   2. Since `source` is a `byte*`, `source[0]` returns a `byte`, which means 
the `nbBytes` can only be between 0-255. These strings can't be longer than 
that?
   
   I would have assumed we would need to `strlen` here - like the real 
Marshal.PtrToStringUTF8 does - 
https://github.com/dotnet/runtime/blob/7febca6802545d8c96e386187d536bc100d40337/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs#L76



##########
csharp/src/Apache.Arrow.Adbc/Core/Interop.cs:
##########
@@ -0,0 +1,694 @@
+/*
+ * 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.Buffers;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using Apache.Arrow.Adbc.Interop;
+using Apache.Arrow.C;
+using static System.Net.Mime.MediaTypeNames;
+
+#if NETSTANDARD
+using Apache.Arrow.Adbc.Extensions;
+#endif
+
+namespace Apache.Arrow.Adbc.Core
+{
+    internal static class AdbcInterop
+    {
+        private unsafe static readonly NativeDelegate<ErrorRelease> 
releaseError = new NativeDelegate<ErrorRelease>(ReleaseError);
+        private unsafe static readonly NativeDelegate<DriverRelease> 
releaseDriver = new NativeDelegate<DriverRelease>(ReleaseDriver);
+
+        private unsafe static readonly NativeDelegate<DatabaseFn> databaseInit 
= new NativeDelegate<DatabaseFn>(InitDatabase);
+        private unsafe static readonly NativeDelegate<DatabaseFn> 
databaseRelease = new NativeDelegate<DatabaseFn>(ReleaseDatabase);
+        private unsafe static readonly NativeDelegate<DatabaseSetOption> 
databaseSetOption = new NativeDelegate<DatabaseSetOption>(SetDatabaseOption);
+
+        private unsafe static readonly NativeDelegate<ConnectionInit> 
connectionInit = new NativeDelegate<ConnectionInit>(InitConnection);
+        private unsafe static readonly NativeDelegate<ConnectionFn> 
connectionRelease = new NativeDelegate<ConnectionFn>(ReleaseConnection);
+        private unsafe static readonly NativeDelegate<ConnectionGetInfo> 
connectionGetInfo = new NativeDelegate<ConnectionGetInfo>(GetConnectionInfo);
+        private unsafe static readonly NativeDelegate<ConnectionSetOption> 
connectionSetOption = new 
NativeDelegate<ConnectionSetOption>(SetConnectionOption);
+        
+        private unsafe static readonly NativeDelegate<StatementExecuteQuery> 
statementExecuteQuery = new 
NativeDelegate<StatementExecuteQuery>(ExecuteStatementQuery);
+        private unsafe static readonly NativeDelegate<StatementNew> 
statementNew = new NativeDelegate<StatementNew>(NewStatement);
+        private unsafe static readonly NativeDelegate<StatementFn> 
statementRelease = new NativeDelegate<StatementFn>(ReleaseStatement);
+        private unsafe static readonly NativeDelegate<StatementSetSqlQuery> 
statementSetSqlQuery = new 
NativeDelegate<StatementSetSqlQuery>(SetStatementSqlQuery);
+
+        public unsafe static AdbcStatusCode AdbcDriverInit(int version, 
NativeAdbcDriver* nativeDriver, NativeAdbcError* error, AdbcDriver driver)
+        {
+            DriverStub stub = new DriverStub(driver);
+            GCHandle handle = GCHandle.Alloc(stub);
+            nativeDriver->private_data = (void*)GCHandle.ToIntPtr(handle);
+            nativeDriver->release = (delegate* 
unmanaged[Stdcall]<NativeAdbcDriver*, NativeAdbcError*, 
AdbcStatusCode>)releaseDriver.Pointer;
+
+            nativeDriver->DatabaseInit = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, NativeAdbcError*, 
AdbcStatusCode>)databaseInit.Pointer;
+            nativeDriver->DatabaseNew = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, NativeAdbcError*, 
AdbcStatusCode>)stub.newDatabase.Pointer;
+            nativeDriver->DatabaseSetOption = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, byte*, byte*, NativeAdbcError*, 
AdbcStatusCode>) databaseSetOption.Pointer;
+            nativeDriver->DatabaseRelease = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, NativeAdbcError*, 
AdbcStatusCode>)databaseRelease.Pointer;
+
+            nativeDriver->ConnectionCommit = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcError*, 
AdbcStatusCode>)connectionRelease.Pointer;
+            //nativeDriver->ConnectionGetInfo = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection *, int*, int, CArrowArrayStream*, 
NativeAdbcError*, AdbcStatusCode>)connectionGetInfo.Pointer;

Review Comment:
   Do these need to be filled out?



##########
csharp/src/Apache.Arrow.Adbc/Core/Interop.cs:
##########
@@ -0,0 +1,694 @@
+/*
+ * 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.Buffers;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using Apache.Arrow.Adbc.Interop;
+using Apache.Arrow.C;
+using static System.Net.Mime.MediaTypeNames;
+
+#if NETSTANDARD
+using Apache.Arrow.Adbc.Extensions;
+#endif
+
+namespace Apache.Arrow.Adbc.Core
+{
+    internal static class AdbcInterop
+    {
+        private unsafe static readonly NativeDelegate<ErrorRelease> 
releaseError = new NativeDelegate<ErrorRelease>(ReleaseError);
+        private unsafe static readonly NativeDelegate<DriverRelease> 
releaseDriver = new NativeDelegate<DriverRelease>(ReleaseDriver);
+
+        private unsafe static readonly NativeDelegate<DatabaseFn> databaseInit 
= new NativeDelegate<DatabaseFn>(InitDatabase);
+        private unsafe static readonly NativeDelegate<DatabaseFn> 
databaseRelease = new NativeDelegate<DatabaseFn>(ReleaseDatabase);
+        private unsafe static readonly NativeDelegate<DatabaseSetOption> 
databaseSetOption = new NativeDelegate<DatabaseSetOption>(SetDatabaseOption);
+
+        private unsafe static readonly NativeDelegate<ConnectionInit> 
connectionInit = new NativeDelegate<ConnectionInit>(InitConnection);
+        private unsafe static readonly NativeDelegate<ConnectionFn> 
connectionRelease = new NativeDelegate<ConnectionFn>(ReleaseConnection);
+        private unsafe static readonly NativeDelegate<ConnectionGetInfo> 
connectionGetInfo = new NativeDelegate<ConnectionGetInfo>(GetConnectionInfo);
+        private unsafe static readonly NativeDelegate<ConnectionSetOption> 
connectionSetOption = new 
NativeDelegate<ConnectionSetOption>(SetConnectionOption);
+        
+        private unsafe static readonly NativeDelegate<StatementExecuteQuery> 
statementExecuteQuery = new 
NativeDelegate<StatementExecuteQuery>(ExecuteStatementQuery);
+        private unsafe static readonly NativeDelegate<StatementNew> 
statementNew = new NativeDelegate<StatementNew>(NewStatement);
+        private unsafe static readonly NativeDelegate<StatementFn> 
statementRelease = new NativeDelegate<StatementFn>(ReleaseStatement);
+        private unsafe static readonly NativeDelegate<StatementSetSqlQuery> 
statementSetSqlQuery = new 
NativeDelegate<StatementSetSqlQuery>(SetStatementSqlQuery);
+
+        public unsafe static AdbcStatusCode AdbcDriverInit(int version, 
NativeAdbcDriver* nativeDriver, NativeAdbcError* error, AdbcDriver driver)
+        {
+            DriverStub stub = new DriverStub(driver);
+            GCHandle handle = GCHandle.Alloc(stub);
+            nativeDriver->private_data = (void*)GCHandle.ToIntPtr(handle);
+            nativeDriver->release = (delegate* 
unmanaged[Stdcall]<NativeAdbcDriver*, NativeAdbcError*, 
AdbcStatusCode>)releaseDriver.Pointer;
+
+            nativeDriver->DatabaseInit = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, NativeAdbcError*, 
AdbcStatusCode>)databaseInit.Pointer;
+            nativeDriver->DatabaseNew = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, NativeAdbcError*, 
AdbcStatusCode>)stub.newDatabase.Pointer;
+            nativeDriver->DatabaseSetOption = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, byte*, byte*, NativeAdbcError*, 
AdbcStatusCode>) databaseSetOption.Pointer;
+            nativeDriver->DatabaseRelease = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, NativeAdbcError*, 
AdbcStatusCode>)databaseRelease.Pointer;
+
+            nativeDriver->ConnectionCommit = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcError*, 
AdbcStatusCode>)connectionRelease.Pointer;
+            //nativeDriver->ConnectionGetInfo = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection *, int*, int, CArrowArrayStream*, 
NativeAdbcError*, AdbcStatusCode>)connectionGetInfo.Pointer;
+            //nativeDriver->ConnectionGetTableSchema = null;
+            //nativeDriver->ConnectionGetTableTypes = null;
+            nativeDriver->ConnectionInit = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcDatabase*, 
NativeAdbcError*, AdbcStatusCode>)connectionInit.Pointer;
+            nativeDriver->ConnectionNew = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcError*, 
AdbcStatusCode>)stub.newConnection.Pointer;
+            nativeDriver->ConnectionSetOption = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, byte*, byte*, NativeAdbcError*, 
AdbcStatusCode>)connectionSetOption.Pointer;
+            //nativeDriver->ConnectionReadPartition = null;
+            nativeDriver->ConnectionRelease = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcError*, 
AdbcStatusCode>)connectionRelease.Pointer;
+            //nativeDriver->ConnectionRollback = null;
+
+           // nativeDriver->StatementBind = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, CArrowArray*, CArrowSchema*, 
NativeAdbcError*, AdbcStatusCode>)
+            nativeDriver->StatementNew = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcStatement*, 
NativeAdbcError*, AdbcStatusCode>)statementNew.Pointer;
+            nativeDriver->StatementSetSqlQuery = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, byte*, NativeAdbcError *, 
AdbcStatusCode >)statementSetSqlQuery.Pointer;
+            nativeDriver->StatementExecuteQuery = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, CArrowArrayStream*, long*, 
NativeAdbcError*, AdbcStatusCode>)statementExecuteQuery.Pointer;
+            nativeDriver->StatementPrepare = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, NativeAdbcError*, 
AdbcStatusCode>)statementRelease.Pointer;
+            nativeDriver->StatementRelease = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, NativeAdbcError*, 
AdbcStatusCode>)statementRelease.Pointer;
+            
+            return 0;
+        }
+
+        private unsafe static void ReleaseError(NativeAdbcError* error)
+        {
+            if (error != null && ((IntPtr)error->message) != IntPtr.Zero)
+            {
+                Marshal.FreeCoTaskMem((IntPtr)error->message);
+            }
+        }
+
+        private unsafe static AdbcStatusCode SetError(NativeAdbcError* error, 
Exception exception)
+        {
+            ReleaseError(error);
+
+            #if NETSTANDARD
+                error->message = 
(char*)MarshalExtensions.StringToCoTaskMemUTF8(exception.Message);
+            #else
+                error->message = 
(char*)Marshal.StringToCoTaskMemUTF8(exception.Message);
+            #endif
+
+            error->sqlstate0 = (char)0;
+            error->sqlstate1 = (char)0;
+            error->sqlstate2 = (char)0;
+            error->sqlstate3 = (char)0;
+            error->sqlstate4 = (char)0;
+            error->vendor_code = 0;
+            error->vendor_code = 0;
+            error->release = (delegate* unmanaged[Stdcall]<NativeAdbcError*, 
void>)releaseError.Pointer;
+            
+            return AdbcStatusCode.UnknownError;
+        }
+
+        private sealed class PinnedArray : IDisposable
+        {
+            IArrowArray _array;
+            MemoryHandle[] pinnedHandles;
+
+            public PinnedArray(IArrowArray array)
+            {
+                _array = array;
+                pinnedHandles = new MemoryHandle[GetHandleCount(array.Data)];
+                int index = 0;
+                PinBuffers(array.Data, pinnedHandles, ref index);
+                Debug.Assert(index == pinnedHandles.Length);
+            }
+
+            public void Dispose()
+            {
+                if (_array != null)
+                {
+                    _array.Dispose();
+                    foreach (MemoryHandle handle in pinnedHandles)
+                    {
+                        handle.Dispose();
+                    }
+                    _array = null;
+                }
+            }
+
+            static int GetHandleCount(ArrayData data)
+            {
+                int handleCount = data.Buffers.Length;
+                foreach (ArrayData child in data.Children)
+                {
+                    handleCount += GetHandleCount(child);
+                }
+                if (data.Dictionary != null)
+                {
+                    handleCount += GetHandleCount(data.Dictionary);
+                }
+                return handleCount;
+            }
+
+            static void PinBuffers(ArrayData data, MemoryHandle[] handles, ref 
int index)
+            {
+                foreach (ArrowBuffer buffer in data.Buffers)
+                {
+                    handles[index++] = buffer.Memory.Pin();
+                }
+                foreach (ArrayData child in data.Children)
+                {
+                    PinBuffers(child, handles, ref index);
+                }
+                if (data.Dictionary != null)
+                {
+                    PinBuffers(data.Dictionary, handles, ref index);
+                }
+            }
+        }
+
+        private static IntPtr FromDisposable(IDisposable d)
+        {
+            GCHandle gch = GCHandle.Alloc(d);
+            return GCHandle.ToIntPtr(gch);
+        }
+
+        private static void Dispose(ref IntPtr p)
+        {
+            GCHandle gch = GCHandle.FromIntPtr(p);
+            ((IDisposable)gch.Target).Dispose();
+            gch.Free();
+            p = IntPtr.Zero;
+        }
+
+        private unsafe static AdbcStatusCode ReleaseDriver(NativeAdbcDriver* 
nativeDriver, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeDriver->private_data);
+            DriverStub stub = (DriverStub)gch.Target;
+            stub.Dispose();
+            gch.Free();
+            nativeDriver->private_data = null;
+            return 0;
+        }
+
+        private unsafe static AdbcStatusCode InitDatabase(NativeAdbcDatabase* 
nativeDatabase, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeDatabase->private_data);
+            DatabaseStub stub = (DatabaseStub)gch.Target;
+            return stub.Init(ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
ReleaseDatabase(NativeAdbcDatabase* nativeDatabase, NativeAdbcError* error)
+        {
+            if (nativeDatabase->private_data == null)
+            {
+                return AdbcStatusCode.UnknownError;
+            }
+
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeDatabase->private_data);
+            DatabaseStub stub = (DatabaseStub)gch.Target;
+            stub.Dispose();
+            gch.Free();
+            nativeDatabase->private_data = null;
+            return AdbcStatusCode.Success;
+        }
+
+        private unsafe static AdbcStatusCode 
SetConnectionOption(NativeAdbcConnection* nativeConnection, byte* name, byte* 
value, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            return stub.SetOption(name, value, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
SetDatabaseOption(NativeAdbcDatabase* nativeDatabase, byte* name, byte* value, 
NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeDatabase->private_data);
+            DatabaseStub stub = (DatabaseStub)gch.Target;
+
+            return stub.SetOption(name, value, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
InitConnection(NativeAdbcConnection* nativeConnection, NativeAdbcDatabase* 
database, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            return stub.InitConnection(ref *database, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
ReleaseConnection(NativeAdbcConnection* nativeConnection, NativeAdbcError* 
error)
+        {
+            if (nativeConnection->private_data == null)
+            {
+                return AdbcStatusCode.UnknownError;
+            }
+
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            stub.Dispose();
+            gch.Free();
+            nativeConnection->private_data = null;
+            return AdbcStatusCode.Success;
+        }
+
+        private unsafe static AdbcStatusCode 
GetConnectionInfo(NativeAdbcConnection* nativeConnection, uint* info_codes, int 
info_codes_length, CArrowArrayStream* stream, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            return stub.GetConnectionInfo(ref *nativeConnection, *info_codes, 
info_codes_length, ref *stream, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
SetStatementSqlQuery(NativeAdbcStatement* nativeStatement, byte* text, 
NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeStatement->private_data);
+            AdbcStatement stub = (AdbcStatement)gch.Target;
+
+            #if NETSTANDARD
+                stub.SqlQuery = 
MarshalExtensions.PtrToStringUTF8((IntPtr)text);
+            #else
+                stub.SqlQuery = Marshal.PtrToStringUTF8((IntPtr)text);
+            #endif
+
+            return AdbcStatusCode.Success;
+        }
+
+        private unsafe static AdbcStatusCode 
ExecuteStatementQuery(NativeAdbcStatement* nativeStatement, CArrowArrayStream* 
stream, long* rows, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeStatement->private_data);
+            AdbcStatement stub = (AdbcStatement)gch.Target;
+            var result = stub.ExecuteQuery();
+            if (rows != null)
+            {
+                *rows = result.RowCount;
+            }
+
+            GCHandle streamHandle = GCHandle.Alloc(result.Stream);
+            stream->private_data = (void*)GCHandle.ToIntPtr(streamHandle);
+
+            return 0;
+        }
+
+        private unsafe static AdbcStatusCode 
NewStatement(NativeAdbcConnection* nativeConnection, NativeAdbcStatement* 
nativeStatement, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            return stub.NewStatement(ref *nativeStatement, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
ReleaseStatement(NativeAdbcStatement* nativeStatement, NativeAdbcError* error)
+        {
+            if (nativeStatement->private_data == null)
+            {
+                return AdbcStatusCode.UnknownError;
+            }
+
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeStatement->private_data);
+            AdbcStatement stub = (AdbcStatement)gch.Target;
+            stub.Dispose();
+            gch.Free();
+            nativeStatement->private_data = null;
+            return AdbcStatusCode.Success;
+        }
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcDatabase
+    {
+        public void* private_data;
+        public NativeAdbcDriver* private_driver;
+
+        public static NativeAdbcDatabase* Create()
+        {
+            var ptr = 
(NativeAdbcDatabase*)Marshal.AllocHGlobal(sizeof(NativeAdbcDatabase));
+
+            ptr->private_data = null;
+            ptr->private_driver = null;
+            
+            return ptr;
+        }
+
+        /// <summary>
+        /// Free a pointer that was allocated in <see cref="Create"/>.
+        /// </summary>
+        /// <remarks>
+        /// Do not call this on a pointer that was allocated elsewhere.
+        /// </remarks>
+        public static void Free(NativeAdbcDatabase* database)
+        {
+            Marshal.FreeHGlobal((IntPtr)database);
+        }
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcConnection
+    {
+        public void* private_data;
+        public NativeAdbcDriver* private_driver;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcStatement
+    {
+        public void* private_data;
+        public NativeAdbcDriver* private_driver;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcPartitions
+    {
+        /// <summary>
+        /// The number of partitions.
+        /// </summary>
+        public int num_partitions;
+
+        /// <summary>
+        /// The partitions of the result set, where each entry (up to
+        /// num_partitions entries) is an opaque identifier that can be
+        /// passed to AdbcConnectionReadPartition.
+        /// </summary>
+        public sbyte** partitions;
+
+        /// <summary>
+        /// The length of each corresponding entry in partitions.
+        /// </summary>
+        public int* partition_lengths;
+
+        /// <summary>
+        /// Opaque implementation-defined state.
+        /// This field is NULLPTR iff the connection is unintialized/freed.
+        /// </summary>
+        public void* private_data;
+
+        /// <summary>
+        /// Release the contained partitions.
+        ///
+        /// Unlike other structures, this is an embedded callback to make it
+        /// easier for the driver manager and driver to cooperate.
+        /// </summary>
+        public delegate* unmanaged[Stdcall]<NativeAdbcPartitions*, void> 
release; 
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcError
+    {
+        /// <summary>
+        /// The error message.
+        /// </summary>
+        public char* message;
+
+        /// <summary>
+        /// A vendor-specific error code, if applicable.
+        /// </summary>
+        public int vendor_code;
+
+        /// <summary>
+        /// A SQLSTATE error code, if provided, as defined by the
+        ///   SQL:2003 standard.  If not set, it should be set to
+        ///   "\0\0\0\0\0".
+        ///</summary>
+        public char sqlstate0;
+        public char sqlstate1;
+        public char sqlstate2;
+        public char sqlstate3;
+        public char sqlstate4;

Review Comment:
   ```suggestion
           public byte sqlstate0;
           public byte sqlstate1;
           public byte sqlstate2;
           public byte sqlstate3;
           public byte sqlstate4;
   ```



##########
csharp/src/Apache.Arrow.Adbc/Core/Interop.cs:
##########
@@ -0,0 +1,694 @@
+/*
+ * 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.Buffers;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using Apache.Arrow.Adbc.Interop;
+using Apache.Arrow.C;
+using static System.Net.Mime.MediaTypeNames;
+
+#if NETSTANDARD
+using Apache.Arrow.Adbc.Extensions;
+#endif
+
+namespace Apache.Arrow.Adbc.Core
+{
+    internal static class AdbcInterop
+    {
+        private unsafe static readonly NativeDelegate<ErrorRelease> 
releaseError = new NativeDelegate<ErrorRelease>(ReleaseError);
+        private unsafe static readonly NativeDelegate<DriverRelease> 
releaseDriver = new NativeDelegate<DriverRelease>(ReleaseDriver);
+
+        private unsafe static readonly NativeDelegate<DatabaseFn> databaseInit 
= new NativeDelegate<DatabaseFn>(InitDatabase);
+        private unsafe static readonly NativeDelegate<DatabaseFn> 
databaseRelease = new NativeDelegate<DatabaseFn>(ReleaseDatabase);
+        private unsafe static readonly NativeDelegate<DatabaseSetOption> 
databaseSetOption = new NativeDelegate<DatabaseSetOption>(SetDatabaseOption);
+
+        private unsafe static readonly NativeDelegate<ConnectionInit> 
connectionInit = new NativeDelegate<ConnectionInit>(InitConnection);
+        private unsafe static readonly NativeDelegate<ConnectionFn> 
connectionRelease = new NativeDelegate<ConnectionFn>(ReleaseConnection);
+        private unsafe static readonly NativeDelegate<ConnectionGetInfo> 
connectionGetInfo = new NativeDelegate<ConnectionGetInfo>(GetConnectionInfo);
+        private unsafe static readonly NativeDelegate<ConnectionSetOption> 
connectionSetOption = new 
NativeDelegate<ConnectionSetOption>(SetConnectionOption);
+        
+        private unsafe static readonly NativeDelegate<StatementExecuteQuery> 
statementExecuteQuery = new 
NativeDelegate<StatementExecuteQuery>(ExecuteStatementQuery);
+        private unsafe static readonly NativeDelegate<StatementNew> 
statementNew = new NativeDelegate<StatementNew>(NewStatement);
+        private unsafe static readonly NativeDelegate<StatementFn> 
statementRelease = new NativeDelegate<StatementFn>(ReleaseStatement);
+        private unsafe static readonly NativeDelegate<StatementSetSqlQuery> 
statementSetSqlQuery = new 
NativeDelegate<StatementSetSqlQuery>(SetStatementSqlQuery);
+
+        public unsafe static AdbcStatusCode AdbcDriverInit(int version, 
NativeAdbcDriver* nativeDriver, NativeAdbcError* error, AdbcDriver driver)
+        {
+            DriverStub stub = new DriverStub(driver);
+            GCHandle handle = GCHandle.Alloc(stub);
+            nativeDriver->private_data = (void*)GCHandle.ToIntPtr(handle);
+            nativeDriver->release = (delegate* 
unmanaged[Stdcall]<NativeAdbcDriver*, NativeAdbcError*, 
AdbcStatusCode>)releaseDriver.Pointer;
+
+            nativeDriver->DatabaseInit = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, NativeAdbcError*, 
AdbcStatusCode>)databaseInit.Pointer;
+            nativeDriver->DatabaseNew = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, NativeAdbcError*, 
AdbcStatusCode>)stub.newDatabase.Pointer;
+            nativeDriver->DatabaseSetOption = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, byte*, byte*, NativeAdbcError*, 
AdbcStatusCode>) databaseSetOption.Pointer;
+            nativeDriver->DatabaseRelease = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, NativeAdbcError*, 
AdbcStatusCode>)databaseRelease.Pointer;
+
+            nativeDriver->ConnectionCommit = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcError*, 
AdbcStatusCode>)connectionRelease.Pointer;
+            //nativeDriver->ConnectionGetInfo = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection *, int*, int, CArrowArrayStream*, 
NativeAdbcError*, AdbcStatusCode>)connectionGetInfo.Pointer;
+            //nativeDriver->ConnectionGetTableSchema = null;
+            //nativeDriver->ConnectionGetTableTypes = null;
+            nativeDriver->ConnectionInit = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcDatabase*, 
NativeAdbcError*, AdbcStatusCode>)connectionInit.Pointer;
+            nativeDriver->ConnectionNew = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcError*, 
AdbcStatusCode>)stub.newConnection.Pointer;
+            nativeDriver->ConnectionSetOption = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, byte*, byte*, NativeAdbcError*, 
AdbcStatusCode>)connectionSetOption.Pointer;
+            //nativeDriver->ConnectionReadPartition = null;
+            nativeDriver->ConnectionRelease = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcError*, 
AdbcStatusCode>)connectionRelease.Pointer;
+            //nativeDriver->ConnectionRollback = null;
+
+           // nativeDriver->StatementBind = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, CArrowArray*, CArrowSchema*, 
NativeAdbcError*, AdbcStatusCode>)
+            nativeDriver->StatementNew = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcStatement*, 
NativeAdbcError*, AdbcStatusCode>)statementNew.Pointer;
+            nativeDriver->StatementSetSqlQuery = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, byte*, NativeAdbcError *, 
AdbcStatusCode >)statementSetSqlQuery.Pointer;
+            nativeDriver->StatementExecuteQuery = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, CArrowArrayStream*, long*, 
NativeAdbcError*, AdbcStatusCode>)statementExecuteQuery.Pointer;
+            nativeDriver->StatementPrepare = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, NativeAdbcError*, 
AdbcStatusCode>)statementRelease.Pointer;
+            nativeDriver->StatementRelease = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, NativeAdbcError*, 
AdbcStatusCode>)statementRelease.Pointer;
+            
+            return 0;
+        }
+
+        private unsafe static void ReleaseError(NativeAdbcError* error)
+        {
+            if (error != null && ((IntPtr)error->message) != IntPtr.Zero)
+            {
+                Marshal.FreeCoTaskMem((IntPtr)error->message);
+            }
+        }
+
+        private unsafe static AdbcStatusCode SetError(NativeAdbcError* error, 
Exception exception)
+        {
+            ReleaseError(error);
+
+            #if NETSTANDARD
+                error->message = 
(char*)MarshalExtensions.StringToCoTaskMemUTF8(exception.Message);
+            #else
+                error->message = 
(char*)Marshal.StringToCoTaskMemUTF8(exception.Message);
+            #endif
+
+            error->sqlstate0 = (char)0;
+            error->sqlstate1 = (char)0;
+            error->sqlstate2 = (char)0;
+            error->sqlstate3 = (char)0;
+            error->sqlstate4 = (char)0;
+            error->vendor_code = 0;
+            error->vendor_code = 0;
+            error->release = (delegate* unmanaged[Stdcall]<NativeAdbcError*, 
void>)releaseError.Pointer;
+            
+            return AdbcStatusCode.UnknownError;
+        }
+
+        private sealed class PinnedArray : IDisposable
+        {
+            IArrowArray _array;
+            MemoryHandle[] pinnedHandles;
+
+            public PinnedArray(IArrowArray array)
+            {
+                _array = array;
+                pinnedHandles = new MemoryHandle[GetHandleCount(array.Data)];
+                int index = 0;
+                PinBuffers(array.Data, pinnedHandles, ref index);
+                Debug.Assert(index == pinnedHandles.Length);
+            }
+
+            public void Dispose()
+            {
+                if (_array != null)
+                {
+                    _array.Dispose();
+                    foreach (MemoryHandle handle in pinnedHandles)
+                    {
+                        handle.Dispose();
+                    }
+                    _array = null;
+                }
+            }
+
+            static int GetHandleCount(ArrayData data)
+            {
+                int handleCount = data.Buffers.Length;
+                foreach (ArrayData child in data.Children)
+                {
+                    handleCount += GetHandleCount(child);
+                }
+                if (data.Dictionary != null)
+                {
+                    handleCount += GetHandleCount(data.Dictionary);
+                }
+                return handleCount;
+            }
+
+            static void PinBuffers(ArrayData data, MemoryHandle[] handles, ref 
int index)
+            {
+                foreach (ArrowBuffer buffer in data.Buffers)
+                {
+                    handles[index++] = buffer.Memory.Pin();
+                }
+                foreach (ArrayData child in data.Children)
+                {
+                    PinBuffers(child, handles, ref index);
+                }
+                if (data.Dictionary != null)
+                {
+                    PinBuffers(data.Dictionary, handles, ref index);
+                }
+            }
+        }
+
+        private static IntPtr FromDisposable(IDisposable d)
+        {
+            GCHandle gch = GCHandle.Alloc(d);
+            return GCHandle.ToIntPtr(gch);
+        }
+
+        private static void Dispose(ref IntPtr p)
+        {
+            GCHandle gch = GCHandle.FromIntPtr(p);
+            ((IDisposable)gch.Target).Dispose();
+            gch.Free();
+            p = IntPtr.Zero;
+        }
+
+        private unsafe static AdbcStatusCode ReleaseDriver(NativeAdbcDriver* 
nativeDriver, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeDriver->private_data);
+            DriverStub stub = (DriverStub)gch.Target;
+            stub.Dispose();
+            gch.Free();
+            nativeDriver->private_data = null;
+            return 0;
+        }
+
+        private unsafe static AdbcStatusCode InitDatabase(NativeAdbcDatabase* 
nativeDatabase, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeDatabase->private_data);
+            DatabaseStub stub = (DatabaseStub)gch.Target;
+            return stub.Init(ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
ReleaseDatabase(NativeAdbcDatabase* nativeDatabase, NativeAdbcError* error)
+        {
+            if (nativeDatabase->private_data == null)
+            {
+                return AdbcStatusCode.UnknownError;
+            }
+
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeDatabase->private_data);
+            DatabaseStub stub = (DatabaseStub)gch.Target;
+            stub.Dispose();
+            gch.Free();
+            nativeDatabase->private_data = null;
+            return AdbcStatusCode.Success;
+        }
+
+        private unsafe static AdbcStatusCode 
SetConnectionOption(NativeAdbcConnection* nativeConnection, byte* name, byte* 
value, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            return stub.SetOption(name, value, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
SetDatabaseOption(NativeAdbcDatabase* nativeDatabase, byte* name, byte* value, 
NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeDatabase->private_data);
+            DatabaseStub stub = (DatabaseStub)gch.Target;
+
+            return stub.SetOption(name, value, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
InitConnection(NativeAdbcConnection* nativeConnection, NativeAdbcDatabase* 
database, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            return stub.InitConnection(ref *database, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
ReleaseConnection(NativeAdbcConnection* nativeConnection, NativeAdbcError* 
error)
+        {
+            if (nativeConnection->private_data == null)
+            {
+                return AdbcStatusCode.UnknownError;
+            }
+
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            stub.Dispose();
+            gch.Free();
+            nativeConnection->private_data = null;
+            return AdbcStatusCode.Success;
+        }
+
+        private unsafe static AdbcStatusCode 
GetConnectionInfo(NativeAdbcConnection* nativeConnection, uint* info_codes, int 
info_codes_length, CArrowArrayStream* stream, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            return stub.GetConnectionInfo(ref *nativeConnection, *info_codes, 
info_codes_length, ref *stream, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
SetStatementSqlQuery(NativeAdbcStatement* nativeStatement, byte* text, 
NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeStatement->private_data);
+            AdbcStatement stub = (AdbcStatement)gch.Target;
+
+            #if NETSTANDARD
+                stub.SqlQuery = 
MarshalExtensions.PtrToStringUTF8((IntPtr)text);
+            #else
+                stub.SqlQuery = Marshal.PtrToStringUTF8((IntPtr)text);
+            #endif
+
+            return AdbcStatusCode.Success;
+        }
+
+        private unsafe static AdbcStatusCode 
ExecuteStatementQuery(NativeAdbcStatement* nativeStatement, CArrowArrayStream* 
stream, long* rows, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeStatement->private_data);
+            AdbcStatement stub = (AdbcStatement)gch.Target;
+            var result = stub.ExecuteQuery();
+            if (rows != null)
+            {
+                *rows = result.RowCount;
+            }
+
+            GCHandle streamHandle = GCHandle.Alloc(result.Stream);
+            stream->private_data = (void*)GCHandle.ToIntPtr(streamHandle);
+
+            return 0;
+        }
+
+        private unsafe static AdbcStatusCode 
NewStatement(NativeAdbcConnection* nativeConnection, NativeAdbcStatement* 
nativeStatement, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            return stub.NewStatement(ref *nativeStatement, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
ReleaseStatement(NativeAdbcStatement* nativeStatement, NativeAdbcError* error)
+        {
+            if (nativeStatement->private_data == null)
+            {
+                return AdbcStatusCode.UnknownError;
+            }
+
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeStatement->private_data);
+            AdbcStatement stub = (AdbcStatement)gch.Target;
+            stub.Dispose();
+            gch.Free();
+            nativeStatement->private_data = null;
+            return AdbcStatusCode.Success;
+        }
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcDatabase
+    {
+        public void* private_data;
+        public NativeAdbcDriver* private_driver;
+
+        public static NativeAdbcDatabase* Create()
+        {
+            var ptr = 
(NativeAdbcDatabase*)Marshal.AllocHGlobal(sizeof(NativeAdbcDatabase));
+
+            ptr->private_data = null;
+            ptr->private_driver = null;
+            
+            return ptr;
+        }
+
+        /// <summary>
+        /// Free a pointer that was allocated in <see cref="Create"/>.
+        /// </summary>
+        /// <remarks>
+        /// Do not call this on a pointer that was allocated elsewhere.
+        /// </remarks>
+        public static void Free(NativeAdbcDatabase* database)
+        {
+            Marshal.FreeHGlobal((IntPtr)database);
+        }
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcConnection
+    {
+        public void* private_data;
+        public NativeAdbcDriver* private_driver;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcStatement
+    {
+        public void* private_data;
+        public NativeAdbcDriver* private_driver;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcPartitions
+    {
+        /// <summary>
+        /// The number of partitions.
+        /// </summary>
+        public int num_partitions;
+
+        /// <summary>
+        /// The partitions of the result set, where each entry (up to
+        /// num_partitions entries) is an opaque identifier that can be
+        /// passed to AdbcConnectionReadPartition.
+        /// </summary>
+        public sbyte** partitions;

Review Comment:
   ```suggestion
           public byte** partitions;
   ```



##########
csharp/src/Apache.Arrow.Adbc/Core/Interop.cs:
##########
@@ -0,0 +1,694 @@
+/*
+ * 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.Buffers;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using Apache.Arrow.Adbc.Interop;
+using Apache.Arrow.C;
+using static System.Net.Mime.MediaTypeNames;
+
+#if NETSTANDARD
+using Apache.Arrow.Adbc.Extensions;
+#endif
+
+namespace Apache.Arrow.Adbc.Core
+{
+    internal static class AdbcInterop
+    {
+        private unsafe static readonly NativeDelegate<ErrorRelease> 
releaseError = new NativeDelegate<ErrorRelease>(ReleaseError);
+        private unsafe static readonly NativeDelegate<DriverRelease> 
releaseDriver = new NativeDelegate<DriverRelease>(ReleaseDriver);
+
+        private unsafe static readonly NativeDelegate<DatabaseFn> databaseInit 
= new NativeDelegate<DatabaseFn>(InitDatabase);
+        private unsafe static readonly NativeDelegate<DatabaseFn> 
databaseRelease = new NativeDelegate<DatabaseFn>(ReleaseDatabase);
+        private unsafe static readonly NativeDelegate<DatabaseSetOption> 
databaseSetOption = new NativeDelegate<DatabaseSetOption>(SetDatabaseOption);
+
+        private unsafe static readonly NativeDelegate<ConnectionInit> 
connectionInit = new NativeDelegate<ConnectionInit>(InitConnection);
+        private unsafe static readonly NativeDelegate<ConnectionFn> 
connectionRelease = new NativeDelegate<ConnectionFn>(ReleaseConnection);
+        private unsafe static readonly NativeDelegate<ConnectionGetInfo> 
connectionGetInfo = new NativeDelegate<ConnectionGetInfo>(GetConnectionInfo);
+        private unsafe static readonly NativeDelegate<ConnectionSetOption> 
connectionSetOption = new 
NativeDelegate<ConnectionSetOption>(SetConnectionOption);
+        
+        private unsafe static readonly NativeDelegate<StatementExecuteQuery> 
statementExecuteQuery = new 
NativeDelegate<StatementExecuteQuery>(ExecuteStatementQuery);
+        private unsafe static readonly NativeDelegate<StatementNew> 
statementNew = new NativeDelegate<StatementNew>(NewStatement);
+        private unsafe static readonly NativeDelegate<StatementFn> 
statementRelease = new NativeDelegate<StatementFn>(ReleaseStatement);
+        private unsafe static readonly NativeDelegate<StatementSetSqlQuery> 
statementSetSqlQuery = new 
NativeDelegate<StatementSetSqlQuery>(SetStatementSqlQuery);
+
+        public unsafe static AdbcStatusCode AdbcDriverInit(int version, 
NativeAdbcDriver* nativeDriver, NativeAdbcError* error, AdbcDriver driver)
+        {
+            DriverStub stub = new DriverStub(driver);
+            GCHandle handle = GCHandle.Alloc(stub);
+            nativeDriver->private_data = (void*)GCHandle.ToIntPtr(handle);
+            nativeDriver->release = (delegate* 
unmanaged[Stdcall]<NativeAdbcDriver*, NativeAdbcError*, 
AdbcStatusCode>)releaseDriver.Pointer;
+
+            nativeDriver->DatabaseInit = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, NativeAdbcError*, 
AdbcStatusCode>)databaseInit.Pointer;
+            nativeDriver->DatabaseNew = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, NativeAdbcError*, 
AdbcStatusCode>)stub.newDatabase.Pointer;
+            nativeDriver->DatabaseSetOption = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, byte*, byte*, NativeAdbcError*, 
AdbcStatusCode>) databaseSetOption.Pointer;
+            nativeDriver->DatabaseRelease = (delegate* 
unmanaged[Stdcall]<NativeAdbcDatabase*, NativeAdbcError*, 
AdbcStatusCode>)databaseRelease.Pointer;
+
+            nativeDriver->ConnectionCommit = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcError*, 
AdbcStatusCode>)connectionRelease.Pointer;
+            //nativeDriver->ConnectionGetInfo = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection *, int*, int, CArrowArrayStream*, 
NativeAdbcError*, AdbcStatusCode>)connectionGetInfo.Pointer;
+            //nativeDriver->ConnectionGetTableSchema = null;
+            //nativeDriver->ConnectionGetTableTypes = null;
+            nativeDriver->ConnectionInit = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcDatabase*, 
NativeAdbcError*, AdbcStatusCode>)connectionInit.Pointer;
+            nativeDriver->ConnectionNew = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcError*, 
AdbcStatusCode>)stub.newConnection.Pointer;
+            nativeDriver->ConnectionSetOption = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, byte*, byte*, NativeAdbcError*, 
AdbcStatusCode>)connectionSetOption.Pointer;
+            //nativeDriver->ConnectionReadPartition = null;
+            nativeDriver->ConnectionRelease = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcError*, 
AdbcStatusCode>)connectionRelease.Pointer;
+            //nativeDriver->ConnectionRollback = null;
+
+           // nativeDriver->StatementBind = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, CArrowArray*, CArrowSchema*, 
NativeAdbcError*, AdbcStatusCode>)
+            nativeDriver->StatementNew = (delegate* 
unmanaged[Stdcall]<NativeAdbcConnection*, NativeAdbcStatement*, 
NativeAdbcError*, AdbcStatusCode>)statementNew.Pointer;
+            nativeDriver->StatementSetSqlQuery = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, byte*, NativeAdbcError *, 
AdbcStatusCode >)statementSetSqlQuery.Pointer;
+            nativeDriver->StatementExecuteQuery = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, CArrowArrayStream*, long*, 
NativeAdbcError*, AdbcStatusCode>)statementExecuteQuery.Pointer;
+            nativeDriver->StatementPrepare = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, NativeAdbcError*, 
AdbcStatusCode>)statementRelease.Pointer;
+            nativeDriver->StatementRelease = (delegate* 
unmanaged[Stdcall]<NativeAdbcStatement*, NativeAdbcError*, 
AdbcStatusCode>)statementRelease.Pointer;
+            
+            return 0;
+        }
+
+        private unsafe static void ReleaseError(NativeAdbcError* error)
+        {
+            if (error != null && ((IntPtr)error->message) != IntPtr.Zero)
+            {
+                Marshal.FreeCoTaskMem((IntPtr)error->message);
+            }
+        }
+
+        private unsafe static AdbcStatusCode SetError(NativeAdbcError* error, 
Exception exception)
+        {
+            ReleaseError(error);
+
+            #if NETSTANDARD
+                error->message = 
(char*)MarshalExtensions.StringToCoTaskMemUTF8(exception.Message);
+            #else
+                error->message = 
(char*)Marshal.StringToCoTaskMemUTF8(exception.Message);
+            #endif
+
+            error->sqlstate0 = (char)0;
+            error->sqlstate1 = (char)0;
+            error->sqlstate2 = (char)0;
+            error->sqlstate3 = (char)0;
+            error->sqlstate4 = (char)0;
+            error->vendor_code = 0;
+            error->vendor_code = 0;
+            error->release = (delegate* unmanaged[Stdcall]<NativeAdbcError*, 
void>)releaseError.Pointer;
+            
+            return AdbcStatusCode.UnknownError;
+        }
+
+        private sealed class PinnedArray : IDisposable
+        {
+            IArrowArray _array;
+            MemoryHandle[] pinnedHandles;
+
+            public PinnedArray(IArrowArray array)
+            {
+                _array = array;
+                pinnedHandles = new MemoryHandle[GetHandleCount(array.Data)];
+                int index = 0;
+                PinBuffers(array.Data, pinnedHandles, ref index);
+                Debug.Assert(index == pinnedHandles.Length);
+            }
+
+            public void Dispose()
+            {
+                if (_array != null)
+                {
+                    _array.Dispose();
+                    foreach (MemoryHandle handle in pinnedHandles)
+                    {
+                        handle.Dispose();
+                    }
+                    _array = null;
+                }
+            }
+
+            static int GetHandleCount(ArrayData data)
+            {
+                int handleCount = data.Buffers.Length;
+                foreach (ArrayData child in data.Children)
+                {
+                    handleCount += GetHandleCount(child);
+                }
+                if (data.Dictionary != null)
+                {
+                    handleCount += GetHandleCount(data.Dictionary);
+                }
+                return handleCount;
+            }
+
+            static void PinBuffers(ArrayData data, MemoryHandle[] handles, ref 
int index)
+            {
+                foreach (ArrowBuffer buffer in data.Buffers)
+                {
+                    handles[index++] = buffer.Memory.Pin();
+                }
+                foreach (ArrayData child in data.Children)
+                {
+                    PinBuffers(child, handles, ref index);
+                }
+                if (data.Dictionary != null)
+                {
+                    PinBuffers(data.Dictionary, handles, ref index);
+                }
+            }
+        }
+
+        private static IntPtr FromDisposable(IDisposable d)
+        {
+            GCHandle gch = GCHandle.Alloc(d);
+            return GCHandle.ToIntPtr(gch);
+        }
+
+        private static void Dispose(ref IntPtr p)
+        {
+            GCHandle gch = GCHandle.FromIntPtr(p);
+            ((IDisposable)gch.Target).Dispose();
+            gch.Free();
+            p = IntPtr.Zero;
+        }
+
+        private unsafe static AdbcStatusCode ReleaseDriver(NativeAdbcDriver* 
nativeDriver, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeDriver->private_data);
+            DriverStub stub = (DriverStub)gch.Target;
+            stub.Dispose();
+            gch.Free();
+            nativeDriver->private_data = null;
+            return 0;
+        }
+
+        private unsafe static AdbcStatusCode InitDatabase(NativeAdbcDatabase* 
nativeDatabase, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeDatabase->private_data);
+            DatabaseStub stub = (DatabaseStub)gch.Target;
+            return stub.Init(ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
ReleaseDatabase(NativeAdbcDatabase* nativeDatabase, NativeAdbcError* error)
+        {
+            if (nativeDatabase->private_data == null)
+            {
+                return AdbcStatusCode.UnknownError;
+            }
+
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeDatabase->private_data);
+            DatabaseStub stub = (DatabaseStub)gch.Target;
+            stub.Dispose();
+            gch.Free();
+            nativeDatabase->private_data = null;
+            return AdbcStatusCode.Success;
+        }
+
+        private unsafe static AdbcStatusCode 
SetConnectionOption(NativeAdbcConnection* nativeConnection, byte* name, byte* 
value, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            return stub.SetOption(name, value, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
SetDatabaseOption(NativeAdbcDatabase* nativeDatabase, byte* name, byte* value, 
NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeDatabase->private_data);
+            DatabaseStub stub = (DatabaseStub)gch.Target;
+
+            return stub.SetOption(name, value, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
InitConnection(NativeAdbcConnection* nativeConnection, NativeAdbcDatabase* 
database, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            return stub.InitConnection(ref *database, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
ReleaseConnection(NativeAdbcConnection* nativeConnection, NativeAdbcError* 
error)
+        {
+            if (nativeConnection->private_data == null)
+            {
+                return AdbcStatusCode.UnknownError;
+            }
+
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            stub.Dispose();
+            gch.Free();
+            nativeConnection->private_data = null;
+            return AdbcStatusCode.Success;
+        }
+
+        private unsafe static AdbcStatusCode 
GetConnectionInfo(NativeAdbcConnection* nativeConnection, uint* info_codes, int 
info_codes_length, CArrowArrayStream* stream, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            return stub.GetConnectionInfo(ref *nativeConnection, *info_codes, 
info_codes_length, ref *stream, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
SetStatementSqlQuery(NativeAdbcStatement* nativeStatement, byte* text, 
NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeStatement->private_data);
+            AdbcStatement stub = (AdbcStatement)gch.Target;
+
+            #if NETSTANDARD
+                stub.SqlQuery = 
MarshalExtensions.PtrToStringUTF8((IntPtr)text);
+            #else
+                stub.SqlQuery = Marshal.PtrToStringUTF8((IntPtr)text);
+            #endif
+
+            return AdbcStatusCode.Success;
+        }
+
+        private unsafe static AdbcStatusCode 
ExecuteStatementQuery(NativeAdbcStatement* nativeStatement, CArrowArrayStream* 
stream, long* rows, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeStatement->private_data);
+            AdbcStatement stub = (AdbcStatement)gch.Target;
+            var result = stub.ExecuteQuery();
+            if (rows != null)
+            {
+                *rows = result.RowCount;
+            }
+
+            GCHandle streamHandle = GCHandle.Alloc(result.Stream);
+            stream->private_data = (void*)GCHandle.ToIntPtr(streamHandle);
+
+            return 0;
+        }
+
+        private unsafe static AdbcStatusCode 
NewStatement(NativeAdbcConnection* nativeConnection, NativeAdbcStatement* 
nativeStatement, NativeAdbcError* error)
+        {
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeConnection->private_data);
+            ConnectionStub stub = (ConnectionStub)gch.Target;
+            return stub.NewStatement(ref *nativeStatement, ref *error);
+        }
+
+        private unsafe static AdbcStatusCode 
ReleaseStatement(NativeAdbcStatement* nativeStatement, NativeAdbcError* error)
+        {
+            if (nativeStatement->private_data == null)
+            {
+                return AdbcStatusCode.UnknownError;
+            }
+
+            GCHandle gch = 
GCHandle.FromIntPtr((IntPtr)nativeStatement->private_data);
+            AdbcStatement stub = (AdbcStatement)gch.Target;
+            stub.Dispose();
+            gch.Free();
+            nativeStatement->private_data = null;
+            return AdbcStatusCode.Success;
+        }
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcDatabase
+    {
+        public void* private_data;
+        public NativeAdbcDriver* private_driver;
+
+        public static NativeAdbcDatabase* Create()
+        {
+            var ptr = 
(NativeAdbcDatabase*)Marshal.AllocHGlobal(sizeof(NativeAdbcDatabase));
+
+            ptr->private_data = null;
+            ptr->private_driver = null;
+            
+            return ptr;
+        }
+
+        /// <summary>
+        /// Free a pointer that was allocated in <see cref="Create"/>.
+        /// </summary>
+        /// <remarks>
+        /// Do not call this on a pointer that was allocated elsewhere.
+        /// </remarks>
+        public static void Free(NativeAdbcDatabase* database)
+        {
+            Marshal.FreeHGlobal((IntPtr)database);
+        }
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcConnection
+    {
+        public void* private_data;
+        public NativeAdbcDriver* private_driver;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcStatement
+    {
+        public void* private_data;
+        public NativeAdbcDriver* private_driver;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcPartitions
+    {
+        /// <summary>
+        /// The number of partitions.
+        /// </summary>
+        public int num_partitions;
+
+        /// <summary>
+        /// The partitions of the result set, where each entry (up to
+        /// num_partitions entries) is an opaque identifier that can be
+        /// passed to AdbcConnectionReadPartition.
+        /// </summary>
+        public sbyte** partitions;
+
+        /// <summary>
+        /// The length of each corresponding entry in partitions.
+        /// </summary>
+        public int* partition_lengths;
+
+        /// <summary>
+        /// Opaque implementation-defined state.
+        /// This field is NULLPTR iff the connection is unintialized/freed.
+        /// </summary>
+        public void* private_data;
+
+        /// <summary>
+        /// Release the contained partitions.
+        ///
+        /// Unlike other structures, this is an embedded callback to make it
+        /// easier for the driver manager and driver to cooperate.
+        /// </summary>
+        public delegate* unmanaged[Stdcall]<NativeAdbcPartitions*, void> 
release; 
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public unsafe struct NativeAdbcError
+    {
+        /// <summary>
+        /// The error message.
+        /// </summary>
+        public char* message;
+
+        /// <summary>
+        /// A vendor-specific error code, if applicable.
+        /// </summary>
+        public int vendor_code;
+
+        /// <summary>
+        /// A SQLSTATE error code, if provided, as defined by the
+        ///   SQL:2003 standard.  If not set, it should be set to
+        ///   "\0\0\0\0\0".
+        ///</summary>
+        public char sqlstate0;
+        public char sqlstate1;
+        public char sqlstate2;
+        public char sqlstate3;
+        public char sqlstate4;

Review Comment:
   I haven't checked all of structs myself against the header file. It would be 
good to double-check all the types used in C# correspond to the type used in 
`adbc.h`.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to