Skip to content

dotnet binding: memory leak, size_t/int mismatch, broken Message marshalling, layout bugs #5847

@prql-bot

Description

@prql-bot

During the nightly survey of prqlc/bindings/dotnet/, I cross-checked the C# binding against the C header at prqlc/bindings/prqlc-c/prqlc.h and found several FFI/marshalling defects that look like they would prevent the binding from working correctly on 64-bit platforms or on any code path that returns more than one diagnostic message. Filing as an issue rather than a PR because the fix likely needs coordinated changes across several files plus a runnable .NET test environment to verify.

Defects found

  1. Memory leak — result_destroy is never called. prqlc.h exports result_destroy(CompileResult *res) and the CompileResult doc comment says it "must be freed by result_destroy". PrqlCompiler.cs never invokes it on the path through Compile / PrqlToPl / PlToRq / RqToSql — every call leaks the native output string and the messages array. Fix: add the DllImport and call it in a finally (or implement IDisposable on Result).

  2. size_t mapped as int (4 bytes) instead of nuint/UIntPtr (8 bytes on 64-bit). Affects:

    On 64-bit platforms this corrupts the marshalled struct layout. MessagesLen in particular ends up reading the high or low half of a usize.

  3. All messages collapse to the first one. Result.cs:20-23 calls Marshal.PtrToStructure<Message>(result.Messages) inside the loop without advancing the pointer, so every entry is a copy of the first message. Needs IntPtr.Add(result.Messages, i * Marshal.SizeOf<Message>()).

  4. Message struct layout does not match the C struct. In prqlc.h the optional fields (code, hint, display) are const char *const * (pointer-to-pointer, may be null), and span / location are pointers (const Span *, const SourceLocation *). The C# Message struct marshals them as string and as inline by-value structs, which will read wrong memory and likely crash on any error path that populates them. Fields need to be IntPtr and dereferenced/null-checked manually.

  5. Wrong exception type for null options. PrqlCompiler.cs:48 and :119 throw ArgumentException for null options, but the XML doc on lines 37 and 107 advertises ArgumentNullException. Either the doc or the throw is wrong.

  6. Wrong paramref in RqToSql doc comments. PrqlCompiler.cs:106,108 reference <paramref name="prqlQuery"/> but the parameter is rqJson.

Context

The dotnet binding has had no commits in the last 6 months, and #4251 / #3791 raise the broader question of moving unmaintained bindings to separate repositories. If the binding is being kept in-tree, the four critical defects above (1–4) are blockers for any real use; #5 and #6 are cosmetic. If the binding is being deprioritized, this issue can serve as the "current known state" record before any move-out decision.

I have not built or run the .NET project to confirm a runtime crash — these findings are from reading the C# source against the C header.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions