context: /********************************************************
   * 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.DenverDB
  {
    using System;
    using System.Collections.Generic;
  
  #if !NET_COMPACT_20 && TRACE_CONNECTION
    using System.Diagnostics;
  #endif
  
    using System.IO;
    using System.Runtime.InteropServices;
  
    /// <summary>
    /// Alternate SQLite3 object, overriding many text behaviors to support UTF-16 (Unicode)
    /// </summary>
    internal sealed class SQLite3_UTF16 : SQLite3
    {
      /// <summary>
      /// Constructs the object used to interact with the SQLite core library
      /// using the UTF-8 text encoding.
      /// </summary>
      /// <param name="fmt">
      /// The DateTime format to be used when converting string values to a
      /// DateTime and binding DateTime parameters.
      /// </param>
      /// <param name="kind">
      /// The <see cref="DateTimeKind" /> to be used when creating DateTime
      /// values.
      /// </param>
      /// <param name="fmtString">
      /// The format string to be used when parsing and formatting DateTime
      /// values.
      /// </param>
      /// <param name="db">
      /// The native handle to be associated with the database connection.
      /// </param>
      /// <param name="fileName">
      /// The fully qualified file name associated with <paramref name="db" />.
      /// </param>
      /// <param name="ownHandle">
      /// Non-zero if the newly created object instance will need to dispose
      /// of <paramref name="db" /> when it is no longer needed.
      /// </param>
      internal SQLite3_UTF16(
          SQLiteDateFormats fmt,
          DateTimeKind kind,
          string fmtString,
          IntPtr db,
          string fileName,
          bool ownHandle
          )
          : base(fmt, kind, fmtString, db, fileName, ownHandle)
      {
      }
  
      ///////////////////////////////////////////////////////////////////////////////////////////////
  
      #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...
                  //////////////////////////////////////
              }
          }
          finally
          {
              base.Dispose(disposing);
  
              //
              // NOTE: Everything should be fully disposed at this point.
              //
              disposed = true;
          }
      }
      #endregion
  
      ///////////////////////////////////////////////////////////////////////////////////////////////
  
      /// <summary>
      /// Overrides SQLiteConvert.ToString() to marshal UTF-16 strings instead of UTF-8
      /// </summary>
      /// <param name="b">A pointer to a UTF-16 string</param>
      /// <param name="nbytelen">The length (IN BYTES) of the string</param>
      /// <returns>A .NET string</returns>
      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)
      {
        //
        // NOTE: If the database connection is currently open, attempt to
        //       close it now.  This must be done because the file name or
        //       other parameters that may impact the underlying database
        //       connection may have changed.
        //
        if (_sql != null) Close(true);
  
        //
        // NOTE: If the connection was not closed successfully, throw an
        //       exception now.
        //
        if (_sql != null)
            throw new SQLiteException("connection handle is still active");
  
        _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() : "<null>"));
  #endif
        }
  
        if (_sql == null)
        {
          try
          {
              // do nothing.
          }
          finally /* NOTE: Thread.Abort() protection. */
          {
            IntPtr db;
            SQLiteErrorCode n;
  
  #if !SQLITE_STANDARD
            if ((connectionFlags & SQLiteConnectionFlags.NoExtensionFunctions) != SQLiteConnectionFlags.NoExtensionFunctions)
            {
              n = UnsafeNativeMethods.sqlite3_open16_interop(ToUTF8(strFilename), openFlags, out db);
            }
            else
  #endif
            {
              //
              // NOTE: This flag check is designed to enforce the constraint that opening
              //       a database file that does not already exist requires specifying the
              //       "Create" flag, even when a native API is used that does not accept
              //       a flags parameter.
              //
              if (((openFlags & SQLiteOpenFlagsEnum.Create) != SQLiteOpenFlagsEnum.Create) && !File.Exists(strFilename))
                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));
  #endif
  
            if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, null);
            _sql = new SQLiteConnectionHandle(db, true);
          }
          lock (_sql) { /* HACK: Force the SyncBlock to be "created" now. */ }
  
          SQLiteConnection.OnChanged(null, new ConnectionEventArgs(
              SQLiteConnectionEventType.NewCriticalHandle, null, null,
              null, null, _sql, strFilename, new object[] { strFilename,
              connectionFlags, openFlags, maxPoolSize, usePool }));
        }
  
        // Bind functions to this connection.  If any previous functions of the same name
        // were already bound, then the new bindings replace the old.
        if ((connectionFlags & SQLiteConnectionFlags.NoBindFunctions) != SQLiteConnectionFlags.NoBindFunctions)
        {
            if (_functions == null)
                _functions = new List<SQLiteFunction>();
  
            _functions.AddRange(new List<SQLiteFunction>(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)
      {
        if (_datetimeFormat == SQLiteDateFormats.Ticks)
          return ToDateTime(GetInt64(stmt, index), _datetimeKind);
  
gt:       else if (_datetimeFormat == SQLiteDateFormats.JulianDay)