System.Data.SQLite
Check-in [187e8cb03d]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add UnbindFunction method to the SQLiteConnection class.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 187e8cb03dd8b88f8a6b017f97d39e44e36302c6
User & Date: mistachkin 2015-07-17 01:08:47
References
2015-10-15
17:20 Ticket [2556655d1b] SQLiteFunction.RegisterFunction ArgumentException status still Open with 6 other changes artifact: 357d23d7cc user: mistachkin
Context
2015-07-17
02:01
Add the UnbindFunctionsOnClose connection flag. check-in: 2a6efe65f7 user: mistachkin tags: trunk
01:08
Add UnbindFunction method to the SQLiteConnection class. check-in: 187e8cb03d user: mistachkin tags: trunk
00:54
Update SQLite core library to the latest trunk code. check-in: f7d0d964fb user: mistachkin tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to Doc/Extra/Provider/version.html.

    52     52         <li>Change the base type for the SQLiteConnectionFlags enumeration to long integer.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    53     53         <li>Add extended return codes to the SQLiteErrorCode enumeration. Pursuant to <a href="https://system.data.sqlite.org/index.html/info/71bedaca19">[71bedaca19]</a>.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    54     54         <li>Improve exception handling in all native callbacks implemented in the SQLiteConnection class.</li>
    55     55         <li>Add Progress event and ProgressOps connection string property to enable raising progress events during long-running queries.</li>
    56     56         <li>Add NoDefaultFlags connection string property to prevent the default connection flags from being used. Pursuant to <a href="https://system.data.sqlite.org/index.html/info/964063da16">[964063da16]</a>.</li>
    57     57         <li>Add VfsName connection string property to allow a non-default VFS to be used by the SQLite core library.</li>
    58     58         <li>Add BusyTimeout connection string property to set the busy timeout to be used by the SQLite core library.</li>
           59  +      <li>Add UnbindFunction method to the SQLiteConnection class.</li>
    59     60         <li>Enable integration with the <a href="http://www.hwaci.com/sw/sqlite/zipvfs.html">ZipVFS</a> extension.</li>
    60     61       </ul>
    61     62       <p><b>1.0.97.0 - May 26, 2015</b></p>
    62     63       <ul>
    63     64         <li>Updated to <a href="https://www.sqlite.org/releaselog/3_8_10_2.html">SQLite 3.8.10.2</a>.</li>
    64     65         <li>Updated to <a href="https://www.nuget.org/packages/EntityFramework/6.1.3">Entity Framework 6.1.3</a>.</li>
    65     66         <li>Improve ADO.NET conformance of the SQLiteDataReader.RecordsAffected property. Fix for <a href="https://system.data.sqlite.org/index.html/info/74542e702e">[74542e702e]</a>.&nbsp;<b>** Potentially Incompatible Change **</b></li>

Changes to System.Data.SQLite/SQLite3.cs.

    73     73   #if (NET_35 || NET_40 || NET_45 || NET_451) && !PLATFORM_COMPACTFRAMEWORK
    74     74       private bool _buildingSchema;
    75     75   #endif
    76     76   
    77     77       /// <summary>
    78     78       /// The user-defined functions registered on this connection
    79     79       /// </summary>
    80         -    protected List<SQLiteFunction> _functions;
           80  +    protected Dictionary<SQLiteFunctionAttribute, SQLiteFunction> _functions;
    81     81   
    82     82   #if INTEROP_VIRTUAL_TABLE
    83     83       /// <summary>
    84     84       /// The modules created using this connection.
    85     85       /// </summary>
    86     86       protected Dictionary<string, SQLiteModule> _modules;
    87     87   #endif
