Could not load Spatialite extension in qSqlite ( QT 5.9)

三世轮回 提交于 2021-01-29 10:05:49

问题


I am trying to load Spatialite as extension in qSqlite ( Qt 5.9), I have done that before with Qt4.8, but I failed with the QT5.9. I changed the sqlite.pri by removing "SQLITE_OMIT_LOAD_EXTENSION", and I did some change on the sqlite.c by removing the "#define SQLITE_OMIT_LOAD_EXTENSION 1", and adding "#define SQLITE_ENABLE_LOAD_EXTENSION 1". I also add the following lines to openDatabase(....)

#if defined(SQLITE_ENABLE_LOAD_EXTENSION)
| SQLITE_LoadExtension|SQLITE_LoadExtFunc
#endif

Now the "requet.setQuery("SELECT load_extension('spatialite')", dbProject);" function is recognized, but I got this message: error "The specified procedure could not be found.\r\nUnable to fetch row" If I take a look on my debug output in MSVC14, I can see that the spatialite.dll and all its dependencies have been loaded.

Notice: I tested this with my Spatialite and also the mod_spatialite that I download form their website.

Any ideas about this problem? Thanks in advance.


回答1:


Based on the example here I have enabled spatialite in sqlite, the function enables that module. To do this you must link the sqlite3 library.

The modifications made are:

  • Change "SELECT load_extension('libspatialite.so')" to "SELECT load_extension('mod_spatialite')"

  • Change "SELECT InitSpatialMetadata()" to "SELECT InitSpatialMetadata(1)"


#include <sqlite3.h>
#include <QSqlDatabase>
#include <QSqlDriver>
#include <QSqlError>

int enable_spatialite(QSqlDatabase db){
    QVariant v = db.driver()->handle();
    if (v.isValid() && qstrcmp(v.typeName(), "sqlite3*")==0)
    {
        sqlite3_initialize();
        sqlite3 *db_handle = *static_cast<sqlite3 **>(v.data());

        if (db_handle != 0) {
            sqlite3_enable_load_extension(db_handle, 1);

            QSqlQuery query;

            query.exec("SELECT load_extension('mod_spatialite')");
            if (query.lastError() .isValid())
            {
                qDebug() << "Error: cannot load the Spatialite extension (" << query.lastError().text()<<")";
                return 0;
            }

            qDebug()<<"**** SpatiaLite loaded as an extension ***";

            query.exec("SELECT InitSpatialMetadata(1)");
            if (query.lastError() .isValid())
            {
                qDebug() << "Error: cannot load the Spatialite extension (" << query.lastError().text()<<")";
                return 0;
            }
            qDebug()<<"**** InitSpatialMetadata successful ***";

            return 1;
        }
    }
    return 0;
}

Example:

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QSqlDatabase db =  QSqlDatabase::addDatabase("QSQLITE");

    db.setDatabaseName("memory.db");
    if (!db.open()) {
        qDebug()<<"not open";
    }

    qDebug()<<enable_spatialite(db);

    QSqlQuery query;

    qDebug()<<query.exec("CREATE TABLE test_geom (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, measured_value DOUBLE NOT NULL);");
    qDebug()<<query.exec("SELECT AddGeometryColumn('test_geom', 'the_geom', 4326, 'POINT', 'XY');");



    for(int i=0; i< 10; i++){
        QString q = QString("INSERT INTO test_geom(id, name, measured_value, the_geom) VALUES (%1,'point %2', %3, GeomFromText('POINT(1.01 2.02)', 4326))")
                .arg("NULL").arg(i).arg(i);
        query.prepare(q);
        qDebug()<< i<<query.exec();
    }

    qDebug()<<query.exec("SELECT id, name, measured_value,  AsText(the_geom), ST_GeometryType(the_geom), ST_Srid(the_geom) FROM test_geom");


    while (query.next()) {
        QString str;
        for(int i=0; i < query.record().count(); i++)
            str += query.value(i).toString() + " ";
        qDebug()<<str;
    }
    return a.exec();
}

Output:

