diff --git a/DuckDB.NET.Bindings/DuckDBNativeObjects.cs b/DuckDB.NET.Bindings/DuckDBNativeObjects.cs index 9c9a62a3..08034a49 100644 --- a/DuckDB.NET.Bindings/DuckDBNativeObjects.cs +++ b/DuckDB.NET.Bindings/DuckDBNativeObjects.cs @@ -55,7 +55,7 @@ public enum DuckDBType } [StructLayout(LayoutKind.Sequential)] - public class DuckDBResult : IDisposable + public struct DuckDBResult : IDisposable { [Obsolete] private long ColumnCount; @@ -76,7 +76,7 @@ public class DuckDBResult : IDisposable public void Dispose() { - NativeMethods.Query.DuckDBDestroyResult(this); + NativeMethods.Query.DuckDBDestroyResult(ref this); } } @@ -232,4 +232,26 @@ private static DuckDBInterval FromTimeSpan(TimeSpan timeSpan) , Convert.ToUInt64(timeSpan.Ticks / 10 - new TimeSpan(timeSpan.Days, 0, 0, 0).Ticks / 10) ); } + + [StructLayout(LayoutKind.Explicit)] + public struct DuckDBString + { + [FieldOffset(0)] public DuckDBStringPointer Pointer; + [FieldOffset(0)] public DuckDBStringInlined Inlined; + } + + [StructLayout(LayoutKind.Explicit)] + public struct DuckDBStringPointer + { + [FieldOffset(0)] public uint length; + [FieldOffset(4)] public IntPtr prefix; + [FieldOffset(8)] public IntPtr ptr; + } + + [StructLayout(LayoutKind.Explicit)] + public struct DuckDBStringInlined + { + [FieldOffset(0)] public uint length; + [FieldOffset(4)] public IntPtr inlined; + } } diff --git a/DuckDB.NET.Bindings/DuckDBWrapperObjects.cs b/DuckDB.NET.Bindings/DuckDBWrapperObjects.cs index 21e35540..ff7baad7 100644 --- a/DuckDB.NET.Bindings/DuckDBWrapperObjects.cs +++ b/DuckDB.NET.Bindings/DuckDBWrapperObjects.cs @@ -1,7 +1,4 @@ -using System; - -using System.Runtime.InteropServices; -using Microsoft.Win32.SafeHandles; +using Microsoft.Win32.SafeHandles; namespace DuckDB.NET { @@ -83,4 +80,30 @@ protected override bool ReleaseHandle() return true; } } + + public class DuckDBLogicalType : SafeHandleZeroOrMinusOneIsInvalid + { + public DuckDBLogicalType() : base(true) + { + } + + protected override bool ReleaseHandle() + { + NativeMethods.LogicalType.DuckDBDestroyLogicalType(out handle); + return true; + } + } + + public class DuckDBDataChunk : SafeHandleZeroOrMinusOneIsInvalid + { + public DuckDBDataChunk() : base(true) + { + } + + protected override bool ReleaseHandle() + { + NativeMethods.DataChunks.DuckDBDestroyDataChunk(out handle); + return true; + } + } } diff --git a/DuckDB.NET.Bindings/NativeMethods.cs b/DuckDB.NET.Bindings/NativeMethods.cs deleted file mode 100644 index 7a7440de..00000000 --- a/DuckDB.NET.Bindings/NativeMethods.cs +++ /dev/null @@ -1,393 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace DuckDB.NET -{ - public class NativeMethods - { - private const string DuckDbLibrary = "duckdb"; - - //Grouped according to https://duckdb.org/docs/api/c/overview - - public static class Startup - { - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_open")] - public static extern DuckDBState DuckDBOpen(string path, out DuckDBDatabase database); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_open_ext")] - public static extern DuckDBState DuckDBOpen(string path, out DuckDBDatabase database, DuckDBConfig config, out IntPtr error); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_close")] - public static extern void DuckDBClose(out IntPtr database); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_connect")] - public static extern DuckDBState DuckDBConnect(DuckDBDatabase database, out DuckDBNativeConnection connection); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_disconnect")] - public static extern void DuckDBDisconnect(out IntPtr connection); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_library_version")] - public static extern IntPtr DuckDBLibraryVersion(); - } - - public static class Configure - { - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_config")] - public static extern DuckDBState DuckDBCreateConfig(out DuckDBConfig config); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_config_count")] - public static extern int DuckDBConfigCount(); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_config_flag")] - public static extern DuckDBState DuckDBGetConfigFlag(int index, out IntPtr name, out IntPtr description); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_set_config")] - public static extern DuckDBState DuckDBSetConfig(DuckDBConfig config, string name, string option); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_destroy_config")] - public static extern void DuckDBDestroyConfig(out IntPtr config); - } - - public static class Query - { - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_query")] - public static extern DuckDBState DuckDBQuery(DuckDBNativeConnection connection, string query, [In, Out] DuckDBResult result); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_query")] - public static extern DuckDBState DuckDBQuery(DuckDBNativeConnection connection, SafeUnmanagedMemoryHandle query, [In, Out] DuckDBResult result); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_destroy_result")] - public static extern void DuckDBDestroyResult([In, Out] DuckDBResult result); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_column_name")] - public static extern IntPtr DuckDBColumnName([In, Out] DuckDBResult result, long col); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_column_type")] - public static extern DuckDBType DuckDBColumnType([In, Out] DuckDBResult result, long col); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_column_count")] - public static extern long DuckDBColumnCount([In, Out] DuckDBResult result); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_row_count")] - public static extern long DuckDBRowCount([In, Out] DuckDBResult result); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_rows_changed")] - public static extern long DuckDBRowsChanged([In, Out] DuckDBResult result); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_column_data")] - public static extern IntPtr DuckDBColumnData([In, Out] DuckDBResult result, long col); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_nullmask_data")] - public static extern IntPtr DuckDBNullmaskData([In, Out] DuckDBResult result, long col); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_result_error")] - public static extern IntPtr DuckDBResultError([In, Out] DuckDBResult result); - } - - public static class Types - { - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_boolean")] - public static extern bool DuckDBValueBoolean([In, Out] DuckDBResult result, long col, long row); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_int8")] - public static extern sbyte DuckDBValueInt8([In, Out] DuckDBResult result, long col, long row); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_int16")] - public static extern short DuckDBValueInt16([In, Out] DuckDBResult result, long col, long row); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_int32")] - public static extern int DuckDBValueInt32([In, Out] DuckDBResult result, long col, long row); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_int64")] - public static extern long DuckDBValueInt64([In, Out] DuckDBResult result, long col, long row); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_decimal")] - public static extern DuckDBDecimal DuckDBValueDecimal([In, Out] DuckDBResult result, long col, long row); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_uint8")] - public static extern byte DuckDBValueUInt8([In, Out] DuckDBResult result, long col, long row); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_uint16")] - public static extern ushort DuckDBValueUInt16([In, Out] DuckDBResult result, long col, long row); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_uint32")] - public static extern uint DuckDBValueUInt32([In, Out] DuckDBResult result, long col, long row); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_uint64")] - public static extern ulong DuckDBValueUInt64([In, Out] DuckDBResult result, long col, long row); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_float")] - public static extern float DuckDBValueFloat([In, Out] DuckDBResult result, long col, long row); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_double")] - public static extern double DuckDBValueDouble([In, Out] DuckDBResult result, long col, long row); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_interval")] - public static extern DuckDBInterval DuckDBValueInterval([In, Out] DuckDBResult result, long col, long row); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_varchar")] - public static extern IntPtr DuckDBValueVarchar([In, Out] DuckDBResult result, long col, long row); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_blob")] - public static extern DuckDBBlob DuckDBValueBlob([In, Out] DuckDBResult result, long col, long row); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_date")] - public static extern DuckDBDate DuckDBValueDate([In, Out] DuckDBResult result, long col, long row); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_time")] - public static extern DuckDBTime DuckDBValueTime([In, Out] DuckDBResult result, long col, long row); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_timestamp")] - public static extern DuckDBTimestampStruct DuckDBValueTimestamp([In, Out] DuckDBResult result, long col, long row); - } - - public static class PreparedStatements - { - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_prepare")] - public static extern DuckDBState DuckDBPrepare(DuckDBNativeConnection connection, string query, out DuckDBPreparedStatement preparedStatement); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_prepare")] - public static extern DuckDBState DuckDBPrepare(DuckDBNativeConnection connection, SafeUnmanagedMemoryHandle query, out DuckDBPreparedStatement preparedStatement); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_destroy_prepare")] - public static extern void DuckDBDestroyPrepare(out IntPtr preparedStatement); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_prepare_error")] - public static extern IntPtr DuckDBPrepareError(DuckDBPreparedStatement preparedStatement); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_nparams")] - public static extern long DuckDBParams(DuckDBPreparedStatement preparedStatement); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_boolean")] - public static extern DuckDBState DuckDBBindBoolean(DuckDBPreparedStatement preparedStatement, long index, bool val); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_int8")] - public static extern DuckDBState DuckDBBindInt8(DuckDBPreparedStatement preparedStatement, long index, sbyte val); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_int16")] - public static extern DuckDBState DuckDBBindInt16(DuckDBPreparedStatement preparedStatement, long index, short val); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_int32")] - public static extern DuckDBState DuckDBBindInt32(DuckDBPreparedStatement preparedStatement, long index, int val); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_int64")] - public static extern DuckDBState DuckDBBindInt64(DuckDBPreparedStatement preparedStatement, long index, long val); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_hugeint")] - public static extern DuckDBState DuckDBBindHugeInt(DuckDBPreparedStatement preparedStatement, long index, DuckDBHugeInt val); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_uint8")] - public static extern DuckDBState DuckDBBindUInt8(DuckDBPreparedStatement preparedStatement, long index, byte val); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_uint16")] - public static extern DuckDBState DuckDBBindUInt16(DuckDBPreparedStatement preparedStatement, long index, ushort val); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_uint32")] - public static extern DuckDBState DuckDBBindUInt32(DuckDBPreparedStatement preparedStatement, long index, uint val); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_uint64")] - public static extern DuckDBState DuckDBBindUInt64(DuckDBPreparedStatement preparedStatement, long index, ulong val); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_float")] - public static extern DuckDBState DuckDBBindFloat(DuckDBPreparedStatement preparedStatement, long index, float val); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_double")] - public static extern DuckDBState DuckDBBindDouble(DuckDBPreparedStatement preparedStatement, long index, double val); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_varchar")] - public static extern DuckDBState DuckDBBindVarchar(DuckDBPreparedStatement preparedStatement, long index, string val); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_varchar")] - public static extern DuckDBState DuckDBBindVarchar(DuckDBPreparedStatement preparedStatement, long index, SafeUnmanagedMemoryHandle val); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_blob")] - public static extern DuckDBState DuckDBBindBlob(DuckDBPreparedStatement preparedStatement, long index, [In] byte[] data, long length); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_null")] - public static extern DuckDBState DuckDBBindNull(DuckDBPreparedStatement preparedStatement, long index); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_date")] - public static extern DuckDBState DuckDBBindDate(DuckDBPreparedStatement preparedStatement, long index, DuckDBDate val); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_time")] - public static extern DuckDBState DuckDBBindTime(DuckDBPreparedStatement preparedStatement, long index, DuckDBTime val); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_timestamp")] - public static extern DuckDBState DuckDBBindTimestamp(DuckDBPreparedStatement preparedStatement, long index, DuckDBTimestampStruct val); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_execute_prepared")] - public static extern DuckDBState DuckDBExecutePrepared(DuckDBPreparedStatement preparedStatement, [In, Out] DuckDBResult result); - } - - public static class ExtractStatements - { - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_extract_statements")] - public static extern int DuckDBExtractStatements(DuckDBNativeConnection connection, string query, out DuckDBExtractedStatements extractedStatements); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_prepare_extracted_statement")] - public static extern DuckDBState DuckDBPrepareExtractedStatement(DuckDBNativeConnection connection, DuckDBExtractedStatements extractedStatements, long index, out DuckDBPreparedStatement preparedStatement); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_extract_statements_error")] - public static extern IntPtr DuckDBExtractStatementsError(DuckDBExtractedStatements extractedStatements); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_destroy_extracted")] - public static extern void DuckDBDestroyExtracted(out IntPtr extractedStatements); - } - - public static class Appender - { - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_appender_create")] - public static extern DuckDBState DuckDBAppenderCreate(DuckDBNativeConnection connection, string schema, string table, out DuckDBAppender appender); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_appender_error")] - public static extern IntPtr DuckDBAppenderError(DuckDBAppender appender); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_appender_flush")] - public static extern DuckDBState DuckDBAppenderFlush(DuckDBAppender appender); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_appender_end_row")] - public static extern DuckDBState DuckDBAppenderEndRow(DuckDBAppender appender); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_appender_close")] - public static extern DuckDBState DuckDBAppenderClose(DuckDBAppender appender); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_appender_destroy")] - public static extern DuckDBState DuckDBDestroyAppender(out IntPtr appender); - -#if NET5_0_OR_GREATER - [SuppressGCTransition] -#endif - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_bool")] - public static extern DuckDBState DuckDBAppendBool(DuckDBAppender appender, bool val); - -#if NET5_0_OR_GREATER - [SuppressGCTransition] -#endif - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_int8")] - public static extern DuckDBState DuckDBAppendInt8(DuckDBAppender appender, sbyte val); - -#if NET5_0_OR_GREATER - [SuppressGCTransition] -#endif - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_int16")] - public static extern DuckDBState DuckDBAppendInt16(DuckDBAppender appender, short val); - -#if NET5_0_OR_GREATER - [SuppressGCTransition] -#endif - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_int32")] - public static extern DuckDBState DuckDBAppendInt32(DuckDBAppender appender, int val); - -#if NET5_0_OR_GREATER - [SuppressGCTransition] -#endif - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_int64")] - public static extern DuckDBState DuckDBAppendInt64(DuckDBAppender appender, long val); - -#if NET5_0_OR_GREATER - [SuppressGCTransition] -#endif - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_hugeint")] - public static extern DuckDBState DuckDBAppendHugeInt(DuckDBAppender appender, DuckDBHugeInt val); - -#if NET5_0_OR_GREATER - [SuppressGCTransition] -#endif - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_uint8")] - public static extern DuckDBState DuckDBAppendUInt8(DuckDBAppender appender, byte val); - -#if NET5_0_OR_GREATER - [SuppressGCTransition] -#endif - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_uint16")] - public static extern DuckDBState DuckDBAppendUInt16(DuckDBAppender appender, ushort val); - -#if NET5_0_OR_GREATER - [SuppressGCTransition] -#endif - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_uint32")] - public static extern DuckDBState DuckDBAppendUInt32(DuckDBAppender appender, uint val); - -#if NET5_0_OR_GREATER - [SuppressGCTransition] -#endif - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_uint64")] - public static extern DuckDBState DuckDBAppendUInt64(DuckDBAppender appender, ulong val); - -#if NET5_0_OR_GREATER - [SuppressGCTransition] -#endif - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_float")] - public static extern DuckDBState DuckDBAppendFloat(DuckDBAppender appender, float val); - -#if NET5_0_OR_GREATER - [SuppressGCTransition] -#endif - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_double")] - public static extern DuckDBState DuckDBAppendDouble(DuckDBAppender appender, double val); - -#if NET5_0_OR_GREATER - [SuppressGCTransition] -#endif - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_date")] - public static extern DuckDBState DuckDBAppendDate(DuckDBAppender appender, DuckDBDate val); - -#if NET5_0_OR_GREATER - [SuppressGCTransition] -#endif - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_time")] - public static extern DuckDBState DuckDBAppendTime(DuckDBAppender appender, DuckDBTime val); - -#if NET5_0_OR_GREATER - [SuppressGCTransition] -#endif - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_timestamp")] - public static extern DuckDBState DuckDBAppendTimestamp(DuckDBAppender appender, DuckDBTimestampStruct val); - -#if NET5_0_OR_GREATER - [SuppressGCTransition] -#endif - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_interval")] - public static extern DuckDBState DuckDBAppendInterval(DuckDBAppender appender, DuckDBInterval val); - -#if NET5_0_OR_GREATER - [SuppressGCTransition] -#endif - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_varchar")] - public static extern DuckDBState DuckDBAppendVarchar(DuckDBAppender appender, SafeUnmanagedMemoryHandle val); - -#if NET5_0_OR_GREATER - [SuppressGCTransition] -#endif - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_null")] - public static extern DuckDBState DuckDBAppendNull(DuckDBAppender appender); - } - - public static class Helpers - { - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_free")] - public static extern void DuckDBFree(IntPtr ptr); - } - - public static class DateTime - { - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_from_date")] - public static extern DuckDBDateOnly DuckDBFromDate(DuckDBDate date); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_to_date")] - public static extern DuckDBDate DuckDBToDate(DuckDBDateOnly dateStruct); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_from_time")] - public static extern DuckDBTimeOnly DuckDBFromTime(DuckDBTime date); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_to_time")] - public static extern DuckDBTime DuckDBToTime(DuckDBTimeOnly dateStruct); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_from_timestamp")] - public static extern DuckDBTimestamp DuckDBFromTimestamp(DuckDBTimestampStruct date); - - [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_to_timestamp")] - public static extern DuckDBTimestampStruct DuckDBToTimestamp(DuckDBTimestamp dateStruct); - } - } -} diff --git a/DuckDB.NET.Bindings/NativeMethods/NativeMethods.All.cs b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.All.cs new file mode 100644 index 00000000..6c3d84fe --- /dev/null +++ b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.All.cs @@ -0,0 +1,8 @@ +namespace DuckDB.NET; + +public static partial class NativeMethods +{ + private const string DuckDbLibrary = "duckdb"; + + //Grouped according to https://duckdb.org/docs/archive/0.8.1/api/c/api +} \ No newline at end of file diff --git a/DuckDB.NET.Bindings/NativeMethods/NativeMethods.Appender.cs b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.Appender.cs new file mode 100644 index 00000000..36647890 --- /dev/null +++ b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.Appender.cs @@ -0,0 +1,136 @@ +using System; +using System.Runtime.InteropServices; + +namespace DuckDB.NET; + +public partial class NativeMethods +{ + public static class Appender + { + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_appender_create")] + public static extern DuckDBState DuckDBAppenderCreate(DuckDBNativeConnection connection, string schema, string table, out DuckDBAppender appender); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_appender_error")] + public static extern IntPtr DuckDBAppenderError(DuckDBAppender appender); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_appender_flush")] + public static extern DuckDBState DuckDBAppenderFlush(DuckDBAppender appender); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_appender_end_row")] + public static extern DuckDBState DuckDBAppenderEndRow(DuckDBAppender appender); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_appender_close")] + public static extern DuckDBState DuckDBAppenderClose(DuckDBAppender appender); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_appender_destroy")] + public static extern DuckDBState DuckDBDestroyAppender(out IntPtr appender); + +#if NET5_0_OR_GREATER + [SuppressGCTransition] +#endif + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_bool")] + public static extern DuckDBState DuckDBAppendBool(DuckDBAppender appender, bool val); + +#if NET5_0_OR_GREATER + [SuppressGCTransition] +#endif + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_int8")] + public static extern DuckDBState DuckDBAppendInt8(DuckDBAppender appender, sbyte val); + +#if NET5_0_OR_GREATER + [SuppressGCTransition] +#endif + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_int16")] + public static extern DuckDBState DuckDBAppendInt16(DuckDBAppender appender, short val); + +#if NET5_0_OR_GREATER + [SuppressGCTransition] +#endif + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_int32")] + public static extern DuckDBState DuckDBAppendInt32(DuckDBAppender appender, int val); + +#if NET5_0_OR_GREATER + [SuppressGCTransition] +#endif + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_int64")] + public static extern DuckDBState DuckDBAppendInt64(DuckDBAppender appender, long val); + +#if NET5_0_OR_GREATER + [SuppressGCTransition] +#endif + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_hugeint")] + public static extern DuckDBState DuckDBAppendHugeInt(DuckDBAppender appender, DuckDBHugeInt val); + +#if NET5_0_OR_GREATER + [SuppressGCTransition] +#endif + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_uint8")] + public static extern DuckDBState DuckDBAppendUInt8(DuckDBAppender appender, byte val); + +#if NET5_0_OR_GREATER + [SuppressGCTransition] +#endif + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_uint16")] + public static extern DuckDBState DuckDBAppendUInt16(DuckDBAppender appender, ushort val); + +#if NET5_0_OR_GREATER + [SuppressGCTransition] +#endif + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_uint32")] + public static extern DuckDBState DuckDBAppendUInt32(DuckDBAppender appender, uint val); + +#if NET5_0_OR_GREATER + [SuppressGCTransition] +#endif + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_uint64")] + public static extern DuckDBState DuckDBAppendUInt64(DuckDBAppender appender, ulong val); + +#if NET5_0_OR_GREATER + [SuppressGCTransition] +#endif + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_float")] + public static extern DuckDBState DuckDBAppendFloat(DuckDBAppender appender, float val); + +#if NET5_0_OR_GREATER + [SuppressGCTransition] +#endif + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_double")] + public static extern DuckDBState DuckDBAppendDouble(DuckDBAppender appender, double val); + +#if NET5_0_OR_GREATER + [SuppressGCTransition] +#endif + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_date")] + public static extern DuckDBState DuckDBAppendDate(DuckDBAppender appender, DuckDBDate val); + +#if NET5_0_OR_GREATER + [SuppressGCTransition] +#endif + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_time")] + public static extern DuckDBState DuckDBAppendTime(DuckDBAppender appender, DuckDBTime val); + +#if NET5_0_OR_GREATER + [SuppressGCTransition] +#endif + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_timestamp")] + public static extern DuckDBState DuckDBAppendTimestamp(DuckDBAppender appender, DuckDBTimestampStruct val); + +#if NET5_0_OR_GREATER + [SuppressGCTransition] +#endif + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_interval")] + public static extern DuckDBState DuckDBAppendInterval(DuckDBAppender appender, DuckDBInterval val); + +#if NET5_0_OR_GREATER + [SuppressGCTransition] +#endif + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_varchar")] + public static extern DuckDBState DuckDBAppendVarchar(DuckDBAppender appender, SafeUnmanagedMemoryHandle val); + +#if NET5_0_OR_GREATER + [SuppressGCTransition] +#endif + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_append_null")] + public static extern DuckDBState DuckDBAppendNull(DuckDBAppender appender); + } +} \ No newline at end of file diff --git a/DuckDB.NET.Bindings/NativeMethods/NativeMethods.Configure.cs b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.Configure.cs new file mode 100644 index 00000000..64e66af1 --- /dev/null +++ b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.Configure.cs @@ -0,0 +1,25 @@ +using System; +using System.Runtime.InteropServices; + +namespace DuckDB.NET; + +public partial class NativeMethods +{ + public static class Configure + { + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_create_config")] + public static extern DuckDBState DuckDBCreateConfig(out DuckDBConfig config); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_config_count")] + public static extern int DuckDBConfigCount(); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_get_config_flag")] + public static extern DuckDBState DuckDBGetConfigFlag(int index, out IntPtr name, out IntPtr description); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_set_config")] + public static extern DuckDBState DuckDBSetConfig(DuckDBConfig config, string name, string option); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_destroy_config")] + public static extern void DuckDBDestroyConfig(out IntPtr config); + } +} \ No newline at end of file diff --git a/DuckDB.NET.Bindings/NativeMethods/NativeMethods.DataChunks.cs b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.DataChunks.cs new file mode 100644 index 00000000..9b77d2ee --- /dev/null +++ b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.DataChunks.cs @@ -0,0 +1,40 @@ +using System; +using System.Runtime.InteropServices; + +namespace DuckDB.NET; + +public partial class NativeMethods +{ + public static class DataChunks + { + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_data_chunk_get_column_count")] + public static extern long DuckDBDataChunkGetColumnCount(IntPtr chunk); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_data_chunk_get_vector")] + public static extern IntPtr DuckDBDataChunkGetVector(DuckDBDataChunk chunk, long columnIndex); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_data_chunk_get_size")] + public static extern long DuckDBDataChunkGetSize(DuckDBDataChunk chunk); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_vector_get_column_type")] + public static extern IntPtr DuckDBVectorGetColumnType(IntPtr vector); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_vector_get_data")] + public static extern IntPtr DuckDBVectorGetData(IntPtr vector); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_vector_get_validity")] + public static extern IntPtr DuckDBVectorGetValidity(IntPtr vector); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_list_vector_get_child")] + public static extern long DuckDBListVectorGetChild(IntPtr vector); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_list_vector_get_size")] + public static extern long DuckDBListVectorGetSize(IntPtr vector); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_struct_vector_get_child")] + public static extern long DuckDBStructVectorGetChild(IntPtr vector, long index); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_destroy_data_chunk")] + public static extern void DuckDBDestroyDataChunk(out IntPtr chunk); + } +} \ No newline at end of file diff --git a/DuckDB.NET.Bindings/NativeMethods/NativeMethods.DateTime.cs b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.DateTime.cs new file mode 100644 index 00000000..ed97906b --- /dev/null +++ b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.DateTime.cs @@ -0,0 +1,27 @@ +using System.Runtime.InteropServices; + +namespace DuckDB.NET; + +public partial class NativeMethods +{ + public static class DateTime + { + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_from_date")] + public static extern DuckDBDateOnly DuckDBFromDate(DuckDBDate date); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_to_date")] + public static extern DuckDBDate DuckDBToDate(DuckDBDateOnly dateStruct); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_from_time")] + public static extern DuckDBTimeOnly DuckDBFromTime(DuckDBTime date); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_to_time")] + public static extern DuckDBTime DuckDBToTime(DuckDBTimeOnly dateStruct); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_from_timestamp")] + public static extern DuckDBTimestamp DuckDBFromTimestamp(DuckDBTimestampStruct date); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_to_timestamp")] + public static extern DuckDBTimestampStruct DuckDBToTimestamp(DuckDBTimestamp dateStruct); + } +} \ No newline at end of file diff --git a/DuckDB.NET.Bindings/NativeMethods/NativeMethods.ExtractStatements.cs b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.ExtractStatements.cs new file mode 100644 index 00000000..164ea18a --- /dev/null +++ b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.ExtractStatements.cs @@ -0,0 +1,22 @@ +using System; +using System.Runtime.InteropServices; + +namespace DuckDB.NET; + +public partial class NativeMethods +{ + public static class ExtractStatements + { + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_extract_statements")] + public static extern int DuckDBExtractStatements(DuckDBNativeConnection connection, string query, out DuckDBExtractedStatements extractedStatements); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_prepare_extracted_statement")] + public static extern DuckDBState DuckDBPrepareExtractedStatement(DuckDBNativeConnection connection, DuckDBExtractedStatements extractedStatements, long index, out DuckDBPreparedStatement preparedStatement); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_extract_statements_error")] + public static extern IntPtr DuckDBExtractStatementsError(DuckDBExtractedStatements extractedStatements); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_destroy_extracted")] + public static extern void DuckDBDestroyExtracted(out IntPtr extractedStatements); + } +} \ No newline at end of file diff --git a/DuckDB.NET.Bindings/NativeMethods/NativeMethods.Helpers.cs b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.Helpers.cs new file mode 100644 index 00000000..726943f3 --- /dev/null +++ b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.Helpers.cs @@ -0,0 +1,16 @@ +using System; +using System.Runtime.InteropServices; + +namespace DuckDB.NET; + +public partial class NativeMethods +{ + public static class Helpers + { + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_free")] + public static extern void DuckDBFree(IntPtr ptr); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_decimal_to_double")] + public static extern double DuckDBDecimalToDouble(DuckDBDecimal val); + } +} \ No newline at end of file diff --git a/DuckDB.NET.Bindings/NativeMethods/NativeMethods.LogicalType.cs b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.LogicalType.cs new file mode 100644 index 00000000..bd9fd5f4 --- /dev/null +++ b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.LogicalType.cs @@ -0,0 +1,22 @@ +using System; +using System.Runtime.InteropServices; + +namespace DuckDB.NET; + +public partial class NativeMethods +{ + public static class LogicalType + { + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_decimal_width")] + public static extern byte DuckDBDecimalWidth(DuckDBLogicalType type); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_decimal_scale")] + public static extern byte DuckDBDecimalScale(DuckDBLogicalType type); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_decimal_internal_type")] + public static extern DuckDBType DuckDBDecimalInternalType(DuckDBLogicalType type); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_destroy_logical_type")] + public static extern void DuckDBDestroyLogicalType(out IntPtr type); + } +} \ No newline at end of file diff --git a/DuckDB.NET.Bindings/NativeMethods/NativeMethods.PreparedStatements.cs b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.PreparedStatements.cs new file mode 100644 index 00000000..5ac11c10 --- /dev/null +++ b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.PreparedStatements.cs @@ -0,0 +1,85 @@ +using System; +using System.Runtime.InteropServices; + +namespace DuckDB.NET; + +public partial class NativeMethods +{ + public static class PreparedStatements + { + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_prepare")] + public static extern DuckDBState DuckDBPrepare(DuckDBNativeConnection connection, string query, out DuckDBPreparedStatement preparedStatement); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_prepare")] + public static extern DuckDBState DuckDBPrepare(DuckDBNativeConnection connection, SafeUnmanagedMemoryHandle query, out DuckDBPreparedStatement preparedStatement); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_destroy_prepare")] + public static extern void DuckDBDestroyPrepare(out IntPtr preparedStatement); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_prepare_error")] + public static extern IntPtr DuckDBPrepareError(DuckDBPreparedStatement preparedStatement); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_nparams")] + public static extern long DuckDBParams(DuckDBPreparedStatement preparedStatement); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_boolean")] + public static extern DuckDBState DuckDBBindBoolean(DuckDBPreparedStatement preparedStatement, long index, bool val); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_int8")] + public static extern DuckDBState DuckDBBindInt8(DuckDBPreparedStatement preparedStatement, long index, sbyte val); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_int16")] + public static extern DuckDBState DuckDBBindInt16(DuckDBPreparedStatement preparedStatement, long index, short val); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_int32")] + public static extern DuckDBState DuckDBBindInt32(DuckDBPreparedStatement preparedStatement, long index, int val); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_int64")] + public static extern DuckDBState DuckDBBindInt64(DuckDBPreparedStatement preparedStatement, long index, long val); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_hugeint")] + public static extern DuckDBState DuckDBBindHugeInt(DuckDBPreparedStatement preparedStatement, long index, DuckDBHugeInt val); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_uint8")] + public static extern DuckDBState DuckDBBindUInt8(DuckDBPreparedStatement preparedStatement, long index, byte val); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_uint16")] + public static extern DuckDBState DuckDBBindUInt16(DuckDBPreparedStatement preparedStatement, long index, ushort val); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_uint32")] + public static extern DuckDBState DuckDBBindUInt32(DuckDBPreparedStatement preparedStatement, long index, uint val); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_uint64")] + public static extern DuckDBState DuckDBBindUInt64(DuckDBPreparedStatement preparedStatement, long index, ulong val); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_float")] + public static extern DuckDBState DuckDBBindFloat(DuckDBPreparedStatement preparedStatement, long index, float val); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_double")] + public static extern DuckDBState DuckDBBindDouble(DuckDBPreparedStatement preparedStatement, long index, double val); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_varchar")] + public static extern DuckDBState DuckDBBindVarchar(DuckDBPreparedStatement preparedStatement, long index, string val); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_varchar")] + public static extern DuckDBState DuckDBBindVarchar(DuckDBPreparedStatement preparedStatement, long index, SafeUnmanagedMemoryHandle val); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_blob")] + public static extern DuckDBState DuckDBBindBlob(DuckDBPreparedStatement preparedStatement, long index, [In] byte[] data, long length); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_null")] + public static extern DuckDBState DuckDBBindNull(DuckDBPreparedStatement preparedStatement, long index); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_date")] + public static extern DuckDBState DuckDBBindDate(DuckDBPreparedStatement preparedStatement, long index, DuckDBDate val); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_time")] + public static extern DuckDBState DuckDBBindTime(DuckDBPreparedStatement preparedStatement, long index, DuckDBTime val); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_bind_timestamp")] + public static extern DuckDBState DuckDBBindTimestamp(DuckDBPreparedStatement preparedStatement, long index, DuckDBTimestampStruct val); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_execute_prepared")] + public static extern DuckDBState DuckDBExecutePrepared(DuckDBPreparedStatement preparedStatement, out DuckDBResult result); + } +} \ No newline at end of file diff --git a/DuckDB.NET.Bindings/NativeMethods/NativeMethods.Query.cs b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.Query.cs new file mode 100644 index 00000000..2115f32b --- /dev/null +++ b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.Query.cs @@ -0,0 +1,46 @@ +using System; +using System.Runtime.InteropServices; + +namespace DuckDB.NET; + +public partial class NativeMethods +{ + public static class Query + { + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_query")] + public static extern DuckDBState DuckDBQuery(DuckDBNativeConnection connection, string query, out DuckDBResult result); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_query")] + public static extern DuckDBState DuckDBQuery(DuckDBNativeConnection connection, SafeUnmanagedMemoryHandle query, out DuckDBResult result); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_destroy_result")] + public static extern void DuckDBDestroyResult([In, Out] ref DuckDBResult result); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_column_name")] + public static extern IntPtr DuckDBColumnName([In, Out] ref DuckDBResult result, long col); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_column_type")] + public static extern DuckDBType DuckDBColumnType([In, Out] ref DuckDBResult result, long col); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_column_logical_type")] + public static extern DuckDBLogicalType DuckDBColumnLogicalType([In, Out] ref DuckDBResult result, long col); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_column_count")] + public static extern long DuckDBColumnCount([In, Out] ref DuckDBResult result); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_row_count")] + public static extern long DuckDBRowCount([In, Out] ref DuckDBResult result); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_rows_changed")] + public static extern long DuckDBRowsChanged([In, Out] ref DuckDBResult result); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_column_data")] + public static extern IntPtr DuckDBColumnData([In, Out] ref DuckDBResult result, long col); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_nullmask_data")] + public static extern IntPtr DuckDBNullmaskData([In, Out] ref DuckDBResult result, long col); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_result_error")] + public static extern IntPtr DuckDBResultError([In, Out] ref DuckDBResult result); + } +} \ No newline at end of file diff --git a/DuckDB.NET.Bindings/NativeMethods/NativeMethods.Startup.cs b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.Startup.cs new file mode 100644 index 00000000..8546afac --- /dev/null +++ b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.Startup.cs @@ -0,0 +1,28 @@ +using System; +using System.Runtime.InteropServices; + +namespace DuckDB.NET; + +public partial class NativeMethods +{ + public static class Startup + { + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_open")] + public static extern DuckDBState DuckDBOpen(string path, out DuckDBDatabase database); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_open_ext")] + public static extern DuckDBState DuckDBOpen(string path, out DuckDBDatabase database, DuckDBConfig config, out IntPtr error); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_close")] + public static extern void DuckDBClose(out IntPtr database); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_connect")] + public static extern DuckDBState DuckDBConnect(DuckDBDatabase database, out DuckDBNativeConnection connection); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_disconnect")] + public static extern void DuckDBDisconnect(out IntPtr connection); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_library_version")] + public static extern IntPtr DuckDBLibraryVersion(); + } +} \ No newline at end of file diff --git a/DuckDB.NET.Bindings/NativeMethods/NativeMethods.Types.cs b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.Types.cs new file mode 100644 index 00000000..193e9c55 --- /dev/null +++ b/DuckDB.NET.Bindings/NativeMethods/NativeMethods.Types.cs @@ -0,0 +1,70 @@ +using System; +using System.Runtime.InteropServices; + +namespace DuckDB.NET; + +public partial class NativeMethods +{ + public static class Types + { + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_boolean")] + public static extern bool DuckDBValueBoolean([In, Out] ref DuckDBResult result, long col, long row); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_int8")] + public static extern sbyte DuckDBValueInt8([In, Out] ref DuckDBResult result, long col, long row); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_int16")] + public static extern short DuckDBValueInt16([In, Out] ref DuckDBResult result, long col, long row); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_int32")] + public static extern int DuckDBValueInt32([In, Out] ref DuckDBResult result, long col, long row); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_int64")] + public static extern long DuckDBValueInt64([In, Out] ref DuckDBResult result, long col, long row); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_decimal")] + public static extern DuckDBDecimal DuckDBValueDecimal([In, Out] ref DuckDBResult result, long col, long row); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_uint8")] + public static extern byte DuckDBValueUInt8([In, Out] ref DuckDBResult result, long col, long row); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_uint16")] + public static extern ushort DuckDBValueUInt16([In, Out] ref DuckDBResult result, long col, long row); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_uint32")] + public static extern uint DuckDBValueUInt32([In, Out] ref DuckDBResult result, long col, long row); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_uint64")] + public static extern ulong DuckDBValueUInt64([In, Out] ref DuckDBResult result, long col, long row); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_float")] + public static extern float DuckDBValueFloat([In, Out] ref DuckDBResult result, long col, long row); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_double")] + public static extern double DuckDBValueDouble([In, Out] ref DuckDBResult result, long col, long row); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_interval")] + public static extern DuckDBInterval DuckDBValueInterval([In, Out] ref DuckDBResult result, long col, long row); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_varchar")] + public static extern IntPtr DuckDBValueVarchar([In, Out] ref DuckDBResult result, long col, long row); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_blob")] + public static extern DuckDBBlob DuckDBValueBlob([In, Out] ref DuckDBResult result, long col, long row); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_date")] + public static extern DuckDBDate DuckDBValueDate([In, Out] ref DuckDBResult result, long col, long row); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_time")] + public static extern DuckDBTime DuckDBValueTime([In, Out] ref DuckDBResult result, long col, long row); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_value_timestamp")] + public static extern DuckDBTimestampStruct DuckDBValueTimestamp([In, Out] ref DuckDBResult result, long col, long row); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_result_get_chunk")] + public static extern DuckDBDataChunk DuckDBResultGetChunk([In, Out] DuckDBResult result, long chunkIndex); + + [DllImport(DuckDbLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "duckdb_result_chunk_count")] + public static extern long DuckDBResultChunkCount([In, Out] DuckDBResult result); + } +} \ No newline at end of file diff --git a/DuckDB.NET.Bindings/Utils.cs b/DuckDB.NET.Bindings/Utils.cs index 323e8099..12ddf242 100644 --- a/DuckDB.NET.Bindings/Utils.cs +++ b/DuckDB.NET.Bindings/Utils.cs @@ -13,22 +13,25 @@ public static bool IsSuccess(this DuckDBState duckDBState) return duckDBState == DuckDBState.DuckDBSuccess; } - public static string ToManagedString(this IntPtr unmanagedString, bool freeWhenCopied = true) + public static string ToManagedString(this IntPtr unmanagedString, bool freeWhenCopied = true, int? length = null) { string result; #if NET6_0_OR_GREATER - result = Marshal.PtrToStringUTF8(unmanagedString); + result = length.HasValue? Marshal.PtrToStringUTF8(unmanagedString, length.Value) : Marshal.PtrToStringUTF8(unmanagedString); #else if (unmanagedString == IntPtr.Zero) { return ""; } - var length = 0; - - while (Marshal.ReadByte(unmanagedString, length) != 0) + if (length == null) { - length++; + length = 0; + + while (Marshal.ReadByte(unmanagedString, length.Value) != 0) + { + length++; + } } if (length == 0) @@ -36,11 +39,11 @@ public static string ToManagedString(this IntPtr unmanagedString, bool freeWhenC return string.Empty; } - var byteArray = new byte[length]; + var byteArray = new byte[length.Value]; - Marshal.Copy(unmanagedString, byteArray, 0, length); + Marshal.Copy(unmanagedString, byteArray, 0, length.Value); - result = Encoding.UTF8.GetString(byteArray, 0, length); + result = Encoding.UTF8.GetString(byteArray, 0, length.Value); #endif if (freeWhenCopied) { @@ -93,4 +96,4 @@ internal static int GetMicrosecond(this TimeOnly timeOnly) } #endif } -} +} \ No newline at end of file diff --git a/DuckDB.NET.Data/DuckDBCommand.cs b/DuckDB.NET.Data/DuckDBCommand.cs index 746e07d6..2f6cd549 100644 --- a/DuckDB.NET.Data/DuckDBCommand.cs +++ b/DuckDB.NET.Data/DuckDBCommand.cs @@ -56,9 +56,10 @@ public override int ExecuteNonQuery() var count = 0; - foreach (var result in results) + for (var index = 0; index < results.Count; index++) { - count += (int)NativeMethods.Query.DuckDBRowsChanged(result); + var result = results[index]; + count += (int)NativeMethods.Query.DuckDBRowsChanged(ref result); result.Dispose(); } diff --git a/DuckDB.NET.Data/DuckDBDataReader.cs b/DuckDB.NET.Data/DuckDBDataReader.cs index 912a3fab..0725d489 100644 --- a/DuckDB.NET.Data/DuckDBDataReader.cs +++ b/DuckDB.NET.Data/DuckDBDataReader.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Data; using System.Data.Common; -using System.Globalization; using System.IO; using System.Numerics; using System.Runtime.InteropServices; @@ -12,10 +11,12 @@ namespace DuckDB.NET.Data { public class DuckDBDataReader : DbDataReader { + private const int InlineStringMaxLength = 12; private readonly DuckDbCommand command; private readonly CommandBehavior behavior; private DuckDBResult currentResult; + private DuckDBDataChunk currentChunk; private readonly List queryResults; private bool closed; @@ -25,6 +26,13 @@ public class DuckDBDataReader : DbDataReader private int fieldCount; private int recordsAffected; + private readonly Dictionary vectors = new(); + private readonly Dictionary vectorValidityMask = new(); + + private long chunkCount; + private int currentChunkIndex; + private int rowsReadFromCurrentChunk; + private long currentChunkRowCount; internal DuckDBDataReader(DuckDbCommand command, List queryResults, CommandBehavior behavior) { @@ -39,24 +47,50 @@ internal DuckDBDataReader(DuckDbCommand command, List queryResults private void InitReaderData() { currentRow = -1; - rowCount = NativeMethods.Query.DuckDBRowCount(currentResult); - fieldCount = (int)NativeMethods.Query.DuckDBColumnCount(currentResult); - recordsAffected = (int)NativeMethods.Query.DuckDBRowsChanged(currentResult); + + rowCount = NativeMethods.Query.DuckDBRowCount(ref currentResult); + fieldCount = (int)NativeMethods.Query.DuckDBColumnCount(ref currentResult); + chunkCount = NativeMethods.Types.DuckDBResultChunkCount(currentResult); + + currentChunkIndex = 0; + rowsReadFromCurrentChunk = 0; + + InitChunkData(); + + //recordsAffected = (int)NativeMethods.Query.DuckDBRowsChanged(currentResult); + } + + private void InitChunkData() + { + currentChunk?.Dispose(); + currentChunk = NativeMethods.Types.DuckDBResultGetChunk(currentResult, currentChunkIndex); + currentChunkRowCount = NativeMethods.DataChunks.DuckDBDataChunkGetSize(currentChunk); + + for (int i = 0; i < fieldCount; i++) + { + var vector = NativeMethods.DataChunks.DuckDBDataChunkGetVector(currentChunk, i); + + vectors[i] = NativeMethods.DataChunks.DuckDBVectorGetData(vector); + vectorValidityMask[i] = NativeMethods.DataChunks.DuckDBVectorGetValidity(vector); + } } public override bool GetBoolean(int ordinal) { - return NativeMethods.Types.DuckDBValueBoolean(currentResult, ordinal, currentRow); + var data = vectors[ordinal]; + return Marshal.ReadByte(data, (rowsReadFromCurrentChunk - 1) * Marshal.SizeOf()) != 0; } public override byte GetByte(int ordinal) { - return NativeMethods.Types.DuckDBValueUInt8(currentResult, ordinal, currentRow); + var data = vectors[ordinal]; + return Marshal.ReadByte(data, (rowsReadFromCurrentChunk - 1) * Marshal.SizeOf()); } private sbyte GetSByte(int ordinal) { - return NativeMethods.Types.DuckDBValueInt8(currentResult, ordinal, currentRow); + var data = vectors[ordinal]; + return (sbyte)Marshal.ReadByte(data, (rowsReadFromCurrentChunk - 1) * Marshal.SizeOf()); } public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length) @@ -76,41 +110,77 @@ public override long GetChars(int ordinal, long dataOffset, char[] buffer, int b public override string GetDataTypeName(int ordinal) { - return NativeMethods.Query.DuckDBColumnType(currentResult, ordinal).ToString(); + return NativeMethods.Query.DuckDBColumnType(ref currentResult, ordinal).ToString(); } public override DateTime GetDateTime(int ordinal) { - var timestampStruct = NativeMethods.Types.DuckDBValueTimestamp(currentResult, ordinal, currentRow); - + var data = vectors[ordinal]; + var timestampStruct = Marshal.PtrToStructure(data + (rowsReadFromCurrentChunk - 1) * Marshal.SizeOf()); + return timestampStruct.ToDateTime(); } private DuckDBDateOnly GetDateOnly(int ordinal) { - var date = NativeMethods.Types.DuckDBValueDate(currentResult, ordinal, currentRow); + var data = vectors[ordinal]; + var date = Marshal.PtrToStructure(data + (rowsReadFromCurrentChunk - 1) * Marshal.SizeOf()); return NativeMethods.DateTime.DuckDBFromDate(date); } private DuckDBTimeOnly GetTimeOnly(int ordinal) { - var time = NativeMethods.Types.DuckDBValueTime(currentResult, ordinal, currentRow); + var data = vectors[ordinal]; + var time = Marshal.PtrToStructure(data + (rowsReadFromCurrentChunk - 1) * Marshal.SizeOf()); return NativeMethods.DateTime.DuckDBFromTime(time); } public override decimal GetDecimal(int ordinal) { - return decimal.Parse(GetString(ordinal), CultureInfo.InvariantCulture); + using (var logicalType = NativeMethods.Query.DuckDBColumnLogicalType(ref currentResult, ordinal)) + { + var scale = NativeMethods.LogicalType.DuckDBDecimalScale(logicalType); + var internalType = NativeMethods.LogicalType.DuckDBDecimalInternalType(logicalType); + + decimal result = 0; + + var pow = (decimal)Math.Pow(10, scale); + + switch (internalType) + { + case DuckDBType.DuckdbTypeSmallInt: + result = decimal.Divide(GetInt16(ordinal), pow); + break; + case DuckDBType.DuckdbTypeInteger: + result = decimal.Divide(GetInt32(ordinal), pow); + break; + case DuckDBType.DuckdbTypeBigInt: + result = decimal.Divide(GetInt64(ordinal), pow); + break; + case DuckDBType.DuckdbTypeHugeInt: + { + var hugeInt = GetBigInteger(ordinal); + + result = (decimal)BigInteger.DivRem(hugeInt, (BigInteger)pow, out var remainder); + + result += decimal.Divide((decimal)remainder, pow); + break; + } + } + + return result; + } } public override double GetDouble(int ordinal) { - return NativeMethods.Types.DuckDBValueDouble(currentResult, ordinal, currentRow); + var data = vectors[ordinal]; + return Marshal.PtrToStructure(data + (rowsReadFromCurrentChunk - 1) * Marshal.SizeOf()); } public override Type GetFieldType(int ordinal) { - return NativeMethods.Query.DuckDBColumnType(currentResult, ordinal) switch + return NativeMethods.Query.DuckDBColumnType(ref currentResult, ordinal) switch { DuckDBType.DuckdbTypeInvalid => throw new DuckDBException("Invalid type"), DuckDBType.DuckdbTypeBoolean => typeof(bool), @@ -138,7 +208,8 @@ public override Type GetFieldType(int ordinal) public override float GetFloat(int ordinal) { - return NativeMethods.Types.DuckDBValueFloat(currentResult, ordinal, currentRow); + var data = vectors[ordinal]; + return Marshal.PtrToStructure(data + (rowsReadFromCurrentChunk - 1) * Marshal.SizeOf()); } public override Guid GetGuid(int ordinal) @@ -148,50 +219,59 @@ public override Guid GetGuid(int ordinal) public override short GetInt16(int ordinal) { - return NativeMethods.Types.DuckDBValueInt16(currentResult, ordinal, currentRow); + var data = vectors[ordinal]; + return Marshal.ReadInt16(data, (rowsReadFromCurrentChunk - 1) * Marshal.SizeOf()); } public override int GetInt32(int ordinal) { - return NativeMethods.Types.DuckDBValueInt32(currentResult, ordinal, currentRow); + var data = vectors[ordinal]; + return Marshal.ReadInt32(data, (rowsReadFromCurrentChunk - 1) * Marshal.SizeOf()); } public override long GetInt64(int ordinal) { - return NativeMethods.Types.DuckDBValueInt64(currentResult, ordinal, currentRow); + var data = vectors[ordinal]; + return Marshal.ReadInt64(data, (rowsReadFromCurrentChunk - 1) * Marshal.SizeOf()); } private ushort GetUInt16(int ordinal) { - return NativeMethods.Types.DuckDBValueUInt16(currentResult, ordinal, currentRow); + var data = vectors[ordinal]; + return (ushort)Marshal.ReadInt32(data, (rowsReadFromCurrentChunk - 1) * Marshal.SizeOf()); } private uint GetUInt32(int ordinal) { - return NativeMethods.Types.DuckDBValueUInt32(currentResult, ordinal, currentRow); + var data = vectors[ordinal]; + return (uint)Marshal.ReadInt32(data, (rowsReadFromCurrentChunk - 1) * Marshal.SizeOf()); } private ulong GetUInt64(int ordinal) { - return NativeMethods.Types.DuckDBValueUInt64(currentResult, ordinal, currentRow); + var data = vectors[ordinal]; + return (ulong)Marshal.ReadInt32(data, (rowsReadFromCurrentChunk - 1) * Marshal.SizeOf()); } private BigInteger GetBigInteger(int ordinal) { - return BigInteger.Parse(GetString(ordinal)); + var data = vectors[ordinal]; + var hugeInt = Marshal.PtrToStructure(data + (rowsReadFromCurrentChunk - 1) * Marshal.SizeOf()); + + return hugeInt.ToBigInteger(); } public override string GetName(int ordinal) { - return NativeMethods.Query.DuckDBColumnName(currentResult, ordinal).ToManagedString(false); + return NativeMethods.Query.DuckDBColumnName(ref currentResult, ordinal).ToManagedString(false); } public override int GetOrdinal(string name) { - var columnCount = NativeMethods.Query.DuckDBColumnCount(currentResult); + var columnCount = NativeMethods.Query.DuckDBColumnCount(ref currentResult); for (var i = 0; i < columnCount; i++) { - var columnName = NativeMethods.Query.DuckDBColumnName(currentResult, i).ToManagedString(false); + var columnName = NativeMethods.Query.DuckDBColumnName(ref currentResult, i).ToManagedString(false); if (name == columnName) { return i; @@ -203,9 +283,19 @@ public override int GetOrdinal(string name) public override string GetString(int ordinal) { - var unmanagedString = NativeMethods.Types.DuckDBValueVarchar(currentResult, ordinal, currentRow); + var data = vectors[ordinal] + (rowsReadFromCurrentChunk - 1) * Marshal.SizeOf(); + + var length = Marshal.ReadInt32(data); - return unmanagedString.ToManagedString(); + if (length <= InlineStringMaxLength) + { + return (data + Marshal.SizeOf()).ToManagedString(false, length); + } + else + { + var intPtr = Marshal.ReadIntPtr(data + Marshal.SizeOf() * 2); + return intPtr.ToManagedString(false, length); + } } public override object GetValue(int ordinal) @@ -215,7 +305,7 @@ public override object GetValue(int ordinal) return DBNull.Value; } - return NativeMethods.Query.DuckDBColumnType(currentResult, ordinal) switch + return NativeMethods.Query.DuckDBColumnType(ref currentResult, ordinal) switch { DuckDBType.DuckdbTypeInvalid => throw new DuckDBException("Invalid type"), DuckDBType.DuckdbTypeBoolean => GetBoolean(ordinal), @@ -243,7 +333,9 @@ public override object GetValue(int ordinal) private DuckDBInterval GetDuckDBInterval(int ordinal) { - return NativeMethods.Types.DuckDBValueInterval(currentResult, ordinal, currentRow); + var data = vectors[ordinal]; + var interval = Marshal.PtrToStructure(data + (rowsReadFromCurrentChunk - 1) * Marshal.SizeOf()); + return interval; } public override int GetValues(object[] values) @@ -258,14 +350,27 @@ public override int GetValues(object[] values) public override Stream GetStream(int ordinal) { - var blob = NativeMethods.Types.DuckDBValueBlob(currentResult, ordinal, currentRow); - return new DuckDBStream(blob); + var data = vectors[ordinal] + (rowsReadFromCurrentChunk - 1) * Marshal.SizeOf(); + + var length = Marshal.ReadInt32(data); + + var blobPointer = length <= InlineStringMaxLength + ? data + Marshal.SizeOf() + : Marshal.ReadIntPtr(data + Marshal.SizeOf() * 2); + + return new DuckDBStream(blobPointer, length); } public override bool IsDBNull(int ordinal) { - var nullMask = NativeMethods.Query.DuckDBNullmaskData(currentResult, ordinal); - return Marshal.ReadByte(nullMask, currentRow) != 0; + var validityMaskEntryIndex = (rowsReadFromCurrentChunk - 1) / 64; + var validityBitIndex = (rowsReadFromCurrentChunk - 1) % 64; + + var validityMaskEntryPtr = vectorValidityMask[ordinal] + validityMaskEntryIndex * Marshal.SizeOf(); + var validityBit = 1ul << validityBitIndex; + + var isValid = (Marshal.PtrToStructure(validityMaskEntryPtr) & validityBit) != 0; + return !isValid; } public override int FieldCount => fieldCount; @@ -283,11 +388,11 @@ public override bool IsDBNull(int ordinal) public override bool NextResult() { currentResultIndex++; - + if (currentResultIndex < queryResults.Count) { currentResult = queryResults[currentResultIndex]; - + InitReaderData(); return true; } @@ -297,7 +402,21 @@ public override bool NextResult() public override bool Read() { - return ++currentRow < rowCount; + var hasMoreRows = ++currentRow < rowCount; + + if (!hasMoreRows) return false; + + if (rowsReadFromCurrentChunk == currentChunkRowCount) + { + currentChunkIndex++; + rowsReadFromCurrentChunk = 0; + + InitChunkData(); + } + + rowsReadFromCurrentChunk++; + + return true; } public override int Depth { get; } @@ -309,7 +428,7 @@ public override IEnumerator GetEnumerator() public override DataTable GetSchemaTable() { - DataTable table = new DataTable + var table = new DataTable { Columns = { @@ -320,8 +439,10 @@ public override DataTable GetSchemaTable() { "AllowDBNull", typeof(bool) } } }; - object[] rowData = new object[5]; - for (int i = 0; i < FieldCount; i++) + + var rowData = new object[5]; + + for (var i = 0; i < FieldCount; i++) { rowData[0] = i; rowData[1] = GetName(i); @@ -330,6 +451,7 @@ public override DataTable GetSchemaTable() rowData[4] = true; table.Rows.Add(rowData); } + return table; } @@ -337,6 +459,7 @@ public override void Close() { if (closed) return; + currentChunk?.Dispose(); foreach (var result in queryResults) { result.Dispose(); diff --git a/DuckDB.NET.Data/DuckDBStream.cs b/DuckDB.NET.Data/DuckDBStream.cs index 27e89a39..87820eb0 100644 --- a/DuckDB.NET.Data/DuckDBStream.cs +++ b/DuckDB.NET.Data/DuckDBStream.cs @@ -6,12 +6,14 @@ namespace DuckDB.NET.Data; class DuckDBStream : Stream { + private readonly IntPtr data; + private long position; - private readonly DuckDBBlob blob; - public DuckDBStream(DuckDBBlob blob) + public DuckDBStream(IntPtr data, long length) { - this.blob = blob; + this.data = data; + Length = length; } public override void Flush() @@ -27,7 +29,7 @@ public override int Read(byte[] buffer, int offset, int count) { unchecked { - var source = position <= int.MaxValue ? IntPtr.Add(blob.Data, (int)position) : new IntPtr(blob.Data.ToInt64() + position); + var source = position <= int.MaxValue ? IntPtr.Add(data, (int)position) : new IntPtr(data.ToInt64() + position); Marshal.Copy(source, buffer, offset, bytesToRead); @@ -73,7 +75,7 @@ public override void Write(byte[] buffer, int offset, int count) public override bool CanSeek => true; public override bool CanWrite => false; - public override long Length => blob.Size; + public override long Length { get; } public override long Position { @@ -83,6 +85,5 @@ public override long Position public override void Close() { - blob.Dispose(); } } \ No newline at end of file diff --git a/DuckDB.NET.Data/Internal/PreparedStatement.cs b/DuckDB.NET.Data/Internal/PreparedStatement.cs index ecc69810..d8a82bff 100644 --- a/DuckDB.NET.Data/Internal/PreparedStatement.cs +++ b/DuckDB.NET.Data/Internal/PreparedStatement.cs @@ -123,13 +123,12 @@ void CleanUp() public DuckDBResult Execute(DuckDBParameterCollection parameterCollection) { - var queryResult = new DuckDBResult(); BindParameters(statement, parameterCollection); - var status = NativeMethods.PreparedStatements.DuckDBExecutePrepared(statement, queryResult); + var status = NativeMethods.PreparedStatements.DuckDBExecutePrepared(statement, out var queryResult); if (!status.IsSuccess()) { - var errorMessage = NativeMethods.Query.DuckDBResultError(queryResult).ToManagedString(false); + var errorMessage = NativeMethods.Query.DuckDBResultError(ref queryResult).ToManagedString(false); queryResult.Dispose(); throw new DuckDBException(string.IsNullOrEmpty(errorMessage) ? "DuckDBQuery failed" : errorMessage, status); } diff --git a/DuckDB.NET.Samples/Program.cs b/DuckDB.NET.Samples/Program.cs index 5b638dd7..454860b4 100644 --- a/DuckDB.NET.Samples/Program.cs +++ b/DuckDB.NET.Samples/Program.cs @@ -91,15 +91,14 @@ private static void LowLevelBindingsSample() result = Startup.DuckDBConnect(database, out var connection); using (connection) { - var queryResult = new DuckDBResult(); - result = Query.DuckDBQuery(connection, "CREATE TABLE integers(foo INTEGER, bar INTEGER);", null); - result = Query.DuckDBQuery(connection, "INSERT INTO integers VALUES (3, 4), (5, 6), (7, NULL);", null); - result = Query.DuckDBQuery(connection, "SELECT foo, bar FROM integers", queryResult); + result = Query.DuckDBQuery(connection, "CREATE TABLE integers(foo INTEGER, bar INTEGER);", out _); + result = Query.DuckDBQuery(connection, "INSERT INTO integers VALUES (3, 4), (5, 6), (7, NULL);", out _); + result = Query.DuckDBQuery(connection, "SELECT foo, bar FROM integers", out var queryResult); PrintQueryResults(queryResult); // clean up - Query.DuckDBDestroyResult(queryResult); + Query.DuckDBDestroyResult(ref queryResult); result = PreparedStatements.DuckDBPrepare(connection, "INSERT INTO integers VALUES (?, ?)", out var insertStatement); @@ -108,7 +107,7 @@ private static void LowLevelBindingsSample() result = PreparedStatements.DuckDBBindInt32(insertStatement, 1, 42); // the parameter index starts counting at 1! result = PreparedStatements.DuckDBBindInt32(insertStatement, 2, 43); - result = PreparedStatements.DuckDBExecutePrepared(insertStatement, null); + result = PreparedStatements.DuckDBExecutePrepared(insertStatement, out _); } @@ -118,13 +117,13 @@ private static void LowLevelBindingsSample() { result = PreparedStatements.DuckDBBindInt32(selectStatement, 1, 42); - result = PreparedStatements.DuckDBExecutePrepared(selectStatement, queryResult); + result = PreparedStatements.DuckDBExecutePrepared(selectStatement, out queryResult); } PrintQueryResults(queryResult); // clean up - Query.DuckDBDestroyResult(queryResult); + Query.DuckDBDestroyResult(ref queryResult); } } } @@ -159,21 +158,21 @@ private static void PrintQueryResults(DbDataReader queryResult) private static void PrintQueryResults(DuckDBResult queryResult) { - var columnCount = Query.DuckDBColumnCount(queryResult); + var columnCount = Query.DuckDBColumnCount(ref queryResult); for (var index = 0; index < columnCount; index++) { - var columnName = Query.DuckDBColumnName(queryResult, index).ToManagedString(false); + var columnName = Query.DuckDBColumnName(ref queryResult, index).ToManagedString(false); Console.Write($"{columnName} "); } Console.WriteLine(); - var rowCount = Query.DuckDBRowCount(queryResult); + var rowCount = Query.DuckDBRowCount(ref queryResult); for (long row = 0; row < rowCount; row++) { for (long column = 0; column < columnCount; column++) { - var val = Types.DuckDBValueInt32(queryResult, column, row); + var val = Types.DuckDBValueInt32(ref queryResult, column, row); Console.Write(val); Console.Write(" "); } diff --git a/DuckDB.NET.Test/AppenderTests.cs b/DuckDB.NET.Test/AppenderTests.cs index 0fcc4f7a..e0054df8 100644 --- a/DuckDB.NET.Test/AppenderTests.cs +++ b/DuckDB.NET.Test/AppenderTests.cs @@ -1,4 +1,3 @@ -using DuckDB.NET; using Xunit; using FluentAssertions; @@ -6,68 +5,67 @@ namespace DuckDB.NET.Test { public class DuckDBAppenderTests { - [Fact] - public void AppenderTests() - { - var result = NativeMethods.Startup.DuckDBOpen(null, out var database); - result.Should().Be(DuckDBState.DuckDBSuccess); + [Fact] + public void AppenderTests() + { + var result = NativeMethods.Startup.DuckDBOpen(null, out var database); + result.Should().Be(DuckDBState.DuckDBSuccess); - result = NativeMethods.Startup.DuckDBConnect(database, out var connection); - result.Should().Be(DuckDBState.DuckDBSuccess); + result = NativeMethods.Startup.DuckDBConnect(database, out var connection); + result.Should().Be(DuckDBState.DuckDBSuccess); - using (database) - using (connection) - { - var table = "CREATE TABLE appenderTest(a BOOLEAN, b TINYINT, c SMALLINT, d INTEGER, e BIGINT, f UTINYINT, g USMALLINT, h UINTEGER, i UBIGINT, j REAL, k DOUBLE, l VARCHAR);"; - result = NativeMethods.Query.DuckDBQuery(connection, table.ToUnmanagedString(), null); - result.Should().Be(DuckDBState.DuckDBSuccess); + using (database) + using (connection) + { + var table = "CREATE TABLE appenderTest(a BOOLEAN, b TINYINT, c SMALLINT, d INTEGER, e BIGINT, f UTINYINT, g USMALLINT, h UINTEGER, i UBIGINT, j REAL, k DOUBLE, l VARCHAR);"; + result = NativeMethods.Query.DuckDBQuery(connection, table.ToUnmanagedString(), out var queryResult); + result.Should().Be(DuckDBState.DuckDBSuccess); - result = NativeMethods.Appender.DuckDBAppenderCreate(connection, null, "appenderTest", out var appender); - result.Should().Be(DuckDBState.DuckDBSuccess); + result = NativeMethods.Appender.DuckDBAppenderCreate(connection, null, "appenderTest", out var appender); + result.Should().Be(DuckDBState.DuckDBSuccess); - var rows = 10; - using (appender) - { - for (var i = 0; i < rows; i++) - { - NativeMethods.Appender.DuckDBAppendBool(appender, i % 2 == 0); - NativeMethods.Appender.DuckDBAppendInt8(appender, (sbyte)i); - NativeMethods.Appender.DuckDBAppendInt16(appender, (short)i); - NativeMethods.Appender.DuckDBAppendInt32(appender, (int)i); - NativeMethods.Appender.DuckDBAppendInt64(appender, (long)i); - NativeMethods.Appender.DuckDBAppendUInt8(appender, (byte)i); - NativeMethods.Appender.DuckDBAppendUInt16(appender, (ushort)i); - NativeMethods.Appender.DuckDBAppendUInt32(appender, (uint)i); - NativeMethods.Appender.DuckDBAppendUInt64(appender, (ulong)i); - NativeMethods.Appender.DuckDBAppendFloat(appender, (float)i); - NativeMethods.Appender.DuckDBAppendDouble(appender, (double)i); - NativeMethods.Appender.DuckDBAppendVarchar(appender, i.ToString().ToUnmanagedString()); - NativeMethods.Appender.DuckDBAppenderEndRow(appender); - } - } + var rows = 10; + using (appender) + { + for (var i = 0; i < rows; i++) + { + NativeMethods.Appender.DuckDBAppendBool(appender, i % 2 == 0); + NativeMethods.Appender.DuckDBAppendInt8(appender, (sbyte)i); + NativeMethods.Appender.DuckDBAppendInt16(appender, (short)i); + NativeMethods.Appender.DuckDBAppendInt32(appender, (int)i); + NativeMethods.Appender.DuckDBAppendInt64(appender, (long)i); + NativeMethods.Appender.DuckDBAppendUInt8(appender, (byte)i); + NativeMethods.Appender.DuckDBAppendUInt16(appender, (ushort)i); + NativeMethods.Appender.DuckDBAppendUInt32(appender, (uint)i); + NativeMethods.Appender.DuckDBAppendUInt64(appender, (ulong)i); + NativeMethods.Appender.DuckDBAppendFloat(appender, (float)i); + NativeMethods.Appender.DuckDBAppendDouble(appender, (double)i); + NativeMethods.Appender.DuckDBAppendVarchar(appender, i.ToString().ToUnmanagedString()); + NativeMethods.Appender.DuckDBAppenderEndRow(appender); + } + } - var queryResult = new DuckDBResult(); - var query = "SELECT * FROM appenderTest"; - result = NativeMethods.Query.DuckDBQuery(connection, query.ToUnmanagedString(), queryResult); - result.Should().Be(DuckDBState.DuckDBSuccess); + var query = "SELECT * FROM appenderTest"; + result = NativeMethods.Query.DuckDBQuery(connection, query.ToUnmanagedString(), out queryResult); + result.Should().Be(DuckDBState.DuckDBSuccess); - var rowCount = NativeMethods.Query.DuckDBRowCount(queryResult); - rowCount.Should().Be(rows); + var rowCount = NativeMethods.Query.DuckDBRowCount(ref queryResult); + rowCount.Should().Be(rows); - for (var i = 0; i < rows; i++) - { - NativeMethods.Types.DuckDBValueBoolean(queryResult, 0, i).Should().Be(i % 2 == 0); - NativeMethods.Types.DuckDBValueInt8(queryResult, 1, i).Should().Be((sbyte)i); - NativeMethods.Types.DuckDBValueInt16(queryResult, 2, i).Should().Be((short)i); - NativeMethods.Types.DuckDBValueInt32(queryResult, 3, i).Should().Be((int)i); - NativeMethods.Types.DuckDBValueInt64(queryResult, 4, i).Should().Be((long)i); - NativeMethods.Types.DuckDBValueFloat(queryResult, 9, i).Should().Be((float)i); - NativeMethods.Types.DuckDBValueDouble(queryResult, 10, i).Should().Be((double)i); - } + for (var i = 0; i < rows; i++) + { + NativeMethods.Types.DuckDBValueBoolean(ref queryResult, 0, i).Should().Be(i % 2 == 0); + NativeMethods.Types.DuckDBValueInt8(ref queryResult, 1, i).Should().Be((sbyte)i); + NativeMethods.Types.DuckDBValueInt16(ref queryResult, 2, i).Should().Be((short)i); + NativeMethods.Types.DuckDBValueInt32(ref queryResult, 3, i).Should().Be((int)i); + NativeMethods.Types.DuckDBValueInt64(ref queryResult, 4, i).Should().Be((long)i); + NativeMethods.Types.DuckDBValueFloat(ref queryResult, 9, i).Should().Be((float)i); + NativeMethods.Types.DuckDBValueDouble(ref queryResult, 10, i).Should().Be((double)i); + } - NativeMethods.Query.DuckDBDestroyResult(queryResult); - } - } + NativeMethods.Query.DuckDBDestroyResult(ref queryResult); + } + } } } diff --git a/DuckDB.NET.Test/DuckDBDataReaderTests.cs b/DuckDB.NET.Test/DuckDBDataReaderTests.cs index 333f4734..14fd8be8 100644 --- a/DuckDB.NET.Test/DuckDBDataReaderTests.cs +++ b/DuckDB.NET.Test/DuckDBDataReaderTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Data; using DuckDB.NET.Data; using FluentAssertions; @@ -68,7 +69,7 @@ public void ReaderValues() reader[1].Should().Be(reader.GetDecimal(1)); reader.GetValue(2).Should().Be(reader.GetBoolean(2)); reader[3].Should().Be(DBNull.Value); - + var values = new object[4]; reader.GetValues(values); values.Should().BeEquivalentTo(new object[] { 1, 2.4, true, DBNull.Value }); @@ -91,10 +92,10 @@ public void ReaderEnumerator() enumerator.MoveNext().Should().Be(true); (enumerator.Current as IDataRecord).GetInt32(0).Should().Be(7); - + enumerator.MoveNext().Should().Be(true); (enumerator.Current as IDataRecord).GetInt32(0).Should().Be(11); - + enumerator.MoveNext().Should().Be(false); } @@ -132,7 +133,7 @@ public void ReadIntervalValues() interval.Micros.Should().Be(30_000_000); } - + [Fact] public void LoadDataTable() { @@ -157,16 +158,71 @@ public void MultipleStatementsQueryData() duckDbCommand.CommandText = "Select 1; Select 2"; using var reader = duckDbCommand.ExecuteReader(); - + reader.Read(); reader.GetInt32(0).Should().Be(1); reader.NextResult().Should().BeTrue(); - + reader.Read().Should().BeTrue(); reader.GetInt32(0).Should().Be(2); reader.NextResult().Should().BeFalse(); } + + [Fact] + public void ReadManyRows() + { + using var connection = new DuckDBConnection("DataSource=:memory:"); + connection.Open(); + + using (var duckDbCommand = connection.CreateCommand()) + { + var table = "CREATE TABLE TableForManyRows(foo INTEGER, bar VARCHAR);"; + duckDbCommand.CommandText = table; + duckDbCommand.ExecuteNonQuery(); + } + + var rows = 10_000; + + var values = new List>(); + + using (var appender = connection.CreateAppender("TableForManyRows")) + { + for (var i = 0; i < rows; i++) + { + var value = new string((char)('A' + i % 26), Random.Shared.Next(2, 20)); + values.Add(new KeyValuePair(i, value)); + + var row = appender.CreateRow(); + + row + .AppendValue(i) + .AppendValue(value) + .EndRow(); + } + values.Add(new KeyValuePair(null, null)); + + appender.CreateRow().AppendNullValue().AppendNullValue().EndRow(); + } + + using (var duckDbCommand = connection.CreateCommand()) + { + duckDbCommand.CommandText = "SELECT * FROM TableForManyRows"; + using var reader = duckDbCommand.ExecuteReader(); + + var readRowIndex = 0; + while (reader.Read()) + { + var item = values[readRowIndex]; + + (reader.IsDBNull(0) ? (int?)null : reader.GetInt32(0)).Should().Be(item.Key); + (reader.IsDBNull(1) ? null : reader.GetString(1)).Should().Be(item.Value); + + readRowIndex++; + } + readRowIndex.Should().Be(rows + 1); + } + } } diff --git a/DuckDB.NET.Test/ManagedAppenderTests.cs b/DuckDB.NET.Test/ManagedAppenderTests.cs index 10901b15..e3e558bf 100644 --- a/DuckDB.NET.Test/ManagedAppenderTests.cs +++ b/DuckDB.NET.Test/ManagedAppenderTests.cs @@ -111,7 +111,7 @@ public void ManagedAppenderUnicodeTests() var results = new List(); while (reader.Read()) { - var text = reader.GetString(1); + var text = reader.IsDBNull(1) ? null : reader.GetString(1); results.Add(text); } diff --git a/DuckDB.NET.Test/Parameters/BlobParameterTests.cs b/DuckDB.NET.Test/Parameters/BlobParameterTests.cs index be2e3e0d..b3da360d 100644 --- a/DuckDB.NET.Test/Parameters/BlobParameterTests.cs +++ b/DuckDB.NET.Test/Parameters/BlobParameterTests.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Numerics; using DuckDB.NET.Data; using FluentAssertions; using Xunit; @@ -63,39 +62,38 @@ public void SeekTest() connection.Open(); var command = connection.CreateCommand(); - command.CommandText = "SELECT 'ABCDEFGH'::BLOB;"; + var blobValue = "ABCDEFGHIJKLMNOPQR"; + command.CommandText = $"SELECT '{blobValue}'::BLOB;"; command.ExecuteNonQuery(); var reader = command.ExecuteReader(); reader.Read(); - using (var stream = reader.GetStream(0)) + using var stream = reader.GetStream(0); + stream.CanSeek.Should().Be(true); + using (var streamReader = new StreamReader(stream, leaveOpen: true)) { - stream.CanSeek.Should().Be(true); - using (var streamReader = new StreamReader(stream, leaveOpen: true)) - { - stream.Seek(2, SeekOrigin.Begin); - var text = streamReader.ReadToEnd(); - text.Should().Be("CDEFGH"); + stream.Seek(2, SeekOrigin.Begin); + var text = streamReader.ReadToEnd(); + text.Should().Be(blobValue.Substring(2)); - stream.Seek(-4, SeekOrigin.End); - streamReader.ReadLine().Should().Be("EFGH"); + stream.Seek(-4, SeekOrigin.End); + streamReader.ReadLine().Should().Be(blobValue[^4..]); - stream.Seek(-4, SeekOrigin.End); - stream.Seek(2, SeekOrigin.Current); + stream.Seek(-4, SeekOrigin.End); + stream.Seek(2, SeekOrigin.Current); - streamReader.ReadLine().Should().Be("GH"); + streamReader.ReadLine().Should().Be(blobValue[^4..][^4..][2..]); - stream.Position = 7; - streamReader.ReadLine().Should().Be("H"); + stream.Position = 7; + streamReader.ReadLine().Should().Be(blobValue[7..]); - stream.Seek(0, SeekOrigin.Begin).Should().Be(0); - stream.Seek(0, SeekOrigin.End).Should().Be(stream.Length); - stream.Position = 5; - stream.Seek(0, SeekOrigin.Current).Should().Be(stream.Position); + stream.Seek(0, SeekOrigin.Begin).Should().Be(0); + stream.Seek(0, SeekOrigin.End).Should().Be(stream.Length); + stream.Position = 5; + stream.Seek(0, SeekOrigin.Current).Should().Be(stream.Position); - stream.Invoking(s => s.Seek(stream.Length+1, SeekOrigin.Current)).Should().Throw(); - } + stream.Invoking(s => s.Seek(stream.Length+1, SeekOrigin.Current)).Should().Throw(); } } diff --git a/DuckDB.NET.Test/Parameters/DecimalParameterTest.cs b/DuckDB.NET.Test/Parameters/DecimalParameterTest.cs index 8fec1dd5..62081dbe 100644 --- a/DuckDB.NET.Test/Parameters/DecimalParameterTest.cs +++ b/DuckDB.NET.Test/Parameters/DecimalParameterTest.cs @@ -1,6 +1,5 @@ using System; using System.Globalization; -using System.Text.RegularExpressions; using DuckDB.NET.Data; using FluentAssertions; using Xunit; @@ -17,18 +16,35 @@ public void SimpleTest() var values = new[]{0m, decimal.Zero, decimal.MinValue, decimal.MaxValue, decimal.MaxValue / 3, decimal.One, - decimal.One / 2, decimal.One / 3, decimal.MinusOne, - //decimal.MinusOne / 2, - decimal.MinusOne / 3}; + decimal.One / 2, decimal.MinusOne, + decimal.MinusOne / 2}; foreach (var value in values) { var command = connection.CreateCommand(); - command.CommandText = $"SELECT {Convert.ToString(value, CultureInfo.InvariantCulture)};"; + command.CommandText = $"SELECT {Convert.ToString(value, CultureInfo.InvariantCulture)}::DECIMAL(38,9);"; command.ExecuteNonQuery(); - //var scalar = command.ExecuteScalar(); - //scalar.Should().Be(value); + var scalar = command.ExecuteScalar(); + scalar.Should().Be(value); + + var reader = command.ExecuteReader(); + reader.Read(); + var receivedValue = reader.GetDecimal(0); + receivedValue.Should().Be(value); + } + + + values = new[] { decimal.One / 3, decimal.MinusOne / 3 }; + + foreach (var value in values) + { + var command = connection.CreateCommand(); + command.CommandText = $"SELECT {Convert.ToString(value, CultureInfo.InvariantCulture)}::DECIMAL(38,28);"; + command.ExecuteNonQuery(); + + var scalar = command.ExecuteScalar(); + scalar.Should().Be(value); var reader = command.ExecuteReader(); reader.Read(); diff --git a/DuckDB.NET.Test/QueryTests.cs b/DuckDB.NET.Test/QueryTests.cs index 5856d058..89ff3f3c 100644 --- a/DuckDB.NET.Test/QueryTests.cs +++ b/DuckDB.NET.Test/QueryTests.cs @@ -18,32 +18,30 @@ public void QueryTest() using (connection) { var table = "CREATE TABLE queryTest(a INTEGER, b BOOLEAN);"; - result = NativeMethods.Query.DuckDBQuery(connection, table.ToUnmanagedString(), null); + result = NativeMethods.Query.DuckDBQuery(connection, table.ToUnmanagedString(), out _); result.Should().Be(DuckDBState.DuckDBSuccess); - - var queryResult = new DuckDBResult(); var insert = "INSERT INTO queryTest VALUES (1, TRUE), (2, FALSE), (3, TRUE);"; - result = NativeMethods.Query.DuckDBQuery(connection, insert.ToUnmanagedString(), queryResult); + result = NativeMethods.Query.DuckDBQuery(connection, insert.ToUnmanagedString(), out var queryResult); result.Should().Be(DuckDBState.DuckDBSuccess); - var rowsChanged = NativeMethods.Query.DuckDBRowsChanged(queryResult); + var rowsChanged = NativeMethods.Query.DuckDBRowsChanged(ref queryResult); rowsChanged.Should().Be(3); - NativeMethods.Query.DuckDBDestroyResult(queryResult); + NativeMethods.Query.DuckDBDestroyResult(ref queryResult); var query = "SELECT * FROM queryTest;"; - result = NativeMethods.Query.DuckDBQuery(connection, query.ToUnmanagedString(), queryResult); + result = NativeMethods.Query.DuckDBQuery(connection, query.ToUnmanagedString(), out queryResult); result.Should().Be(DuckDBState.DuckDBSuccess); - var rowCount = NativeMethods.Query.DuckDBRowCount(queryResult); + var rowCount = NativeMethods.Query.DuckDBRowCount(ref queryResult); rowCount.Should().Be(3); - var columnCount = NativeMethods.Query.DuckDBColumnCount(queryResult); + var columnCount = NativeMethods.Query.DuckDBColumnCount(ref queryResult); columnCount.Should().Be(2); - NativeMethods.Query.DuckDBDestroyResult(queryResult); + NativeMethods.Query.DuckDBDestroyResult(ref queryResult); } } } diff --git a/testenvironments.json b/testenvironments.json new file mode 100644 index 00000000..539ead6b --- /dev/null +++ b/testenvironments.json @@ -0,0 +1,10 @@ +{ + "version": "1", + "environments": [ + { + "name": "Ubuntu", + "type": "wsl", + "wslDistribution": "Ubuntu" + } + ] +}