................................................................................
   343    343       /// </param>
   344    344       internal override void BindFunction(
   345    345           SQLiteFunctionAttribute functionAttribute,
   346    346           SQLiteFunction function,
   347    347           SQLiteConnectionFlags flags
   348    348           )
   349    349       {
          350  +        if (functionAttribute == null)
          351  +            throw new ArgumentNullException("functionAttribute");
          352  +
          353  +        if (function == null)
          354  +            throw new ArgumentNullException("function");
          355  +
   350    356           SQLiteFunction.BindFunction(this, functionAttribute, function, flags);
   351    357   
   352    358           if (_functions == null)
   353         -            _functions = new List<SQLiteFunction>();
          359  +            _functions = new Dictionary<SQLiteFunctionAttribute, SQLiteFunction>();
   354    360   
   355         -        _functions.Add(function);
          361  +        _functions[functionAttribute] = function;
          362  +    }
          363  +
          364  +    /// <summary>
          365  +    /// This function binds a user-defined function to the connection.
          366  +    /// </summary>
          367  +    /// <param name="functionAttribute">
          368  +    /// The <see cref="SQLiteFunctionAttribute"/> object instance containing
          369  +    /// the metadata for the function to be unbound.
          370  +    /// </param>
          371  +    /// <param name="flags">
          372  +    /// The flags associated with the parent connection object.
          373  +    /// </param>
          374  +    /// <returns>Non-zero if the function was unbound and removed.</returns>
          375  +    internal override bool UnbindFunction(
          376  +        SQLiteFunctionAttribute functionAttribute,
          377  +        SQLiteConnectionFlags flags
          378  +        )
          379  +    {
          380  +        if (functionAttribute == null)
          381  +            throw new ArgumentNullException("functionAttribute");
          382  +
          383  +        if (_functions == null)
          384  +            return false;
          385  +
          386  +        SQLiteFunction function;
          387  +
          388  +        if (_functions.TryGetValue(functionAttribute, out function))
          389  +        {
          390  +            if (SQLiteFunction.UnbindFunction(
          391  +                    this, functionAttribute, function, flags) &&
          392  +                _functions.Remove(functionAttribute))
          393  +            {
          394  +                return true;
          395  +            }
          396  +        }
          397  +
          398  +        return false;
   356    399       }
   357    400   
   358    401       internal override string Version
   359    402       {
   360    403         get
   361    404         {
   362    405           return SQLiteVersion;
................................................................................
   584    627           {
   585    628               if (_sql == null)
   586    629                   throw new SQLiteException("no connection handle available");
   587    630   
   588    631               return _sql.OwnHandle;
   589    632           }
   590    633       }
          634  +
          635  +    /// <summary>
          636  +    /// Returns the logical list of functions associated with this connection.
          637  +    /// </summary>
          638  +    internal override IDictionary<SQLiteFunctionAttribute, SQLiteFunction> Functions
          639  +    {
          640  +        get { return _functions; }
          641  +    }
   591    642   
   592    643       internal override SQLiteErrorCode SetMemoryStatus(bool value)
   593    644       {
   594    645           return StaticSetMemoryStatus(value);
   595    646       }
   596    647   
   597    648       internal static SQLiteErrorCode StaticSetMemoryStatus(bool value)
................................................................................
   816    867         }
   817    868   
   818    869         // Bind functions to this connection.  If any previous functions of the same name
   819    870         // were already bound, then the new bindings replace the old.
   820    871         if ((connectionFlags & SQLiteConnectionFlags.NoBindFunctions) != SQLiteConnectionFlags.NoBindFunctions)
   821    872         {
   822    873             if (_functions == null)
   823         -              _functions = new List<SQLiteFunction>();
          874  +              _functions = new Dictionary<SQLiteFunctionAttribute, SQLiteFunction>();
   824    875   
   825         -          _functions.AddRange(new List<SQLiteFunction>(SQLiteFunction.BindFunctions(this, connectionFlags)));
          876  +          foreach (KeyValuePair<SQLiteFunctionAttribute, SQLiteFunction> pair
          877  +                  in SQLiteFunction.BindFunctions(this, connectionFlags))
          878  +          {
          879  +              _functions[pair.Key] = pair.Value;
          880  +          }
   826    881         }
   827    882   
   828    883         SetTimeout(0);
   829    884         GC.KeepAlive(_sql);
   830    885       }
   831    886   
   832    887       internal override void ClearPool()
................................................................................
  1992   2047       }
  1993   2048   
  1994   2049       internal override int AggregateCount(IntPtr context)
  1995   2050       {
  1996   2051         return UnsafeNativeMethods.sqlite3_aggregate_count(context);
  1997   2052       }
  1998   2053   
  1999         -    internal override void CreateFunction(string strFunction, int nArgs, bool needCollSeq, SQLiteCallback func, SQLiteCallback funcstep, SQLiteFinalCallback funcfinal)
         2054  +    internal override SQLiteErrorCode CreateFunction(string strFunction, int nArgs, bool needCollSeq, SQLiteCallback func, SQLiteCallback funcstep, SQLiteFinalCallback funcfinal, bool canThrow)
  2000   2055       {
  2001   2056         SQLiteErrorCode n;
  2002   2057   
  2003   2058   #if !SQLITE_STANDARD
  2004   2059         n = UnsafeNativeMethods.sqlite3_create_function_interop(_sql, ToUTF8(strFunction), nArgs, 4, IntPtr.Zero, func, funcstep, funcfinal, (needCollSeq == true) ? 1 : 0);
  2005   2060         if (n == SQLiteErrorCode.Ok) n = UnsafeNativeMethods.sqlite3_create_function_interop(_sql, ToUTF8(strFunction), nArgs, 1, IntPtr.Zero, func, funcstep, funcfinal, (needCollSeq == true) ? 1 : 0);
  2006   2061   #else
  2007   2062         n = UnsafeNativeMethods.sqlite3_create_function(_sql, ToUTF8(strFunction), nArgs, 4, IntPtr.Zero, func, funcstep, funcfinal);
  2008   2063         if (n == SQLiteErrorCode.Ok) n = UnsafeNativeMethods.sqlite3_create_function(_sql, ToUTF8(strFunction), nArgs, 1, IntPtr.Zero, func, funcstep, funcfinal);
  2009   2064   #endif
  2010         -      if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
         2065  +      if (canThrow && (n != SQLiteErrorCode.Ok)) throw new SQLiteException(n, GetLastError());
         2066  +      return n;
  2011   2067       }
  2012   2068   
  2013         -    internal override void CreateCollation(string strCollation, SQLiteCollation func, SQLiteCollation func16)
         2069  +    internal override SQLiteErrorCode CreateCollation(string strCollation, SQLiteCollation func, SQLiteCollation func16, bool canThrow)
  2014   2070       {
  2015   2071         SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_create_collation(_sql, ToUTF8(strCollation), 2, IntPtr.Zero, func16);
  2016   2072         if (n == SQLiteErrorCode.Ok) n = UnsafeNativeMethods.sqlite3_create_collation(_sql, ToUTF8(strCollation), 1, IntPtr.Zero, func);
  2017         -      if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
         2073  +      if (canThrow && (n != SQLiteErrorCode.Ok)) throw new SQLiteException(n, GetLastError());
         2074  +      return n;
  2018   2075       }
  2019   2076   
  2020   2077       internal override int ContextCollateCompare(CollationEncodingEnum enc, IntPtr context, string s1, string s2)
  2021   2078       {
  2022   2079   #if !SQLITE_STANDARD
  2023   2080         byte[] b1;
  2024   2081         byte[] b2;

Changes to System.Data.SQLite/SQLite3_UTF16.cs.

   220    220         }
   221    221   
   222    222         // Bind functions to this connection.  If any previous functions of the same name
   223    223         // were already bound, then the new bindings replace the old.
   224    224         if ((connectionFlags & SQLiteConnectionFlags.NoBindFunctions) != SQLiteConnectionFlags.NoBindFunctions)
   225    225         {
   226    226             if (_functions == null)
   227         -              _functions = new List<SQLiteFunction>();
          227  +              _functions = new Dictionary<SQLiteFunctionAttribute, SQLiteFunction>();
   228    228   
   229         -          _functions.AddRange(new List<SQLiteFunction>(SQLiteFunction.BindFunctions(this, connectionFlags)));
          229  +          foreach (KeyValuePair<SQLiteFunctionAttribute, SQLiteFunction> pair
          230  +                  in SQLiteFunction.BindFunctions(this, connectionFlags))
          231  +          {
          232  +              _functions[pair.Key] = pair.Value;
          233  +          }
   230    234         }
   231    235   
   232    236         SetTimeout(0);
   233    237         GC.KeepAlive(_sql);
   234    238       }
   235    239   
   236    240       internal override void Bind_DateTime(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, DateTime dt)

