Index: SQLite.Interop/src/win/interop.c ================================================================== --- SQLite.Interop/src/win/interop.c +++ SQLite.Interop/src/win/interop.c @@ -389,18 +389,18 @@ } SQLITE_API const unsigned char * WINAPI sqlite3_column_text_interop(sqlite3_stmt *stmt, int iCol, int *plen) { const unsigned char *pval = sqlite3_column_text(stmt, iCol); - *plen = (pval != 0) ? strlen((char *)pval) : 0; + *plen = sqlite3_column_bytes(stmt, iCol); return pval; } SQLITE_API const void * WINAPI sqlite3_column_text16_interop(sqlite3_stmt *stmt, int iCol, int *plen) { const void *pval = sqlite3_column_text16(stmt, iCol); - *plen = (pval != 0) ? wcslen((wchar_t *)pval) * sizeof(wchar_t): 0; + *plen = sqlite3_column_bytes16(stmt, iCol); return pval; } SQLITE_API int WINAPI sqlite3_finalize_interop(sqlite3_stmt *stmt) { @@ -525,18 +525,18 @@ } SQLITE_API const unsigned char * WINAPI sqlite3_value_text_interop(sqlite3_value *val, int *plen) { const unsigned char *pval = sqlite3_value_text(val); - *plen = (pval != 0) ? strlen((char *)pval) : 0; + *plen = sqlite3_value_bytes(val); return pval; } SQLITE_API const void * WINAPI sqlite3_value_text16_interop(sqlite3_value *val, int *plen) { const void *pval = sqlite3_value_text16(val); - *plen = (pval != 0) ? wcslen((wchar_t *)pval) * sizeof(wchar_t) : 0; + *plen = sqlite3_value_bytes16(val); return pval; } SQLITE_API void WINAPI sqlite3_result_double_interop(sqlite3_context *pctx, double *val) { Index: System.Data.SQLite/SQLite3.cs ================================================================== --- System.Data.SQLite/SQLite3.cs +++ System.Data.SQLite/SQLite3.cs @@ -1202,21 +1202,23 @@ { #if !SQLITE_STANDARD int len; return UTF8ToString(UnsafeNativeMethods.sqlite3_column_text_interop(stmt._sqlite_stmt, index, out len), len); #else - return UTF8ToString(UnsafeNativeMethods.sqlite3_column_text(stmt._sqlite_stmt, index), -1); + return UTF8ToString(UnsafeNativeMethods.sqlite3_column_text(stmt._sqlite_stmt, index), + UnsafeNativeMethods.sqlite3_column_bytes(stmt._sqlite_stmt, index)); #endif } internal override DateTime GetDateTime(SQLiteStatement stmt, int index) { #if !SQLITE_STANDARD int len; return ToDateTime(UnsafeNativeMethods.sqlite3_column_text_interop(stmt._sqlite_stmt, index, out len), len); #else - return ToDateTime(UnsafeNativeMethods.sqlite3_column_text(stmt._sqlite_stmt, index), -1); + return ToDateTime(UnsafeNativeMethods.sqlite3_column_text(stmt._sqlite_stmt, index), + UnsafeNativeMethods.sqlite3_column_bytes(stmt._sqlite_stmt, index)); #endif } internal override long GetBytes(SQLiteStatement stmt, int index, int nDataOffset, byte[] bDest, int nStart, int nLength) { @@ -1430,11 +1432,12 @@ { #if !SQLITE_STANDARD int len; return UTF8ToString(UnsafeNativeMethods.sqlite3_value_text_interop(ptr, out len), len); #else - return UTF8ToString(UnsafeNativeMethods.sqlite3_value_text(ptr), -1); + return UTF8ToString(UnsafeNativeMethods.sqlite3_value_text(ptr), + UnsafeNativeMethods.sqlite3_value_bytes(ptr)); #endif } internal override TypeAffinity GetParamValueType(IntPtr ptr) { Index: System.Data.SQLite/SQLite3_UTF16.cs ================================================================== --- System.Data.SQLite/SQLite3_UTF16.cs +++ System.Data.SQLite/SQLite3_UTF16.cs @@ -1,115 +1,115 @@ -/******************************************************** - * ADO.NET 2.0 Data Provider for SQLite Version 3.X - * Written by Robert Simpson (robert@blackcastlesoft.com) - * - * Released to the public domain, use at your own risk! - ********************************************************/ - -namespace System.Data.SQLite -{ - using System; - -#if !NET_COMPACT_20 && TRACE_CONNECTION - using System.Diagnostics; -#endif - - using System.IO; - using System.Runtime.InteropServices; - - /// - /// Alternate SQLite3 object, overriding many text behaviors to support UTF-16 (Unicode) - /// - internal class SQLite3_UTF16 : SQLite3 - { - internal SQLite3_UTF16(SQLiteDateFormats fmt, DateTimeKind kind) - : base(fmt, kind) - { - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - - #region IDisposable "Pattern" Members - private bool disposed; - private void CheckDisposed() /* throw */ - { -#if THROW_ON_DISPOSED - if (disposed) - throw new ObjectDisposedException(typeof(SQLite3_UTF16).Name); -#endif - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - - protected override void Dispose(bool disposing) - { - try - { - if (!disposed) - { - //if (disposing) - //{ - // //////////////////////////////////// - // // dispose managed resources here... - // //////////////////////////////////// - //} - - ////////////////////////////////////// - // release unmanaged resources here... - ////////////////////////////////////// - - disposed = true; - } - } - finally - { - base.Dispose(disposing); - } - } - #endregion - - /////////////////////////////////////////////////////////////////////////////////////////////// - - /// - /// Overrides SQLiteConvert.ToString() to marshal UTF-16 strings instead of UTF-8 - /// - /// A pointer to a UTF-16 string - /// The length (IN BYTES) of the string - /// A .NET string - public override string ToString(IntPtr b, int nbytelen) - { - CheckDisposed(); - return UTF16ToString(b, nbytelen); - } - - public static string UTF16ToString(IntPtr b, int nbytelen) - { - if (nbytelen == 0 || b == IntPtr.Zero) return ""; - - if (nbytelen == -1) - return Marshal.PtrToStringUni(b); - else - return Marshal.PtrToStringUni(b, nbytelen / 2); - } - - internal override void Open(string strFilename, SQLiteConnectionFlags connectionFlags, SQLiteOpenFlagsEnum openFlags, int maxPoolSize, bool usePool) - { - if (_sql != null) return; - - _usePool = usePool; - _fileName = strFilename; - - if (usePool) - { - _sql = SQLiteConnectionPool.Remove(strFilename, maxPoolSize, out _poolVersion); - -#if !NET_COMPACT_20 && TRACE_CONNECTION - Trace.WriteLine(String.Format("Open16 (Pool): {0}", (_sql != null) ? _sql.ToString() : "")); -#endif - } - - if (_sql == null) +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + +namespace System.Data.SQLite +{ + using System; + +#if !NET_COMPACT_20 && TRACE_CONNECTION + using System.Diagnostics; +#endif + + using System.IO; + using System.Runtime.InteropServices; + + /// + /// Alternate SQLite3 object, overriding many text behaviors to support UTF-16 (Unicode) + /// + internal class SQLite3_UTF16 : SQLite3 + { + internal SQLite3_UTF16(SQLiteDateFormats fmt, DateTimeKind kind) + : base(fmt, kind) + { + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLite3_UTF16).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + protected override void Dispose(bool disposing) + { + try + { + if (!disposed) + { + //if (disposing) + //{ + // //////////////////////////////////// + // // dispose managed resources here... + // //////////////////////////////////// + //} + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + /// + /// Overrides SQLiteConvert.ToString() to marshal UTF-16 strings instead of UTF-8 + /// + /// A pointer to a UTF-16 string + /// The length (IN BYTES) of the string + /// A .NET string + public override string ToString(IntPtr b, int nbytelen) + { + CheckDisposed(); + return UTF16ToString(b, nbytelen); + } + + public static string UTF16ToString(IntPtr b, int nbytelen) + { + if (nbytelen == 0 || b == IntPtr.Zero) return ""; + + if (nbytelen == -1) + return Marshal.PtrToStringUni(b); + else + return Marshal.PtrToStringUni(b, nbytelen / 2); + } + + internal override void Open(string strFilename, SQLiteConnectionFlags connectionFlags, SQLiteOpenFlagsEnum openFlags, int maxPoolSize, bool usePool) + { + if (_sql != null) return; + + _usePool = usePool; + _fileName = strFilename; + + if (usePool) + { + _sql = SQLiteConnectionPool.Remove(strFilename, maxPoolSize, out _poolVersion); + +#if !NET_COMPACT_20 && TRACE_CONNECTION + Trace.WriteLine(String.Format("Open16 (Pool): {0}", (_sql != null) ? _sql.ToString() : "")); +#endif + } + + if (_sql == null) { try { // do nothing. } @@ -136,139 +136,141 @@ throw new SQLiteException(SQLiteErrorCode.CantOpen, strFilename); n = UnsafeNativeMethods.sqlite3_open16(strFilename, out db); } -#if !NET_COMPACT_20 && TRACE_CONNECTION - Trace.WriteLine(String.Format("Open16: {0}", db)); +#if !NET_COMPACT_20 && TRACE_CONNECTION + Trace.WriteLine(String.Format("Open16: {0}", db)); #endif if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, null); _sql = new SQLiteConnectionHandle(db); - } - lock (_sql) { /* HACK: Force the SyncBlock to be "created" now. */ } - } - _functionsArray = SQLiteFunction.BindFunctions(this, connectionFlags); - SetTimeout(0); - GC.KeepAlive(_sql); - } - - internal override void Bind_DateTime(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, DateTime dt) - { - switch (_datetimeFormat) - { - case SQLiteDateFormats.Ticks: - case SQLiteDateFormats.JulianDay: - case SQLiteDateFormats.UnixEpoch: - { - base.Bind_DateTime(stmt, flags, index, dt); - break; - } - default: - { -#if !PLATFORM_COMPACTFRAMEWORK - if ((flags & SQLiteConnectionFlags.LogBind) == SQLiteConnectionFlags.LogBind) - { - SQLiteStatementHandle handle = - (stmt != null) ? stmt._sqlite_stmt : null; - - LogBind(handle, index, dt); - } -#endif - - Bind_Text(stmt, flags, index, ToString(dt)); - break; - } - } - } - - internal override void Bind_Text(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, string value) - { - SQLiteStatementHandle handle = stmt._sqlite_stmt; - -#if !PLATFORM_COMPACTFRAMEWORK - if ((flags & SQLiteConnectionFlags.LogBind) == SQLiteConnectionFlags.LogBind) - { - LogBind(handle, index, value); - } -#endif - - SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_text16(handle, index, value, value.Length * 2, (IntPtr)(-1)); - if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError()); - } - - internal override DateTime GetDateTime(SQLiteStatement stmt, int index) - { - return ToDateTime(GetText(stmt, index)); - } - - internal override string ColumnName(SQLiteStatement stmt, int index) - { -#if !SQLITE_STANDARD - int len; - return UTF16ToString(UnsafeNativeMethods.sqlite3_column_name16_interop(stmt._sqlite_stmt, index, out len), len); -#else - return UTF16ToString(UnsafeNativeMethods.sqlite3_column_name16(stmt._sqlite_stmt, index), -1); -#endif - } - - internal override string GetText(SQLiteStatement stmt, int index) - { -#if !SQLITE_STANDARD - int len; - return UTF16ToString(UnsafeNativeMethods.sqlite3_column_text16_interop(stmt._sqlite_stmt, index, out len), len); -#else - return UTF16ToString(UnsafeNativeMethods.sqlite3_column_text16(stmt._sqlite_stmt, index), -1); -#endif - } - - internal override string ColumnOriginalName(SQLiteStatement stmt, int index) - { -#if !SQLITE_STANDARD - int len; - return UTF16ToString(UnsafeNativeMethods.sqlite3_column_origin_name16_interop(stmt._sqlite_stmt, index, out len), len); -#else - return UTF16ToString(UnsafeNativeMethods.sqlite3_column_origin_name16(stmt._sqlite_stmt, index), -1); -#endif - } - - internal override string ColumnDatabaseName(SQLiteStatement stmt, int index) - { -#if !SQLITE_STANDARD - int len; - return UTF16ToString(UnsafeNativeMethods.sqlite3_column_database_name16_interop(stmt._sqlite_stmt, index, out len), len); -#else - return UTF16ToString(UnsafeNativeMethods.sqlite3_column_database_name16(stmt._sqlite_stmt, index), -1); -#endif - } - - internal override string ColumnTableName(SQLiteStatement stmt, int index) - { -#if !SQLITE_STANDARD - int len; - return UTF16ToString(UnsafeNativeMethods.sqlite3_column_table_name16_interop(stmt._sqlite_stmt, index, out len), len); -#else - return UTF16ToString(UnsafeNativeMethods.sqlite3_column_table_name16(stmt._sqlite_stmt, index), -1); -#endif - } - - internal override string GetParamValueText(IntPtr ptr) - { -#if !SQLITE_STANDARD - int len; - return UTF16ToString(UnsafeNativeMethods.sqlite3_value_text16_interop(ptr, out len), len); -#else - return UTF16ToString(UnsafeNativeMethods.sqlite3_value_text16(ptr), -1); -#endif - } - - internal override void ReturnError(IntPtr context, string value) - { - UnsafeNativeMethods.sqlite3_result_error16(context, value, value.Length * 2); - } - - internal override void ReturnText(IntPtr context, string value) - { - UnsafeNativeMethods.sqlite3_result_text16(context, value, value.Length * 2, (IntPtr)(-1)); - } - } -} + } + lock (_sql) { /* HACK: Force the SyncBlock to be "created" now. */ } + } + _functionsArray = SQLiteFunction.BindFunctions(this, connectionFlags); + SetTimeout(0); + GC.KeepAlive(_sql); + } + + internal override void Bind_DateTime(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, DateTime dt) + { + switch (_datetimeFormat) + { + case SQLiteDateFormats.Ticks: + case SQLiteDateFormats.JulianDay: + case SQLiteDateFormats.UnixEpoch: + { + base.Bind_DateTime(stmt, flags, index, dt); + break; + } + default: + { +#if !PLATFORM_COMPACTFRAMEWORK + if ((flags & SQLiteConnectionFlags.LogBind) == SQLiteConnectionFlags.LogBind) + { + SQLiteStatementHandle handle = + (stmt != null) ? stmt._sqlite_stmt : null; + + LogBind(handle, index, dt); + } +#endif + + Bind_Text(stmt, flags, index, ToString(dt)); + break; + } + } + } + + internal override void Bind_Text(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, string value) + { + SQLiteStatementHandle handle = stmt._sqlite_stmt; + +#if !PLATFORM_COMPACTFRAMEWORK + if ((flags & SQLiteConnectionFlags.LogBind) == SQLiteConnectionFlags.LogBind) + { + LogBind(handle, index, value); + } +#endif + + SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_text16(handle, index, value, value.Length * 2, (IntPtr)(-1)); + if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError()); + } + + internal override DateTime GetDateTime(SQLiteStatement stmt, int index) + { + return ToDateTime(GetText(stmt, index)); + } + + internal override string ColumnName(SQLiteStatement stmt, int index) + { +#if !SQLITE_STANDARD + int len; + return UTF16ToString(UnsafeNativeMethods.sqlite3_column_name16_interop(stmt._sqlite_stmt, index, out len), len); +#else + return UTF16ToString(UnsafeNativeMethods.sqlite3_column_name16(stmt._sqlite_stmt, index), -1); +#endif + } + + internal override string GetText(SQLiteStatement stmt, int index) + { +#if !SQLITE_STANDARD + int len; + return UTF16ToString(UnsafeNativeMethods.sqlite3_column_text16_interop(stmt._sqlite_stmt, index, out len), len); +#else + return UTF16ToString(UnsafeNativeMethods.sqlite3_column_text16(stmt._sqlite_stmt, index), + UnsafeNativeMethods.sqlite3_column_bytes16(stmt._sqlite_stmt, index)); +#endif + } + + internal override string ColumnOriginalName(SQLiteStatement stmt, int index) + { +#if !SQLITE_STANDARD + int len; + return UTF16ToString(UnsafeNativeMethods.sqlite3_column_origin_name16_interop(stmt._sqlite_stmt, index, out len), len); +#else + return UTF16ToString(UnsafeNativeMethods.sqlite3_column_origin_name16(stmt._sqlite_stmt, index), -1); +#endif + } + + internal override string ColumnDatabaseName(SQLiteStatement stmt, int index) + { +#if !SQLITE_STANDARD + int len; + return UTF16ToString(UnsafeNativeMethods.sqlite3_column_database_name16_interop(stmt._sqlite_stmt, index, out len), len); +#else + return UTF16ToString(UnsafeNativeMethods.sqlite3_column_database_name16(stmt._sqlite_stmt, index), -1); +#endif + } + + internal override string ColumnTableName(SQLiteStatement stmt, int index) + { +#if !SQLITE_STANDARD + int len; + return UTF16ToString(UnsafeNativeMethods.sqlite3_column_table_name16_interop(stmt._sqlite_stmt, index, out len), len); +#else + return UTF16ToString(UnsafeNativeMethods.sqlite3_column_table_name16(stmt._sqlite_stmt, index), -1); +#endif + } + + internal override string GetParamValueText(IntPtr ptr) + { +#if !SQLITE_STANDARD + int len; + return UTF16ToString(UnsafeNativeMethods.sqlite3_value_text16_interop(ptr, out len), len); +#else + return UTF16ToString(UnsafeNativeMethods.sqlite3_value_text16(ptr), + UnsafeNativeMethods.sqlite3_value_bytes16(ptr)); +#endif + } + + internal override void ReturnError(IntPtr context, string value) + { + UnsafeNativeMethods.sqlite3_result_error16(context, value, value.Length * 2); + } + + internal override void ReturnText(IntPtr context, string value) + { + UnsafeNativeMethods.sqlite3_result_text16(context, value, value.Length * 2, (IntPtr)(-1)); + } + } +} Index: System.Data.SQLite/SQLiteConvert.cs ================================================================== --- System.Data.SQLite/SQLiteConvert.cs +++ System.Data.SQLite/SQLiteConvert.cs @@ -160,17 +160,19 @@ /// The pointer to the memory where the UTF-8 string is encoded /// The number of bytes to decode /// A string containing the translated character(s) public static string UTF8ToString(IntPtr nativestring, int nativestringlen) { - if (nativestringlen == 0 || nativestring == IntPtr.Zero) return ""; - if (nativestringlen == -1) + if (nativestring == IntPtr.Zero || nativestringlen == 0) return String.Empty; + if (nativestringlen < 0) { - do - { + nativestringlen = 0; + + while (Marshal.ReadByte(nativestring, nativestringlen) != 0) nativestringlen++; - } while (Marshal.ReadByte(nativestring, nativestringlen) != 0); + + if (nativestringlen == 0) return String.Empty; } byte[] byteArray = new byte[nativestringlen]; Marshal.Copy(nativestring, byteArray, 0, nativestringlen); Index: System.Data.SQLite/UnsafeNativeMethods.cs ================================================================== --- System.Data.SQLite/UnsafeNativeMethods.cs +++ System.Data.SQLite/UnsafeNativeMethods.cs @@ -1128,10 +1128,17 @@ internal static extern int sqlite3_column_bytes(IntPtr stmt, int index); #if !PLATFORM_COMPACTFRAMEWORK [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] #else + [DllImport(SQLITE_DLL)] +#endif + internal static extern int sqlite3_column_bytes16(IntPtr stmt, int index); + +#if !PLATFORM_COMPACTFRAMEWORK + [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] +#else [DllImport(SQLITE_DLL)] #endif internal static extern TypeAffinity sqlite3_column_type(IntPtr stmt, int index); #if !PLATFORM_COMPACTFRAMEWORK @@ -1160,10 +1167,17 @@ #else [DllImport(SQLITE_DLL)] #endif internal static extern int sqlite3_value_bytes(IntPtr p); +#if !PLATFORM_COMPACTFRAMEWORK + [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] +#else + [DllImport(SQLITE_DLL)] +#endif + internal static extern int sqlite3_value_bytes16(IntPtr p); + #if !PLATFORM_COMPACTFRAMEWORK [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)] internal static extern double sqlite3_value_double(IntPtr p); #else [DllImport(SQLITE_DLL)] ADDED Tests/tkt-3567020edf.eagle Index: Tests/tkt-3567020edf.eagle ================================================================== --- /dev/null +++ Tests/tkt-3567020edf.eagle @@ -0,0 +1,63 @@ +############################################################################### +# +# tkt-3567020edf.eagle -- +# +# Written by Joe Mistachkin. +# Released to the public domain, use at your own risk! +# +############################################################################### + +package require Eagle +package require Eagle.Library +package require Eagle.Test + +runTestPrologue + +############################################################################### + +package require System.Data.SQLite.Test +runSQLiteTestPrologue + +############################################################################### + +runTest {test tkt-3567020edf-1.1 {embedded NUL characters (UTF-8)} -setup { + setupDb [set fileName tkt-3567020edf-1.1.db] +} -body { + sql execute $db "CREATE TABLE t1(x);" + + sql execute $db "INSERT INTO t1 (x) VALUES(?);" \ + [list param1 String one\x00two] + + sql execute -execute reader -format list $db "SELECT x FROM t1;" +} -cleanup { + cleanupDb $fileName + + unset -nocomplain db fileName +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -result \ +"one\x00two"} + +############################################################################### + +runTest {test tkt-3567020edf-1.2 {embedded NUL characters (UTF-16)} -setup { + setupDb [set fileName tkt-3567020edf-1.2.db] "" "" "" "" \ + UseUTF16Encoding=True +} -body { + sql execute $db "CREATE TABLE t1(x);" + + sql execute $db "INSERT INTO t1 (x) VALUES(?);" \ + [list param1 String one\x00two] + + sql execute -execute reader -format list $db "SELECT x FROM t1;" +} -cleanup { + cleanupDb $fileName + + unset -nocomplain db fileName +} -constraints \ +{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -result \ +"one\x00two"} + +############################################################################### + +runSQLiteTestEpilogue +runTestEpilogue