From 4cffa7bc8bc7acff43fb07533c548ce5ce953494 Mon Sep 17 00:00:00 2001 From: Curt Hagenlocher Date: Sat, 4 May 2024 05:39:37 -0700 Subject: [PATCH] fix(csharp/src/Apache.Arrow.Adbc/C): imported errors don't return a native error or SQL state (#1815) Creates subclass of AdbcException for imported drivers which reads the native error code and SQLState from the C error structure. Closes #1813 --- .../src/Apache.Arrow.Adbc/AdbcConnection.cs | 2 -- .../C/CAdbcDriverImporter.cs | 31 +++++++++++++++---- .../Client/DuckDbClientTests.cs | 2 +- .../ImportedDuckDbTests.cs | 6 ++-- .../Drivers/Interop/Snowflake/ValueTests.cs | 6 ++-- 5 files changed, 32 insertions(+), 15 deletions(-) diff --git a/csharp/src/Apache.Arrow.Adbc/AdbcConnection.cs b/csharp/src/Apache.Arrow.Adbc/AdbcConnection.cs index 07cb586c86..1efe89e07a 100644 --- a/csharp/src/Apache.Arrow.Adbc/AdbcConnection.cs +++ b/csharp/src/Apache.Arrow.Adbc/AdbcConnection.cs @@ -17,8 +17,6 @@ using System; using System.Collections.Generic; -using System.Linq; -using Apache.Arrow.Adbc.C; using Apache.Arrow.Ipc; namespace Apache.Arrow.Adbc diff --git a/csharp/src/Apache.Arrow.Adbc/C/CAdbcDriverImporter.cs b/csharp/src/Apache.Arrow.Adbc/C/CAdbcDriverImporter.cs index b7b755405d..786c347de5 100644 --- a/csharp/src/Apache.Arrow.Adbc/C/CAdbcDriverImporter.cs +++ b/csharp/src/Apache.Arrow.Adbc/C/CAdbcDriverImporter.cs @@ -20,6 +20,7 @@ using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; +using System.Text; using System.Threading; using Apache.Arrow.Adbc.Extensions; using Apache.Arrow.C; @@ -1099,6 +1100,28 @@ private unsafe void SetSqlQuery(string sqlQuery) } } + internal class ImportedAdbcException : AdbcException + { + private readonly string? _sqlState; + private readonly int _nativeError; + + public unsafe ImportedAdbcException(ref CAdbcError error, AdbcStatusCode statusCode) + : base(MarshalExtensions.PtrToStringUTF8(error.message) ?? "Undefined error", statusCode) + { + if (error.sqlstate0 != 0) + { + fixed (CAdbcError* fixedError = &error) + { + _sqlState = Encoding.ASCII.GetString(&fixedError->sqlstate0, 5); + } + _nativeError = error.vendor_code; + } + } + + public override string? SqlState => _sqlState; + public override int NativeError => _nativeError; + } + /// /// Assists with UTF8/string marshalling /// @@ -1424,15 +1447,11 @@ public unsafe void TranslateCode(AdbcStatusCode statusCode) { if (statusCode != AdbcStatusCode.Success) { - string message = "Undefined error"; - if (_error.message != null) - { - message = MarshalExtensions.PtrToStringUTF8(_error.message)!; - } + ImportedAdbcException exception = new ImportedAdbcException(ref _error, statusCode); Dispose(); - throw new AdbcException(message); + throw exception; } } } diff --git a/csharp/test/Apache.Arrow.Adbc.Tests/Client/DuckDbClientTests.cs b/csharp/test/Apache.Arrow.Adbc.Tests/Client/DuckDbClientTests.cs index 0d276db07e..31e25f662a 100644 --- a/csharp/test/Apache.Arrow.Adbc.Tests/Client/DuckDbClientTests.cs +++ b/csharp/test/Apache.Arrow.Adbc.Tests/Client/DuckDbClientTests.cs @@ -111,7 +111,7 @@ public void SetIsolationLevelFails() using var connection = _duckDb.CreateConnection("clientisolation.db", null); connection.Open(); - Assert.Throws(() => + Assert.ThrowsAny(() => { connection.BeginTransaction(System.Data.IsolationLevel.ReadCommitted); }); diff --git a/csharp/test/Apache.Arrow.Adbc.Tests/ImportedDuckDbTests.cs b/csharp/test/Apache.Arrow.Adbc.Tests/ImportedDuckDbTests.cs index 0cd25a14c7..8816b828fc 100644 --- a/csharp/test/Apache.Arrow.Adbc.Tests/ImportedDuckDbTests.cs +++ b/csharp/test/Apache.Arrow.Adbc.Tests/ImportedDuckDbTests.cs @@ -125,7 +125,7 @@ public void ReadOnlyTest() connection.ReadOnly = true; - AdbcException exception = Assert.Throws(() => + AdbcException exception = Assert.ThrowsAny(() => { statement.SqlQuery = "INSERT INTO test VALUES (3), (5), (7);"; statement.ExecuteUpdate(); @@ -144,7 +144,7 @@ public void ReadOnlyFails() using var database = _duckDb.OpenDatabase("readonly.db"); using var connection = database.Connect(null); - Assert.Throws(() => + Assert.ThrowsAny(() => { connection.ReadOnly = true; }); @@ -156,7 +156,7 @@ public void SetIsolationLevelFails() using var database = _duckDb.OpenDatabase("isolation.db"); using var connection = database.Connect(null); - Assert.Throws(() => + Assert.ThrowsAny(() => { connection.IsolationLevel = IsolationLevel.Default; }); diff --git a/csharp/test/Drivers/Interop/Snowflake/ValueTests.cs b/csharp/test/Drivers/Interop/Snowflake/ValueTests.cs index 3a48eb50b7..1311112701 100644 --- a/csharp/test/Drivers/Interop/Snowflake/ValueTests.cs +++ b/csharp/test/Drivers/Interop/Snowflake/ValueTests.cs @@ -107,7 +107,7 @@ public void TestSmallNumberRangeOverlimit(int value) { string columnName = "SMALLNUMBER"; string table = CreateTemporaryTable(string.Format("{0} NUMBER(2,0)", columnName)); - Assert.Throws(() => ValidateInsertSelectDeleteSingleDecimalValue(table, columnName, value)); + Assert.ThrowsAny(() => ValidateInsertSelectDeleteSingleDecimalValue(table, columnName, value)); } /// @@ -138,7 +138,7 @@ public void TestLargeScaleNumberOverlimit(string value) { string columnName = "LARGESCALENUMBER"; string table = CreateTemporaryTable(string.Format("{0} NUMBER(38,37)", columnName)); - Assert.Throws(() => ValidateInsertSelectDeleteSingleDecimalValue(table, columnName, SqlDecimal.Parse(value))); + Assert.ThrowsAny(() => ValidateInsertSelectDeleteSingleDecimalValue(table, columnName, SqlDecimal.Parse(value))); } /// @@ -166,7 +166,7 @@ public void TestSmallScaleNumberOverlimit(string value) { string columnName = "SMALLSCALENUMBER"; string table = CreateTemporaryTable(string.Format("{0} NUMBER(38,2)", columnName)); - Assert.Throws(() => ValidateInsertSelectDeleteSingleDecimalValue(table, columnName, SqlDecimal.Parse(value))); + Assert.ThrowsAny(() => ValidateInsertSelectDeleteSingleDecimalValue(table, columnName, SqlDecimal.Parse(value))); }