diff --git a/qsqlite/src/qsql_sqlite.cpp b/qsqlite/src/qsql_sqlite.cpp index c1e9508..5da232f 100644 --- a/qsqlite/src/qsql_sqlite.cpp +++ b/qsqlite/src/qsql_sqlite.cpp @@ -528,7 +528,7 @@ static int qGetSqliteOpenMode(QString opts) return SQLITE_OPEN_READONLY; } // The SQLITE_OPEN_NOMUTEX flag causes the database connection to be in the multi-thread mode - return SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX; + return SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_SHAREDCACHE; } /* @@ -543,8 +543,10 @@ bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, c if (db.isEmpty()) return false; + sqlite3_enable_shared_cache(1); if (sqlite3_open_v2(db.toUtf8().constData(), &d->access, qGetSqliteOpenMode(conOpts), NULL) == SQLITE_OK) { sqlite3_busy_timeout(d->access, qGetSqliteTimeout(conOpts)); + sqlite3_extended_result_codes(d->access, 1); setOpen(true); setOpenError(false); return true; diff --git a/qsqlite/src/sqlite_blocking.cpp b/qsqlite/src/sqlite_blocking.cpp index c0fe3f2..180685c 100644 --- a/qsqlite/src/sqlite_blocking.cpp +++ b/qsqlite/src/sqlite_blocking.cpp @@ -1,63 +1,94 @@ +/* + Copyright (c) 2009 Bertjan Broeksema + Copyright (c) 2014 Daniel Vrátil + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + #include "sqlite_blocking.h" #include -#ifndef _WIN32 -#include -#else -#include -#define usleep(x) Sleep(x/1000) -#endif - -#include "qdebug.h" + +#include +#include #include "qstringbuilder.h" #include "qthread.h" +#include + +/* Based on example in http://www.sqlite.org/unlock_notify.html */ -QString debugString() +struct UnlockNotification { + bool fired; + QWaitCondition cond; + QMutex mutex; +}; + +static void qSqlite3UnlockNotifyCb(void **apArg, int nArg) { - return QString( QLatin1Literal("[QSQLITE3: ") + QString::number( quint64( QThread::currentThreadId() ) ) + QLatin1Literal("] ") ); + for (int i = 0; i < nArg; ++i) { + UnlockNotification *ntf = static_cast(apArg[i]); + ntf->mutex.lock(); + ntf->fired = true; + ntf->cond.wakeOne(); + ntf->mutex.unlock(); + } } -int sqlite3_blocking_step( sqlite3_stmt *pStmt ) +static int qSqlite3WaitForUnlockNotify(sqlite3 *db) { - // NOTE: The example at http://www.sqlite.org/unlock_notify.html says to wait - // for SQLITE_LOCK but for some reason I don't understand I get - // SQLITE_BUSY. - int rc = sqlite3_step( pStmt ); - - QThread::currentThreadId(); - if ( rc == SQLITE_BUSY ) - qDebug() << debugString() << "sqlite3_blocking_step: Entering while loop"; - - while( rc == SQLITE_BUSY ) { - usleep(5000); - sqlite3_reset( pStmt ); - rc = sqlite3_step( pStmt ); - - if ( rc != SQLITE_BUSY ) { - qDebug() << debugString() << "sqlite3_blocking_step: Leaving while loop"; + int rc; + UnlockNotification un; + un.fired = false; + + rc = sqlite3_unlock_notify(db, qSqlite3UnlockNotifyCb, (void *)&un); + Q_ASSERT(rc == SQLITE_LOCKED || rc == SQLITE_OK); + + if (rc == SQLITE_OK) { + un.mutex.lock(); + if (!un.fired) { + un.cond.wait(&un.mutex); } + un.mutex.unlock(); } return rc; } -int sqlite3_blocking_prepare16_v2( sqlite3 *db, /* Database handle. */ - const void *zSql, /* SQL statement, UTF-16 encoded */ - int nSql, /* Length of zSql in bytes. */ - sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ - const void **pzTail /* OUT: Pointer to unused portion of zSql */ ) +int sqlite3_blocking_step(sqlite3_stmt *pStmt) { - int rc = sqlite3_prepare16_v2( db, zSql, nSql, ppStmt, pzTail ); - - if ( rc == SQLITE_BUSY ) - qDebug() << debugString() << "sqlite3_blocking_prepare16_v2: Entering while loop"; + int rc; + while (SQLITE_LOCKED_SHAREDCACHE == (rc = sqlite3_step(pStmt))) { + rc = qSqlite3WaitForUnlockNotify(sqlite3_db_handle(pStmt)); + if (rc != SQLITE_OK) { + break; + } + sqlite3_reset(pStmt); + } - while( rc == SQLITE_BUSY ) { - usleep(500000); - rc = sqlite3_prepare16_v2( db, zSql, nSql, ppStmt, pzTail ); + return rc; +} - if ( rc != SQLITE_BUSY ) { - qDebug() << debugString() << "sqlite3_prepare16_v2: Leaving while loop"; +int sqlite3_blocking_prepare16_v2(sqlite3 *db, const void *zSql, int nSql, + sqlite3_stmt **ppStmt, const void **pzTail) +{ + int rc; + while (SQLITE_LOCKED_SHAREDCACHE == (rc = sqlite3_prepare16_v2(db, zSql, nSql, ppStmt, pzTail))) { + rc = qSqlite3WaitForUnlockNotify(db); + if (rc != SQLITE_OK) { + break; } } diff --git a/qsqlite/src/sqlite_blocking.h b/qsqlite/src/sqlite_blocking.h index 0d6f6a0..9f13946 100644 --- a/qsqlite/src/sqlite_blocking.h +++ b/qsqlite/src/sqlite_blocking.h @@ -1,3 +1,22 @@ +/* + Copyright (c) 2009 Bertjan Broeksema + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + #ifndef SQLITE_BLOCKING_H #define SQLITE_BLOCKING_H