Giorgi / DuckDB.NET

Bindings and ADO.NET Provider for DuckDB
https://duckdb.net
MIT License
338 stars 61 forks source link

System.AccessViolationException when using Appender with arrays that contain null values #199

Closed brendonparker closed 2 weeks ago

brendonparker commented 2 weeks ago

I'm so excited you have this for .NET :)

Title is self-explanatory, I think. Here is a minimal console app to reproduce:

using DuckDB.NET.Data;

using var conn = new DuckDBConnection("Data Source=file.db");
await conn.OpenAsync();

using var cmd = conn.CreateCommand();
cmd.CommandText = """
    CREATE OR REPLACE TABLE sample (
        id BIGINT PRIMARY KEY,
        name VARCHAR,
        sales DOUBLE[]
    )
    """;
await cmd.ExecuteNonQueryAsync();

using var appender = conn.CreateAppender("sample");
var row = appender.CreateRow();
row.AppendValue(123);
row.AppendValue("This is a sample");
// This line throws an exception
row.AppendValue<double?>([123, 456, null, 789]);
row.EndRow();

appender.Close();

This is the exception/stacktrace:

Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
   at System.Runtime.CompilerServices.CastHelpers.ChkCastAny(Void*, System.Object)
   at DuckDB.NET.Data.Internal.Writer.ListVectorDataWriter.AppendCollection(System.Collections.ICollection, Int32)
   at DuckDB.NET.Data.Internal.Writer.VectorDataWriterBase.AppendValue[[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.__Canon, Int32)
   at DuckDB.NET.Data.DuckDBAppenderRow.AppendValueInternal[[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.__Canon)
   at DuckDB.NET.Data.DuckDBAppenderRow.AppendValue[[System.Nullable`1[[System.Double, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.Collections.Generic.IEnumerable`1<System.Nullable`1<Double>>)
   at Program+<<Main>$>d__0.MoveNext()
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.__Canon ByRef)
   at Program.<Main>$(System.String[])
   at Program.<Main>(System.String[])

Using the command line, it accepts null:

   v1.0.0 1f98600c2c
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
D CREATE OR REPLACE TABLE sample (
┬╖     id BIGINT PRIMARY KEY,
┬╖     name VARCHAR,
┬╖     sales DOUBLE[]
┬╖ );
D INSERT INTO sample (id, name, sales) VALUES (123, 'Hello', [123, 456, NULL, 789]);
D SELECT * FROM sample;
┌───────┬─────────┬─────────────────────────────┐
│  id   │  name   │            sales            │
│ int64 │ varchar │          double[]           │
├───────┼─────────┼─────────────────────────────┤
│   123 │ Hello   │ [123.0, 456.0, NULL, 789.0] │
└───────┴─────────┴─────────────────────────────┘

Works fine for non-nullable...

row.AppendValue<double>([123, 456, 789]);