**** SpatiaLite loaded as an extension ***
**** InitSpatialMetadata successful ***
1
true
true
0 true
1 true
2 true
3 true
4 true
5 true
6 true
7 true
8 true
9 true
true
"1 point 0 0 POINT(1.01 2.02) POINT 4326 "
"2 point 1 1 POINT(1.01 2.02) POINT 4326 "
"3 point 2 2 POINT(1.01 2.02) POINT 4326 "
"4 point 3 3 POINT(1.01 2.02) POINT 4326 "
"5 point 4 4 POINT(1.01 2.02) POINT 4326 "
"6 point 5 5 POINT(1.01 2.02) POINT 4326 "
"7 point 6 6 POINT(1.01 2.02) POINT 4326 "
"8 point 7 7 POINT(1.01 2.02) POINT 4326 "
"9 point 8 8 POINT(1.01 2.02) POINT 4326 "
"10 point 9 9 POINT(1.01 2.02) POINT 4326 "

The complete example can be found here.

This code has been tested on linux Arch Linux 4.11.3-1-ARCH, Qt 5.8




回答2:


Thanks for eyllanesc, I found the solution thanks to your explanations and suggestions.

The problem was with my compiled library (still i don't know why?), and also with the mod_spatialite that I downloaded. This last one when we use it with the visual studio need a replacement for the libstdc++_64-6.dll because it will cause crash.

My problem was here, the one that I used was not the good one, and was causing the The specified procedure could not be found, so I downloaded the x86_64-5.3.0-release-win32-seh-rt_v4-rev0 and I used the libstdc++-6.dll (I changed the name to libstdc++_64-6.dll) with the libgcc_s_seh-1.dll. I also changed the libxml2-2.dll with another one that I compiled before.

Then I used one of the following solutions:

Solution 1: I used the code before with modification because the ret = sqlite3_exec (db_handle, sql, NULL, NULL, &err_msg); was causing crash, and also I sent the db via parameters because it does not work like it was in the previous code.

so my working code now is:

#include <QtCore/QCoreApplication>
#include <QtSql/QtSql>
#include "sqlite3.h"

int enable_spatialite(QSqlDatabase db) {
    QVariant v = db.driver()->handle();
    if (v.isValid() && qstrcmp(v.typeName(), "sqlite3*") == 0)
    {
        sqlite3_initialize();
        sqlite3 *db_handle = *static_cast<sqlite3 **>(v.data());

        if (db_handle != 0) {

            sqlite3_enable_load_extension(db_handle, 1);

            QSqlQueryModel sql;
            sql.setQuery("SELECT load_extension('mod_spatialite')", db);
            if (sql.lastError().isValid())
            {
                qDebug() << "Error: cannot load the Spatialite extension (" << sql.lastError().text()<<")";
                return 0;
            }
            else    return 1;
        }
    }
    return 0;
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "Project");
    db.setDatabaseName("dbTest.db");
    if (!db.open())
    {
        qDebug()<<"Critical"<< "Impossible to intialize the database !\n" + db.lastError().text();
        return 0;
    }
    qDebug()<<enable_spatialite(db);

    //just a test
    QSqlQueryModel sql;
    sql.setQuery("SELECT HEX(GeomFromText('POINT(10 20)'));", db);
    qDebug() << sql.index(0, 0).data().toString();

    return a.exec();
}

one important things also, is that I recompiled the qsqlite driver to remove the SQLITE_OMIT_LOAD_EXTENSION

Solution 2: (Direct solution)

open the "Qt5.9.0\5.9\Src\qtbase\src\3rdparty\sqlite\" folder and change the sqlite3.c as following:

  1. comment or remove the #define SQLITE_OMIT_LOAD_EXTENSION 1

  2. add:


#ifndef SQLITE_ENABLE_LOAD_EXTENSION
#define SQLITE_ENABLE_LOAD_EXTENSION 1
#endif 
  1. go the static int openDatabase( const char *zFilename,sqlite3 **ppDb, unsigned int flags, const char *zVfs) function and add | SQLITE_LoadExtFunc to SQLITE_ENABLE_LOAD_EXTENSION as following:

#ifdef SQLITE_ENABLE_LOAD_EXTENSION
                 | SQLITE_LoadExtension | SQLITE_LoadExtFunc
#endif 
  1. compile your plugin again using your compiler ( nmake in my case) in Qt5.9.0\5.9\Src\qtbase\src\plugins\sqldrivers\sqlite\sqlite.pro

  2. call the following code to load your spatialite:


QSqlQueryModel sql;
sql.setQuery("SELECT load_extension('mod_spatialite')", db); 


来源:https://stackoverflow.com/questions/61013620/building-spatialite-into-qt-qsqldatabase-driver

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!