Changes to System.Data.SQLite/SQLiteBase.cs.

     4      4    *
     5      5    * Released to the public domain, use at your own risk!
     6      6    ********************************************************/
     7      7   
     8      8   namespace System.Data.SQLite
     9      9   {
    10     10     using System;
           11  +  using System.Collections.Generic;
    11     12   
    12     13   #if !PLATFORM_COMPACTFRAMEWORK
    13     14     using System.Runtime.InteropServices;
    14     15   #endif
    15     16   
    16     17     /// <summary>
    17     18     /// This internal class provides the foundation of SQLite support.  It defines all the abstract members needed to implement
................................................................................
    59     60       /// </summary>
    60     61       internal abstract long MemoryHighwater { get; }
    61     62       /// <summary>
    62     63       /// Returns non-zero if the underlying native connection handle is owned by this instance.
    63     64       /// </summary>
    64     65       internal abstract bool OwnHandle { get; }
    65     66       /// <summary>
           67  +    /// Returns the logical list of functions associated with this connection.
           68  +    /// </summary>
           69  +    internal abstract IDictionary<SQLiteFunctionAttribute, SQLiteFunction> Functions { get; }
           70  +    /// <summary>
    66     71       /// Sets the status of the memory usage tracking subsystem in the SQLite core library.  By default, this is enabled.
    67     72       /// If this is disabled, memory usage tracking will not be performed.  This is not really a per-connection value, it is
    68     73       /// global to the process.
    69     74       /// </summary>
    70     75       /// <param name="value">Non-zero to enable memory usage tracking, zero otherwise.</param>
    71     76       /// <returns>A standard SQLite return code (i.e. zero for success and non-zero for failure).</returns>
    72     77       internal abstract SQLiteErrorCode SetMemoryStatus(bool value);
................................................................................
   179    184       /// <summary>
   180    185       /// Attempts to interrupt the query currently executing on the associated
   181    186       /// native database connection.
   182    187       /// </summary>
   183    188       internal abstract void Cancel();
   184    189   
   185    190       /// <summary>
   186         -    /// This function binds a user-defined functions to the connection.
          191  +    /// This function binds a user-defined function to the connection.
   187    192       /// </summary>
   188    193       /// <param name="functionAttribute">
   189    194       /// The <see cref="SQLiteFunctionAttribute"/> object instance containing
   190    195       /// the metadata for the function to be bound.
   191    196       /// </param>
   192    197       /// <param name="function">
   193    198       /// The <see cref="SQLiteFunction"/> object instance that implements the
................................................................................
   194    199       /// function to be bound.
   195    200       /// </param>
   196    201       /// <param name="flags">
   197    202       /// The flags associated with the parent connection object.
   198    203       /// </param>
   199    204       internal abstract void BindFunction(SQLiteFunctionAttribute functionAttribute, SQLiteFunction function, SQLiteConnectionFlags flags);
   200    205   
          206  +    /// <summary>
          207  +    /// This function unbinds a user-defined function from the connection.
          208  +    /// </summary>
          209  +    /// <param name="functionAttribute">
          210  +    /// The <see cref="SQLiteFunctionAttribute"/> object instance containing
          211  +    /// the metadata for the function to be unbound.
          212  +    /// </param>
          213  +    /// <param name="flags">
          214  +    /// The flags associated with the parent connection object.
          215  +    /// </param>
          216  +    /// <returns>Non-zero if the function was unbound.</returns>
          217  +    internal abstract bool UnbindFunction(SQLiteFunctionAttribute functionAttribute, SQLiteConnectionFlags flags);
          218  +
   201    219       internal abstract void Bind_Double(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, double value);
   202    220       internal abstract void Bind_Int32(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, Int32 value);
   203    221       internal abstract void Bind_UInt32(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, UInt32 value);
   204    222       internal abstract void Bind_Int64(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, Int64 value);
   205    223       internal abstract void Bind_UInt64(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, UInt64 value);
   206    224       internal abstract void Bind_Text(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, string value);
   207    225       internal abstract void Bind_Blob(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, byte[] blobData);
................................................................................
   234    252       internal abstract UInt64 GetUInt64(SQLiteStatement stmt, int index);
   235    253       internal abstract string GetText(SQLiteStatement stmt, int index);
   236    254       internal abstract long GetBytes(SQLiteStatement stmt, int index, int nDataoffset, byte[] bDest, int nStart, int nLength);
   237    255       internal abstract long GetChars(SQLiteStatement stmt, int index, int nDataoffset, char[] bDest, int nStart, int nLength);
   238    256       internal abstract DateTime GetDateTime(SQLiteStatement stmt, int index);
   239    257       internal abstract bool IsNull(SQLiteStatement stmt, int index);
   240    258   
   241         -    internal abstract void CreateCollation(string strCollation, SQLiteCollation func, SQLiteCollation func16);
   242         -    internal abstract void CreateFunction(string strFunction, int nArgs, bool needCollSeq, SQLiteCallback func, SQLiteCallback funcstep, SQLiteFinalCallback funcfinal);
          259  +    internal abstract SQLiteErrorCode CreateCollation(string strCollation, SQLiteCollation func, SQLiteCollation func16, bool @throw);
          260  +    internal abstract SQLiteErrorCode CreateFunction(string strFunction, int nArgs, bool needCollSeq, SQLiteCallback func, SQLiteCallback funcstep, SQLiteFinalCallback funcfinal, bool @throw);
   243    261       internal abstract CollationSequence GetCollationSequence(SQLiteFunction func, IntPtr context);
   244    262       internal abstract int ContextCollateCompare(CollationEncodingEnum enc, IntPtr context, string s1, string s2);
   245    263       internal abstract int ContextCollateCompare(CollationEncodingEnum enc, IntPtr context, char[] c1, char[] c2);
   246    264   
   247    265       internal abstract int AggregateCount(IntPtr context);
   248    266       internal abstract IntPtr AggregateContext(IntPtr context);
   249    267   

Changes to System.Data.SQLite/SQLiteConnection.cs.

  1316   1316   
  1317   1317           if (_sql == null)
  1318   1318               throw new InvalidOperationException(
  1319   1319                   "Database connection not valid for binding functions.");
  1320   1320   
  1321   1321           _sql.BindFunction(functionAttribute, function, _flags);
  1322   1322       }
         1323  +
         1324  +    ///////////////////////////////////////////////////////////////////////////////////////////////
         1325  +
         1326  +    /// <summary>
         1327  +    /// Attempts to unbind the specified <see cref="SQLiteFunction" /> object
         1328  +    /// instance to this connection.
         1329  +    /// </summary>
         1330  +    /// <param name="functionAttribute">
         1331  +    /// The <see cref="SQLiteFunctionAttribute"/> object instance containing
         1332  +    /// the metadata for the function to be unbound.
         1333  +    /// </param>
         1334  +    /// <returns>Non-zero if the function was unbound.</returns>
         1335  +    public bool UnbindFunction(
         1336  +        SQLiteFunctionAttribute functionAttribute
         1337  +        )
         1338  +    {
         1339  +        CheckDisposed();
         1340  +
         1341  +        if (_sql == null)
         1342  +            throw new InvalidOperationException(
         1343  +                "Database connection not valid for unbinding functions.");
         1344  +
         1345  +        return _sql.UnbindFunction(functionAttribute, _flags);
         1346  +    }
  1323   1347   
  1324   1348       ///////////////////////////////////////////////////////////////////////////////////////////////
  1325   1349   
  1326   1350       [Conditional("CHECK_STATE")]
  1327   1351       internal static void Check(SQLiteConnection connection)
  1328   1352       {
  1329   1353           if (connection == null)

Changes to System.Data.SQLite/SQLiteFunction.cs.

    75     75   
    76     76       /// <summary>
    77     77       /// Current context of the current callback.  Only valid during a callback
    78     78       /// </summary>
    79     79       internal IntPtr _context;
    80     80   
    81     81       /// <summary>
    82         -    /// This static list contains all the user-defined functions declared using the proper attributes.
           82  +    /// This static dictionary contains all the user-defined functions declared
           83  +    /// using the proper attributes.  The contained dictionary values are always
           84  +    /// null and are not currently used.
    83     85       /// </summary>
    84         -    private static List<SQLiteFunctionAttribute> _registeredFunctions;
           86  +    private static IDictionary<SQLiteFunctionAttribute, object> _registeredFunctions;
    85     87   
    86     88       /// <summary>
    87     89       /// Internal constructor, initializes the function's internal variables.
    88     90       /// </summary>
    89     91       protected SQLiteFunction()
    90     92       {
    91     93         _contextDataList = new Dictionary<IntPtr, AggregateData>();
................................................................................
   645    647       /// have a SQLiteFunctionAttribute attribute, and registering them accordingly.
   646    648       /// </summary>
   647    649   #if !PLATFORM_COMPACTFRAMEWORK
   648    650       [Security.Permissions.FileIOPermission(Security.Permissions.SecurityAction.Assert, AllFiles = Security.Permissions.FileIOPermissionAccess.PathDiscovery)]
   649    651   #endif
   650    652       static SQLiteFunction()
   651    653       {
   652         -      _registeredFunctions = new List<SQLiteFunctionAttribute>();
          654  +      _registeredFunctions = new Dictionary<SQLiteFunctionAttribute, object>();
   653    655         try
   654    656         {
   655    657   #if !PLATFORM_COMPACTFRAMEWORK
   656    658           //
   657    659           // NOTE: If the "No_SQLiteFunctions" environment variable is set,
   658    660           //       skip all our special code and simply return.
   659    661           //
................................................................................
   703    705               int u = arAtt.Length;
   704    706               for (int y = 0; y < u; y++)
   705    707               {
   706    708                 at = arAtt[y] as SQLiteFunctionAttribute;
   707    709                 if (at != null)
   708    710                 {
   709    711                   at.InstanceType = arTypes[x];
   710         -                _registeredFunctions.Add(at);
          712  +                _registeredFunctions.Add(at, null);
   711    713                 }
   712    714               }
   713    715             }
   714    716           }
   715    717   #endif
   716    718         }
   717    719         catch // SQLite provider can continue without being able to find built-in functions
................................................................................
   732    734   
   733    735         for (int y = 0; y < u; y++)
   734    736         {
   735    737           at = arAtt[y] as SQLiteFunctionAttribute;
   736    738           if (at != null)
   737    739           {
   738    740             at.InstanceType = typ;
   739         -          _registeredFunctions.Add(at);
          741  +          _registeredFunctions.Add(at, null);
   740    742           }
   741    743         }
   742    744       }
   743    745   
   744    746       /// <summary>
   745         -    /// Called by SQLiteBase derived classes, this function binds all user-defined functions to a connection.
          747  +    /// Called by SQLiteBase derived classes, this function binds all registered user-defined functions to a connection.
   746    748       /// It is done this way so that all user-defined functions will access the database using the same encoding scheme
   747    749       /// as the connection (UTF-8 or UTF-16).
   748    750       /// </summary>
   749    751       /// <remarks>
   750    752       /// The wrapper functions that interop with SQLite will create a unique cookie value, which internally is a pointer to
   751    753       /// all the wrapped callback functions.  The interop function uses it to map CDecl callbacks to StdCall callbacks.
   752    754       /// </remarks>
   753         -    /// <param name="sqlbase">The base object on which the functions are to bind</param>
   754         -    /// <param name="flags">The flags associated with the parent connection object</param>
          755  +    /// <param name="sqlbase">The base object on which the functions are to bind.</param>
          756  +    /// <param name="flags">The flags associated with the parent connection object.</param>
   755    757       /// <returns>Returns a logical list of functions which the connection should retain until it is closed.</returns>
   756         -    internal static IEnumerable<SQLiteFunction> BindFunctions(SQLiteBase sqlbase, SQLiteConnectionFlags flags)
          758  +    internal static IDictionary<SQLiteFunctionAttribute, SQLiteFunction> BindFunctions(
          759  +        SQLiteBase sqlbase,
          760  +        SQLiteConnectionFlags flags
          761  +        )
   757    762       {
   758         -        List<SQLiteFunction> lFunctions = new List<SQLiteFunction>();
          763  +        IDictionary<SQLiteFunctionAttribute, SQLiteFunction> lFunctions =
          764  +            new Dictionary<SQLiteFunctionAttribute, SQLiteFunction>();
   759    765   
   760         -        foreach (SQLiteFunctionAttribute pr in _registeredFunctions)
          766  +        foreach (KeyValuePair<SQLiteFunctionAttribute, object> pair
          767  +                in _registeredFunctions)
   761    768           {
   762         -            SQLiteFunction f = (SQLiteFunction)Activator.CreateInstance(pr.InstanceType);
          769  +            SQLiteFunctionAttribute pr = pair.Key;
          770  +
          771  +            if (pr == null)
          772  +                continue;
          773  +
          774  +            SQLiteFunction f = (SQLiteFunction)Activator.CreateInstance(
          775  +                pr.InstanceType);
          776  +
   763    777               BindFunction(sqlbase, pr, f, flags);
   764         -            lFunctions.Add(f);
          778  +            lFunctions[pr] = f;
   765    779           }
   766    780   
   767    781           return lFunctions;
   768    782       }
          783  +
          784  +    /// <summary>
          785  +    /// Called by SQLiteBase derived classes, this function unbinds all previously bound
          786  +    /// user-defined functions from a connection.
          787  +    /// </summary>
          788  +    /// <param name="sqlbase">The base object from which the functions are to be unbound.</param>
          789  +    /// <param name="flags">The flags associated with the parent connection object.</param>
          790  +    /// <returns>Non-zero if all previously bound user-defined functions were unbound.</returns>
          791  +    internal static bool UnbindFunctions(
          792  +        SQLiteBase sqlbase,
          793  +        SQLiteConnectionFlags flags
          794  +        )
          795  +    {
          796  +        if (sqlbase == null)
          797  +            return false;
          798  +
          799  +        IDictionary<SQLiteFunctionAttribute, SQLiteFunction> lFunctions =
          800  +            sqlbase.Functions;
          801  +
          802  +        if (lFunctions == null)
          803  +            return false;
          804  +
          805  +        bool result = true;
          806  +
          807  +        foreach (KeyValuePair<SQLiteFunctionAttribute, object> pair
          808  +                in _registeredFunctions)
          809  +        {
          810  +            SQLiteFunctionAttribute pr = pair.Key;
          811  +
          812  +            if (pr == null)
          813  +                continue;
          814  +
          815  +            SQLiteFunction f;
          816  +
          817  +            if (!lFunctions.TryGetValue(pr, out f) ||
          818  +                (f == null) ||
          819  +                !UnbindFunction(sqlbase, pr, f, flags))
          820  +            {
          821  +                result = false;
          822  +            }
          823  +        }
          824  +
          825  +        return result;
          826  +    }
   769    827   
   770    828       /// <summary>
   771    829       /// This function binds a user-defined functions to a connection.
   772    830       /// </summary>
   773    831       /// <param name="sqliteBase">
   774    832       /// The <see cref="SQLiteBase" /> object instance associated with the
   775    833       /// <see cref="SQLiteConnection" /> that the function should be bound to.
