Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Microsoft.Data.Sqlite: Can't load extensions that modify existing functions or collations #26220

Closed
krazado opened this issue Sep 30, 2021 · 6 comments · Fixed by #28903
Closed
Assignees
Labels
area-adonet-sqlite closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported good first issue This issue should be relatively straightforward to fix. type-bug
Milestone

Comments

@krazado
Copy link

krazado commented Sep 30, 2021

LoadExtension method from SqliteConnection(Microsoft.Data.Sqlite) throws exception, but same method from SQLiteConnection(System.Data.SQLite) works fine.

I built a ICU extension and trying to load it with efcore, ef uses the SqliteConnection which is root reason of the problem, because I tried to create new SqliteConnection and same error is thrown, but as I stated out exactly same setup works fine with SQLiteConnection(System.Data.SQLite).

here is the code snippets, but I attached complete working project with both examples.

 //this is throwing exception 
        public static void LoadICUWithSqliteConnection(SqliteConnection sqliteConn)
        {
            try
            {                              
                if (sqliteConn.State == System.Data.ConnectionState.Closed)
                    sqliteConn.Open();

                if (sqliteConn != null
                    && File.Exists(ICUPath))
                {
                    sqliteConn.EnableExtensions(true);
                    sqliteConn.LoadExtension(ICUPath, "sqlite3_icu_init");
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

//this works like it should
        public static void LoadICUWithSQLiteConnection(SQLiteConnection sqliteConnection)
        {
            try
            {

                if (sqliteConnection != null
                    && sqliteConnection.State == System.Data.ConnectionState.Open
                    && File.Exists(ICUPath))
                {
                    sqliteConnection.EnableExtensions(true);
                    sqliteConnection.LoadExtension(ICUPath, "sqlite3_icu_init");
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
Microsoft.Data.Sqlite.SqliteException: 'SQLite Error 1: 'error during initialization: '.'

Include version information

Microsoft.Data.Sqlite version:5.0.10.0
Target framework: NET 5.0
Operating system: Windows 10

Unfortunately I could not upload .zip here, it says file is not supported, but here is the public link and you can download sample project: https://drive.google.com/file/d/1o7mXRxnNNFU1CFLokI106YTUQ6z4z5yC/view?usp=sharing

@ajcvickers
Copy link
Member

/cc @bricelam

@bricelam
Copy link
Contributor

bricelam commented Oct 6, 2021

Two native libraries (icuin69.dll and icuuc69d.dll) seem to be missing from the sample...

@krazado
Copy link
Author

krazado commented Oct 7, 2021

I added all missing dlls.
I have had included path from different location in the Path env variable and it was working for me

@bricelam
Copy link
Contributor

The load_extension() SQL function will fail if the extension attempts to modify or delete an SQL function or collating sequence. The extension can add new functions or collating sequences, but cannot modify or delete existing functions or collating sequences because those functions and/or collating sequences might be used elsewhere in the currently running SQL statement. To load an extension that changes or deletes functions or collating sequences, use the sqlite3_load_extension() C-language API.

@ericsink Would it be possible to add sqlite3_load_extension to SQLitePCLRaw?

@bricelam
Copy link
Contributor

Just for fun, here's the horrible C code I had to write to get to the bottom of this. 😬 Don't bother pointing out all the leaked memory; handling all those result codes was painful enough.

#include <cassert>
#include <cstdio>
#include "sqlite3.h"

int main()
{
    sqlite3* db;
    int rc = sqlite3_open(":memory:", &db);
    assert(rc == SQLITE_OK);

    rc = sqlite3_enable_load_extension(db, 1);
    assert(rc == SQLITE_OK);

    sqlite3_stmt* stmt;
    rc = sqlite3_prepare_v2(db, "SELECT load_extension('ICU_2017_V2', 'sqlite3_icu_init')\0", -1, &stmt, NULL);
    assert(rc == SQLITE_OK);

    rc = sqlite3_step(stmt);
    if (rc == SQLITE_ERROR)
    {
        printf(sqlite3_errmsg(db));
    }
}

@bricelam bricelam changed the title LoadExtension throws exception 'SQLite Error 1: 'error during initialization: ' Microsoft.Data.Sqlite: Can't load extensions that modify existing functions or collations Oct 21, 2021
@bricelam
Copy link
Contributor

bricelam commented Oct 21, 2021

Note, you can work around this limitation by writing your own P/Invoke code.

using System.Data;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Data.Sqlite;
using SQLitePCL;

using static SQLitePCL.raw;

static class SqliteConnectionExtensions
{
    public static void LoadExtension2(this SqliteConnection connection, string file, string proc)
    {
        if (connection.State != ConnectionState.Open)
            throw new InvalidOperationException("Connection must be open.");

        var rc = sqlite3_load_extension(
            connection.Handle!,
            ToUTF8(file),
            ToUTF8(proc),
            out var errMsg);

        if (rc != SQLITE_OK)
            throw new SqliteException(Marshal.PtrToStringUTF8(errMsg), rc);
    }

    [DllImport("e_sqlite3", CallingConvention = CallingConvention.Cdecl)]
    static extern int sqlite3_load_extension(sqlite3 db, byte[] zFile, byte[] zProc, out IntPtr errMsg);

    static byte[] ToUTF8(string value)
    {
        var length = Encoding.UTF8.GetByteCount(value);
        var result = new byte[length + 1];

        Encoding.UTF8.GetBytes(value, result);
        result[length] = 0;

        return result;
    }
}
sqliteConn.LoadExtension2(ICUPath, "sqlite3_icu_init");

@ajcvickers ajcvickers added this to the Backlog milestone Oct 22, 2021
@bricelam bricelam removed the blocked label May 2, 2022
@bricelam bricelam removed this from the Backlog milestone May 2, 2022
@ajcvickers ajcvickers added this to the 7.0.0 milestone May 6, 2022
@bricelam bricelam added the good first issue This issue should be relatively straightforward to fix. label Jun 8, 2022
bricelam added a commit to bricelam/efcore that referenced this issue Aug 26, 2022
@bricelam bricelam added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Aug 26, 2022
@bricelam bricelam modified the milestones: 7.0.0, 7.0.0-rc2 Aug 29, 2022
@ajcvickers ajcvickers modified the milestones: 7.0.0-rc2, 7.0.0 Nov 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-adonet-sqlite closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported good first issue This issue should be relatively straightforward to fix. type-bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants