System.Data.SQLite

Login
This project makes use of Eagle, provided by Mistachkin Systems.
Eagle: Secure Software Automation
Ticket Hash: b30dcd10afbe52050b6ab2ef07b90cb14608e9b3
Title: Error when loading an extension...
Status: Closed Type: Incident
Severity: Important Priority: Medium
Subsystem: Connection Resolution: Works_As_Designed
Last Modified: 2016-10-31 01:38:12
Version Found In: 1.0.103
User Comments:
anonymous added on 2016-09-15 15:36:03: (text/x-fossil-plain)
LoadExtension("SQLite.Interop.dll", "sqlite3_fts_init");

System.Data.SQLite.SQLiteException occurred
  HResult=-2147467259
  Message=SQL logic error or missing database
error during initialization:
  Source=System.Data.SQLite
  ErrorCode=1
  StackTrace:
       at System.Data.SQLite.SQLite3.LoadExtension(String fileName, String procName)
       at ApplicationServices.Factories.ConnectionDataFactory.<CreateConnectionAsync>d__9.MoveNext() in C:\Dropbox\SoftLAnd\Project\VS13\TOTKSilverlight\Goods\ApplicationServices\Factories\ConnectionDataFactory.cs:line 111
  InnerException:

anonymous added on 2016-09-15 15:43:26: (text/x-fossil-plain)
connection.Pooling = true;
recreateconnection

mistachkin added on 2016-09-15 19:52:13: (text/x-fossil-plain)
I'm a bit confused about this ticket.  First, when calling the LoadExtension
method, you will almost certainly need to pass the fully qualified path to the
"SQLite.Interop.dll" file.  Also, I have a couple questions:

1. Which extension are you trying to load?

2. Are you actually using connection pooling?

anonymous added on 2016-09-15 20:22:43: (text/x-fossil-plain)
Example defect code.
using System;
using System.Linq;
using System.Data.SQLite;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var cs = "data source=:memory:;pooling=True;";
            var path = Environment.Is64BitProcess ? "x64" : "x86";
            var dllFullFileName = Path.Combine(path, "SQLite.Interop.dll");
            foreach (var i in Enumerable.Range(0, 10))
            {
                using (var connection = new SQLiteConnection(cs))
                {
                    connection.Open();
                    connection.EnableExtensions(true);
                    connection.LoadExtension(dllFullFileName, "sqlite3_fts5_init");
                    connection.EnableExtensions(false);
                }
            }
        }
    }
}

mistachkin added on 2016-09-16 03:14:55: (text/x-fossil-plain)
Off hand, I can tell you that connection pooling does not interact well with the
load extension mechanism.  I think System.Data.SQLite prevents connections that
use extensions from entering (or re-entering) the pool.

mistachkin added on 2016-09-16 03:20:13: (text/x-fossil-plain)
Ok, I appear to be wrong.  It's CreateModule that disables the pool.

mistachkin added on 2016-09-16 03:37:40: (text/x-fossil-plain)
Thanks for the example code.  Reproduced the error message locally.

mistachkin added on 2016-09-16 03:44:41: (text/x-fossil-plain)
The root cause of the error is trying to load an extension into a connection that
already has it (i.e. due to it being from the pool).  To be specific:

1. Connection #1 is opened fresh.

2. Connection #1 loads the FTS5 extension.

3. Connection #1 is closed -AND- added to the connection pool.

4. Connection #2 is opened from the pool (with FTS5 already loaded).

5. An attempt is made to load the FTS5 extension into Connection #2.

6. Exception is thrown.

mistachkin added on 2016-09-16 03:47:45: (text/x-fossil-plain)
I see three workarounds to this issue:

1. Avoid using Pooling=True when you plan to load extensions.

2. Check if an extension is already loaded prior to calling LoadExtension
   (e.g. by trying to use something that it adds to the connection).

3. Wrap the LoadExtension calls in a try/catch and ignore exceptions (this is
   the most problematic workaround because LoadExtension can fail for reasons
   other than the extension already being loaded).