................................................................................
   826    884           if (functionType != FunctionType.Collation)
   827    885           {
   828    886               bool needCollSeq = (function is SQLiteFunctionEx);
   829    887   
   830    888               sqliteBase.CreateFunction(
   831    889                   name, functionAttribute.Arguments, needCollSeq,
   832    890                   function._InvokeFunc, function._StepFunc,
   833         -                function._FinalFunc);
          891  +                function._FinalFunc, true);
   834    892           }
   835    893           else
   836    894           {
   837    895               sqliteBase.CreateCollation(
   838         -                name, function._CompareFunc, function._CompareFunc16);
          896  +                name, function._CompareFunc, function._CompareFunc16,
          897  +                true);
          898  +        }
          899  +    }
          900  +
          901  +    /// <summary>
          902  +    /// This function unbinds a user-defined functions from a connection.
          903  +    /// </summary>
          904  +    /// <param name="sqliteBase">
          905  +    /// The <see cref="SQLiteBase" /> object instance associated with the
          906  +    /// <see cref="SQLiteConnection" /> that the function should be bound to.
          907  +    /// </param>
          908  +    /// <param name="functionAttribute">
          909  +    /// The <see cref="SQLiteFunctionAttribute"/> object instance containing
          910  +    /// the metadata for the function to be bound.
          911  +    /// </param>
          912  +    /// <param name="function">
          913  +    /// The <see cref="SQLiteFunction"/> object instance that implements the
          914  +    /// function to be bound.
          915  +    /// </param>
          916  +    /// <param name="flags">
          917  +    /// The flags associated with the parent connection object.
          918  +    /// </param>
          919  +    /// <returns>Non-zero if the function was unbound.</returns>
          920  +    internal static bool UnbindFunction(
          921  +        SQLiteBase sqliteBase,
          922  +        SQLiteFunctionAttribute functionAttribute,
          923  +        SQLiteFunction function,
          924  +        SQLiteConnectionFlags flags /* NOT USED */
          925  +        )
          926  +    {
          927  +        if (sqliteBase == null)
          928  +            throw new ArgumentNullException("sqliteBase");
          929  +
          930  +        if (functionAttribute == null)
          931  +            throw new ArgumentNullException("functionAttribute");
          932  +
          933  +        if (function == null)
          934  +            throw new ArgumentNullException("function");
          935  +
          936  +        FunctionType functionType = functionAttribute.FuncType;
          937  +        string name = functionAttribute.Name;
          938  +
          939  +        if (functionType != FunctionType.Collation)
          940  +        {
          941  +            bool needCollSeq = (function is SQLiteFunctionEx);
          942  +
          943  +            return sqliteBase.CreateFunction(
          944  +                name, functionAttribute.Arguments, needCollSeq,
          945  +                null, null, null, false) == SQLiteErrorCode.Ok;
          946  +        }
          947  +        else
          948  +        {
          949  +            return sqliteBase.CreateCollation(
          950  +                name, null, null, false) == SQLiteErrorCode.Ok;
   839    951           }
   840    952       }
   841    953     }
          954  +
          955  +
          956  +
   842    957   
   843    958     /// <summary>
   844    959     /// Extends SQLiteFunction and allows an inherited class to obtain the collating sequence associated with a function call.
   845    960     /// </summary>
   846    961     /// <remarks>
   847    962     /// User-defined functions can call the GetCollationSequence() method in this class and use it to compare strings and char arrays.
   848    963     /// </remarks>

