Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Problem with SQLite Database and threads "Database is locked"

Problem with SQLite Database and threads "Database is locked"

Scheduled Pinned Locked Moved General and Desktop
threaddatabase
14 Posts 4 Posters 5.8k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • D Offline
    D Offline
    davidesalvetti
    wrote on 6 Jun 2019, 15:24 last edited by
    #1

    Hi all,

    I'm having a problem trying to manage my database in a multi-thread application.
    I have 3 different thread that access the same local SQLite database, one of them is the main thread.
    The main thread creat the connection to the database using the function:

        db = QSqlDatabase::addDatabase("QSQLITE");    //db is a global variable
        db.setDatabaseName(str + "/prove.db");
    

    while the other two threads create a clone of the db:

    db_2 = QSqlDatabase::cloneDatabase(db,"second");
    
    db_3 = QSqlDatabase::cloneDatabase(db,"third");
    

    In a cronological order, the first database (the one of the main thread) is created, then is created the second and the the third.
    I access to it from the third thread and then it gives me the problem when I try to acces with the second.

    The problem is that when I try to access using the second thread (but I don't know if it's a case that happans only with the second thread), I get the error

    "Unable to fetch row" " database is locked"
    

    Am I doing something wrong? The cloneDatabase method should be thread safe, isn't it?

    if you need more information don't hesitate to ask for them, please.
    If somebody has any hint it would be appreciated a lot.

    Thanks in advance

    K 1 Reply Last reply 7 Jun 2019, 07:03
    0
    • K Offline
      K Offline
      Kent-Dorfman
      wrote on 6 Jun 2019, 17:33 last edited by
      #2

      I believe sqlite concurrency only works for pure read operations. I think your clone operations are considered writes, even though they are to different databases.

      D 1 Reply Last reply 7 Jun 2019, 09:14
      1
      • S Offline
        S Offline
        SGaist
        Lifetime Qt Champion
        wrote on 6 Jun 2019, 18:39 last edited by
        #3

        Hi,

        How are you passing the main DB connection to your threads ?

        Interested in AI ? www.idiap.ch
        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

        D 1 Reply Last reply 7 Jun 2019, 09:12
        1
        • D davidesalvetti
          6 Jun 2019, 15:24

          Hi all,

          I'm having a problem trying to manage my database in a multi-thread application.
          I have 3 different thread that access the same local SQLite database, one of them is the main thread.
          The main thread creat the connection to the database using the function:

              db = QSqlDatabase::addDatabase("QSQLITE");    //db is a global variable
              db.setDatabaseName(str + "/prove.db");
          

          while the other two threads create a clone of the db:

          db_2 = QSqlDatabase::cloneDatabase(db,"second");
          
          db_3 = QSqlDatabase::cloneDatabase(db,"third");
          

          In a cronological order, the first database (the one of the main thread) is created, then is created the second and the the third.
          I access to it from the third thread and then it gives me the problem when I try to acces with the second.

          The problem is that when I try to access using the second thread (but I don't know if it's a case that happans only with the second thread), I get the error

          "Unable to fetch row" " database is locked"
          

          Am I doing something wrong? The cloneDatabase method should be thread safe, isn't it?

          if you need more information don't hesitate to ask for them, please.
          If somebody has any hint it would be appreciated a lot.

          Thanks in advance

          K Offline
          K Offline
          KroMignon
          wrote on 7 Jun 2019, 07:03 last edited by
          #4

          @davidesalvetti Hi, I don' remember where I found this, but since Qt 5.11, sharing the same connection between threads is not allowed.
          You have to create a QSqlDatabase for each thread. The easiest is to add the thread id to the connection name to avoid name collision.
          Something like this:

          QSqlDatabase MyClass::getDBConnection()
          {
              // Starting with Qt 5.11, sharing the same connection between threads is not allowed.
              // Use a dedicated connection for each thread requiring access to the database,
              // using the thread address as connection name.
          
              QSqlDatabase cnx;
          
              QString dbName = QStringLiteral("myConnection_%1").arg(qintptr(QThread::currentThreadId()), 0, 16);
              if(QSqlDatabase::contains(dbName))
              {
                  cnx = QSqlDatabase::database(dbName);
              }
              else
              {
                  cnx = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), dbName);
                  cnx.setDatabaseName(m_dbPath);
                  cnx.setConnectOptions("QSQLITE_BUSY_TIMEOUT=1000");
                  if (!cnx.isValid() || !cnx.open())
                  {
                      qDebug() << "DB connection creation error!";
                  }
              }
              return cnx;
          }
          
          

          It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

          D 1 Reply Last reply 7 Jun 2019, 09:16
          1
          • S SGaist
            6 Jun 2019, 18:39

            Hi,

            How are you passing the main DB connection to your threads ?

            D Offline
            D Offline
            davidesalvetti
            wrote on 7 Jun 2019, 09:12 last edited by
            #5

            @SGaist I have a global file that contains the declaration of the main DB, that is included in all the three threads.

            1 Reply Last reply
            0
            • K Kent-Dorfman
              6 Jun 2019, 17:33

              I believe sqlite concurrency only works for pure read operations. I think your clone operations are considered writes, even though they are to different databases.

              D Offline
              D Offline
              davidesalvetti
              wrote on 7 Jun 2019, 09:14 last edited by
              #6

              @Kent-Dorfman so what should I do to avoid this problem?

              1 Reply Last reply
              0
              • K KroMignon
                7 Jun 2019, 07:03

                @davidesalvetti Hi, I don' remember where I found this, but since Qt 5.11, sharing the same connection between threads is not allowed.
                You have to create a QSqlDatabase for each thread. The easiest is to add the thread id to the connection name to avoid name collision.
                Something like this:

                QSqlDatabase MyClass::getDBConnection()
                {
                    // Starting with Qt 5.11, sharing the same connection between threads is not allowed.
                    // Use a dedicated connection for each thread requiring access to the database,
                    // using the thread address as connection name.
                
                    QSqlDatabase cnx;
                
                    QString dbName = QStringLiteral("myConnection_%1").arg(qintptr(QThread::currentThreadId()), 0, 16);
                    if(QSqlDatabase::contains(dbName))
                    {
                        cnx = QSqlDatabase::database(dbName);
                    }
                    else
                    {
                        cnx = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), dbName);
                        cnx.setDatabaseName(m_dbPath);
                        cnx.setConnectOptions("QSQLITE_BUSY_TIMEOUT=1000");
                        if (!cnx.isValid() || !cnx.open())
                        {
                            qDebug() << "DB connection creation error!";
                        }
                    }
                    return cnx;
                }
                
                
                D Offline
                D Offline
                davidesalvetti
                wrote on 7 Jun 2019, 09:16 last edited by
                #7

                @KroMignon I didn't specified it but I'm using it 5.9.1 with MinGW 32 bit compiler, do you think it can be something that happens also in previous versions?

                K 1 Reply Last reply 7 Jun 2019, 09:23
                0
                • D davidesalvetti
                  7 Jun 2019, 09:16

                  @KroMignon I didn't specified it but I'm using it 5.9.1 with MinGW 32 bit compiler, do you think it can be something that happens also in previous versions?

                  K Offline
                  K Offline
                  KroMignon
                  wrote on 7 Jun 2019, 09:23 last edited by KroMignon 6 Jul 2019, 09:28
                  #8

                  @davidesalvetti I don't remember exactly what the problem is, but when QSqlDatabase() is not created in the same thread in which is it used, then something goes wrong internally.
                  To avoid this kind of issues, starting with Qt 5.11, Qt does not allow to used QSqlDatabase() is another thread as in own.

                  So yes, it is a bad practice to use same QSqlDatabase() in multiple threads.

                  Addendum: when cloning database with QSqlDatabase::cloneDatabase(), don't forget to call open(), as you can see in documentation:

                  Note: The new connection has not been opened. Before using the new connection, you must call open().

                  It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

                  D 1 Reply Last reply 7 Jun 2019, 09:40
                  2
                  • K KroMignon
                    7 Jun 2019, 09:23

                    @davidesalvetti I don't remember exactly what the problem is, but when QSqlDatabase() is not created in the same thread in which is it used, then something goes wrong internally.
                    To avoid this kind of issues, starting with Qt 5.11, Qt does not allow to used QSqlDatabase() is another thread as in own.

                    So yes, it is a bad practice to use same QSqlDatabase() in multiple threads.

                    Addendum: when cloning database with QSqlDatabase::cloneDatabase(), don't forget to call open(), as you can see in documentation:

                    Note: The new connection has not been opened. Before using the new connection, you must call open().

                    D Offline
                    D Offline
                    davidesalvetti
                    wrote on 7 Jun 2019, 09:40 last edited by
                    #9

                    @KroMignon thanks for your answer. Yes, I'm opening the connection and debugging I can see that it opens the it.

                    What I didn't understand quite well is:
                    I can't use the same QSqlDatabase() in different threads or I can't connect to the same database in different threads? I'm cloning the main QSqlDatabase so I have different instances of QSqlDatabase, each one on it's own thread, is this a problem? Or using different instances it's correct?

                    K 1 Reply Last reply 7 Jun 2019, 10:10
                    0
                    • D davidesalvetti
                      7 Jun 2019, 09:40

                      @KroMignon thanks for your answer. Yes, I'm opening the connection and debugging I can see that it opens the it.

                      What I didn't understand quite well is:
                      I can't use the same QSqlDatabase() in different threads or I can't connect to the same database in different threads? I'm cloning the main QSqlDatabase so I have different instances of QSqlDatabase, each one on it's own thread, is this a problem? Or using different instances it's correct?

                      K Offline
                      K Offline
                      KroMignon
                      wrote on 7 Jun 2019, 10:10 last edited by
                      #10

                      @davidesalvetti said in Problem with SQLite Database and threads "Database is locked":

                      I can't use the same QSqlDatabase() in different threads or I can't connect to the same database in different threads?

                      You can/should only use QSqlDatabase() in the thread in which it has been created.
                      All QSqlDatabase() created in main thread, must only be used in main thread. QSqlDatabase() must be create in the thread in which you want to use it.
                      This is why I've create a little function which create a new QSqlDatabase() when I need to dialog with database. So I am always sure the QSqlDatabase() I use is the right one.
                      You should never store locally in your class a QSqlDatabase() instance, only create one when you need it, and destroy it after. This is the recommended usage for QSqlDatabase().

                      Extract for QSqlDatabase documentation:

                      Warning: It is highly recommended that you do not keep a copy of the QSqlDatabase around as a member of a class, as this will prevent the instance from being correctly cleaned up on shutdown. If you need to access an existing QSqlDatabase, it should be accessed with database(). If you chose to have a QSqlDatabase member variable, this needs to be deleted before the QCoreApplication instance is deleted, otherwise it may lead to undefined behavior.

                      It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

                      D 1 Reply Last reply 10 Jun 2019, 13:43
                      3
                      • K KroMignon
                        7 Jun 2019, 10:10

                        @davidesalvetti said in Problem with SQLite Database and threads "Database is locked":

                        I can't use the same QSqlDatabase() in different threads or I can't connect to the same database in different threads?

                        You can/should only use QSqlDatabase() in the thread in which it has been created.
                        All QSqlDatabase() created in main thread, must only be used in main thread. QSqlDatabase() must be create in the thread in which you want to use it.
                        This is why I've create a little function which create a new QSqlDatabase() when I need to dialog with database. So I am always sure the QSqlDatabase() I use is the right one.
                        You should never store locally in your class a QSqlDatabase() instance, only create one when you need it, and destroy it after. This is the recommended usage for QSqlDatabase().

                        Extract for QSqlDatabase documentation:

                        Warning: It is highly recommended that you do not keep a copy of the QSqlDatabase around as a member of a class, as this will prevent the instance from being correctly cleaned up on shutdown. If you need to access an existing QSqlDatabase, it should be accessed with database(). If you chose to have a QSqlDatabase member variable, this needs to be deleted before the QCoreApplication instance is deleted, otherwise it may lead to undefined behavior.

                        D Offline
                        D Offline
                        davidesalvetti
                        wrote on 10 Jun 2019, 13:43 last edited by
                        #11

                        @KroMignon The problem is that I have created three different QsqlDatabase() in three different thread, and in every thread I use the QSqlDatabase() created in that thread. In this way it should work but it keeps giving me the problem. But I'll do more tests.

                        Anyway I found a workaround for my personal application, but maybe other people may be interested in a solution.

                        1 Reply Last reply
                        0
                        • S Offline
                          S Offline
                          SGaist
                          Lifetime Qt Champion
                          wrote on 10 Jun 2019, 14:18 last edited by
                          #12

                          What workaround is that ?

                          Can you show how you are creating your database object in these threads ?

                          Interested in AI ? www.idiap.ch
                          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                          D 1 Reply Last reply 12 Jun 2019, 12:33
                          0
                          • S SGaist
                            10 Jun 2019, 14:18

                            What workaround is that ?

                            Can you show how you are creating your database object in these threads ?

                            D Offline
                            D Offline
                            davidesalvetti
                            wrote on 12 Jun 2019, 12:33 last edited by
                            #13

                            @SGaist The workaround is good only for a few people. Since I always worked with two different threads (the main thread and another thread) that have access to the database, I'm just telling the second thread to do the things that the third thread should do with the database, but obviously is just a solution for my case.

                            this is the way I'm createing the database connection:

                            void T_Analysis::connectDB()
                            {
                                db_2 = QSqlDatabase::cloneDatabase(db,"second");    
                            
                                if(!db_2.open())
                                {
                                    qDebug() << "error";
                                }
                                else
                                {
                                    qDebug() << "okdb_2";
                                }
                            }
                            
                            void T_Usb::connectDB()
                            {
                                db_3 = QSqlDatabase::cloneDatabase(db,"third");    
                            
                                if(!db_3.open())
                                {
                                    qDebug() << "error";
                                }
                                else
                                {
                                    qDebug() << "okdb_3";
                                }
                            }
                            

                            Main thread:

                            void MainWindow::connect()
                            {
                                 db = QSqlDatabase::addDatabase("QSQLITE");
                                db.setDatabaseName("Prova.db");
                            
                                if (!db.open())
                                {
                                         //.... some code
                                }
                            
                            K 1 Reply Last reply 12 Jun 2019, 12:44
                            0
                            • D davidesalvetti
                              12 Jun 2019, 12:33

                              @SGaist The workaround is good only for a few people. Since I always worked with two different threads (the main thread and another thread) that have access to the database, I'm just telling the second thread to do the things that the third thread should do with the database, but obviously is just a solution for my case.

                              this is the way I'm createing the database connection:

                              void T_Analysis::connectDB()
                              {
                                  db_2 = QSqlDatabase::cloneDatabase(db,"second");    
                              
                                  if(!db_2.open())
                                  {
                                      qDebug() << "error";
                                  }
                                  else
                                  {
                                      qDebug() << "okdb_2";
                                  }
                              }
                              
                              void T_Usb::connectDB()
                              {
                                  db_3 = QSqlDatabase::cloneDatabase(db,"third");    
                              
                                  if(!db_3.open())
                                  {
                                      qDebug() << "error";
                                  }
                                  else
                                  {
                                      qDebug() << "okdb_3";
                                  }
                              }
                              

                              Main thread:

                              void MainWindow::connect()
                              {
                                   db = QSqlDatabase::addDatabase("QSQLITE");
                                  db.setDatabaseName("Prova.db");
                              
                                  if (!db.open())
                                  {
                                           //.... some code
                                  }
                              
                              K Offline
                              K Offline
                              KroMignon
                              wrote on 12 Jun 2019, 12:44 last edited by KroMignon 6 Dec 2019, 12:45
                              #14

                              @davidesalvetti Hmm, I am not very confident in your solution. I would create a helper class to create/use right connection according to current thread.
                              Something like this (it is just a skeleton, not sure it is working as it is):

                              #include <QSqlDatabase>
                              #include <QThread>
                              
                              class MyBDConnection
                              {
                                  QString m_dbPath;
                                  QString m_dbName;
                                  Q_DISABLE_COPY(MyBDConnection)
                              public:
                                  explicit MyBDConnection(const QString &sqlitePath, const QString &cnxName): 
                                                                                  m_dbPath(sqlitePath), m_dbName(cnxName) 
                                  {}
                              
                                  QSqlDatabase getDBConnection()
                                  {
                                      // Starting with Qt 5.11, sharing the same connection between threads is not allowed.
                                      // Use a dedicated connection for each thread requiring access to the database,
                                      // using the thread address as connection name.
                              
                                      QSqlDatabase cnx;
                              
                                      QString dbName = QStringLiteral("%1_%2").arg(m_dbName).arg(qintptr(QThread::currentThreadId()), 0, 16);
                                      if(QSqlDatabase::contains(dbName))
                                      {
                                          cnx = QSqlDatabase::database(dbName);
                                      }
                                      else
                                      {
                                          cnx = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), dbName);
                                          cnx.setDatabaseName(m_dbPath);
                                          if (!cnx.isValid() || !cnx.open())
                                          {
                                              qDebug() << "DB connection creation error!";
                                          }
                                      }
                                      return cnx;
                                  }
                              }
                              

                              And the only create on instance of this class and pass the pointer to each class which need connection to DB.

                              It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

                              1 Reply Last reply
                              0

                              8/14

                              7 Jun 2019, 09:23

                              • Login

                              • Login or register to search.
                              8 out of 14
                              • First post
                                8/14
                                Last post
                              0
                              • Categories
                              • Recent
                              • Tags
                              • Popular
                              • Users
                              • Groups
                              • Search
                              • Get Qt Extensions
                              • Unsolved