Changes to Tests/basic.eagle.

  2528   2528   
  2529   2529     unset -nocomplain dateTime db fileName
  2530   2530   } -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
  2531   2531   System.Data.SQLite} -result {1 630874007980000000}}
  2532   2532   
  2533   2533   ###############################################################################
  2534   2534   
  2535         -runTest {test data-1.54 {bind SQLiteFunction to one SQLiteConnection} -setup {
         2535  +runTest {test data-1.54 {bind function to a SQLiteConnection} -setup {
  2536   2536     set fileName data-1.54.db
  2537   2537   } -body {
  2538   2538     set id [object invoke Interpreter.GetActive NextId]
  2539   2539     set dataSource [file join [getDatabaseDirectory] $fileName]
  2540   2540   
  2541   2541     set sql { \
  2542   2542       SELECT MyRandom(); \
................................................................................
  3277   3277     cleanupDb $fileName
  3278   3278   
  3279   3279     unset -nocomplain db fileName
  3280   3280   } -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
  3281   3281   System.Data.SQLite} -match regexp -result {^\{fts5: \d{4}-\d{2}-\d{2}\
  3282   3282   \d{2}:\d{2}:\d{2} [0-9a-f]{40}\} \{\} \{\} \{\} \{\} \{\} \{rowid 3 x horse\
  3283   3283   rowid 4 x house\}$}}
         3284  +
         3285  +###############################################################################
         3286  +
         3287  +runTest {test data-1.72 {unbind function from a SQLiteConnection} -setup {
         3288  +  set fileName data-1.72.db
         3289  +} -body {
         3290  +  set id [object invoke Interpreter.GetActive NextId]
         3291  +  set dataSource [file join [getDatabaseDirectory] $fileName]
         3292  +
         3293  +  set sql { \
         3294  +    SELECT MyRandom(); \
         3295  +  }
         3296  +
         3297  +  unset -nocomplain results errors
         3298  +
         3299  +  set code [compileCSharpWith [subst {
         3300  +    using System;
         3301  +    using System.Data.SQLite;
         3302  +
         3303  +    namespace _Dynamic${id}
         3304  +    {
         3305  +      public class Test${id} : SQLiteFunction
         3306  +      {
         3307  +        private Random random;
         3308  +        private static SQLiteFunctionAttribute functionAttribute;
         3309  +        private static SQLiteConnection connection;
         3310  +
         3311  +        ///////////////////////////////////////////////////////////////////////
         3312  +
         3313  +        public Test${id}()
         3314  +        {
         3315  +          random = new Random();
         3316  +        }
         3317  +
         3318  +        ///////////////////////////////////////////////////////////////////////
         3319  +
         3320  +        public override object Invoke(
         3321  +          object\[\] args
         3322  +          )
         3323  +        {
         3324  +          return random.Next();
         3325  +        }
         3326  +
         3327  +        ///////////////////////////////////////////////////////////////////////
         3328  +
         3329  +        private static void Initialize()
         3330  +        {
         3331  +          if (functionAttribute == null)
         3332  +          {
         3333  +            functionAttribute = new SQLiteFunctionAttribute(
         3334  +                "MyRandom", 0, FunctionType.Scalar);
         3335  +          }
         3336  +
         3337  +          if (connection == null)
         3338  +          {
         3339  +            connection = new SQLiteConnection(
         3340  +                "Data Source=${dataSource};[getFlagsProperty]");
         3341  +
         3342  +            connection.Open();
         3343  +          }
         3344  +        }
         3345  +
         3346  +        ///////////////////////////////////////////////////////////////////////
         3347  +
         3348  +        public static void BindFunction()
         3349  +        {
         3350  +          Initialize();
         3351  +
         3352  +          connection.BindFunction(functionAttribute, new Test${id}());
         3353  +        }
         3354  +
         3355  +        ///////////////////////////////////////////////////////////////////////
         3356  +
         3357  +        public static object CallFunction()
         3358  +        {
         3359  +          Initialize();
         3360  +
         3361  +          using (SQLiteCommand command = new SQLiteCommand("${sql}",
         3362  +              connection))
         3363  +          {
         3364  +            return command.ExecuteScalar();
         3365  +          }
         3366  +        }
         3367  +
         3368  +        ///////////////////////////////////////////////////////////////////////
         3369  +
         3370  +        public static bool UnbindFunction()
         3371  +        {
         3372  +          Initialize();
         3373  +
         3374  +          return connection.UnbindFunction(functionAttribute);
         3375  +        }
         3376  +
         3377  +        ///////////////////////////////////////////////////////////////////////
         3378  +
         3379  +        public static void Uninitialize()
         3380  +        {
         3381  +          if (connection != null)
         3382  +          {
         3383  +            connection.Close();
         3384  +            connection = null;
         3385  +          }
         3386  +
         3387  +          if (functionAttribute != null)
         3388  +            functionAttribute = null;
         3389  +        }
         3390  +
         3391  +        ///////////////////////////////////////////////////////////////////////
         3392  +
         3393  +        public static void Main()
         3394  +        {
         3395  +          // do nothing.
         3396  +        }
         3397  +      }
         3398  +    }
         3399  +  }] true true true results errors System.Data.SQLite.dll]
         3400  +
         3401  +  list $code $results \
         3402  +      [expr {[info exists errors] ? $errors : ""}] \
         3403  +      [expr {$code eq "Ok" ? [catch {
         3404  +        object invoke _Dynamic${id}.Test${id} BindFunction
         3405  +      } result] : [set result ""]}] $result \
         3406  +      [expr {$code eq "Ok" ? [catch {
         3407  +        object invoke _Dynamic${id}.Test${id} CallFunction
         3408  +      } result] : [set result ""]}] $result \
         3409  +      [expr {$code eq "Ok" ? [catch {
         3410  +        object invoke _Dynamic${id}.Test${id} UnbindFunction
         3411  +      } result] : [set result ""]}] $result \
         3412  +      [expr {$code eq "Ok" ? [catch {
         3413  +        object invoke _Dynamic${id}.Test${id} CallFunction
         3414  +      } result] : [set result ""]}] $result \
         3415  +      [expr {$code eq "Ok" ? [catch {
         3416  +        object invoke _Dynamic${id}.Test${id} BindFunction
         3417  +      } result] : [set result ""]}] $result \
         3418  +      [expr {$code eq "Ok" ? [catch {
         3419  +        object invoke _Dynamic${id}.Test${id} CallFunction
         3420  +      } result] : [set result ""]}] $result \
         3421  +      [expr {$code eq "Ok" ? [catch {
         3422  +        object invoke _Dynamic${id}.Test${id} Uninitialize
         3423  +      } result] : [set result ""]}] $result
         3424  +} -cleanup {
         3425  +  cleanupDb $fileName
         3426  +
         3427  +  unset -nocomplain result code results errors sql dataSource id fileName
         3428  +} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
         3429  +System.Data.SQLite compileCSharp} -match regexp -result \
         3430  +[string map [list \n \r\n] {^Ok System#CodeDom#Compiler#CompilerResults#\d+\
         3431  +\{\} 0 \{\} 0 (?:-)?\d+ 0 True 1\
         3432  +\{System\.Reflection\.TargetInvocationException: Exception has been thrown by\
         3433  +the target of an invocation\. ---> System\.Data\.SQLite\.SQLiteException: SQL\
         3434  +logic error or missing database
         3435  +no such function: MyRandom.*\} 0 \{\} 0 (?:-)?\d+ 0 \{\}$}]}
  3284   3436   
  3285   3437   ###############################################################################
  3286   3438   
  3287   3439   reportSQLiteResources $test_channel
  3288   3440   
  3289   3441   ###############################################################################
  3290   3442   
  3291   3443   runSQLiteTestFilesEpilogue
  3292   3444   runSQLiteTestEpilogue
  3293   3445   runTestEpilogue

Changes to readme.htm.

   219    219       <li>Change the base type for the SQLiteConnectionFlags enumeration to long integer.&nbsp;<b>** Potentially Incompatible Change **</b></li>
   220    220       <li>Add extended return codes to the SQLiteErrorCode enumeration. Pursuant to [71bedaca19].&nbsp;<b>** Potentially Incompatible Change **</b></li>
   221    221       <li>Improve exception handling in all native callbacks implemented in the SQLiteConnection class.</li>
   222    222       <li>Add Progress event and ProgressOps connection string property to enable raising progress events during long-running queries.</li>
   223    223       <li>Add NoDefaultFlags connection string property to prevent the default connection flags from being used. Pursuant to [964063da16].</li>
   224    224       <li>Add VfsName connection string property to allow a non-default VFS to be used by the SQLite core library.</li>
   225    225       <li>Add BusyTimeout connection string property to set the busy timeout to be used by the SQLite core library.</li>
          226  +    <li>Add UnbindFunction method to the SQLiteConnection class.</li>
   226    227       <li>Enable integration with the <a href="http://www.hwaci.com/sw/sqlite/zipvfs.html">ZipVFS</a> extension.</li>
   227    228   </ul>
   228    229   <p>
   229    230       <b>1.0.97.0 - May 26, 2015</b>
   230    231   </p>
   231    232   <ul>
   232    233       <li>Updated to <a href="https://www.sqlite.org/releaselog/3_8_10_2.html">SQLite 3.8.10.2</a>.</li>

Changes to www/news.wiki.

    13     13       <li>Change the base type for the SQLiteConnectionFlags enumeration to long integer.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    14     14       <li>Add extended return codes to the SQLiteErrorCode enumeration. Pursuant to [71bedaca19].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    15     15       <li>Improve exception handling in all native callbacks implemented in the SQLiteConnection class.</li>
    16     16       <li>Add Progress event and ProgressOps connection string property to enable raising progress events during long-running queries.</li>
    17     17       <li>Add NoDefaultFlags connection string property to prevent the default connection flags from being used. Pursuant to [964063da16].</li>
    18     18       <li>Add VfsName connection string property to allow a non-default VFS to be used by the SQLite core library.</li>
    19     19       <li>Add BusyTimeout connection string property to set the busy timeout to be used by the SQLite core library.</li>
           20  +    <li>Add UnbindFunction method to the SQLiteConnection class.</li>
    20     21       <li>Enable integration with the [http://www.hwaci.com/sw/sqlite/zipvfs.html|ZipVFS] extension.</li>
    21     22   </ul>
    22     23   <p>
    23     24       <b>1.0.97.0 - May 26, 2015</b>
    24     25   </p>
    25     26   <ul>
    26     27       <li>Updated to [https://www.sqlite.org/releaselog/3_8_10_2.html|SQLite 3.8.10.2].</li>