Changeset View
Changeset View
Standalone View
Standalone View
src/server/storage/querybuilder.cpp
Show All 13 Lines | 1 | /* | |||
---|---|---|---|---|---|
14 | You should have received a copy of the GNU Library General Public License | 14 | You should have received a copy of the GNU Library General Public License | ||
15 | along with this library; see the file COPYING.LIB. If not, write to the | 15 | along with this library; see the file COPYING.LIB. If not, write to the | ||
16 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 16 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
17 | 02110-1301, USA. | 17 | 02110-1301, USA. | ||
18 | */ | 18 | */ | ||
19 | 19 | | |||
20 | #include "querybuilder.h" | 20 | #include "querybuilder.h" | ||
21 | #include "akonadiserver_debug.h" | 21 | #include "akonadiserver_debug.h" | ||
22 | #include "dbexception.h" | ||||
22 | 23 | | |||
23 | #ifndef QUERYBUILDER_UNITTEST | 24 | #ifndef QUERYBUILDER_UNITTEST | ||
24 | #include "storage/datastore.h" | 25 | #include "storage/datastore.h" | ||
25 | #include "storage/querycache.h" | 26 | #include "storage/querycache.h" | ||
26 | #include "storage/storagedebugger.h" | 27 | #include "storage/storagedebugger.h" | ||
27 | #endif | 28 | #endif | ||
28 | 29 | | |||
29 | #include <shared/akranges.h> | 30 | #include <shared/akranges.h> | ||
▲ Show 20 Lines • Show All 311 Lines • ▼ Show 20 Line(s) | 341 | if (mDatabaseType == DbType::Sqlite) { | |||
341 | // SQLite does not support SELECT ... FOR UPDATE syntax, because it does | 342 | // SQLite does not support SELECT ... FOR UPDATE syntax, because it does | ||
342 | // table-level locking | 343 | // table-level locking | ||
343 | } else { | 344 | } else { | ||
344 | *statement += QLatin1Literal(" FOR UPDATE"); | 345 | *statement += QLatin1Literal(" FOR UPDATE"); | ||
345 | } | 346 | } | ||
346 | } | 347 | } | ||
347 | } | 348 | } | ||
348 | 349 | | |||
349 | bool QueryBuilder::retryLastTransaction(bool rollback) | | |||
350 | { | | |||
351 | #ifndef QUERYBUILDER_UNITTEST | | |||
352 | mQuery = DataStore::self()->retryLastTransaction(rollback); | | |||
353 | return !mQuery.lastError().isValid(); | | |||
354 | #else | | |||
355 | Q_UNUSED(rollback); | | |||
356 | return true; | | |||
357 | #endif | | |||
358 | } | | |||
359 | | ||||
360 | bool QueryBuilder::exec() | 350 | bool QueryBuilder::exec() | ||
361 | { | 351 | { | ||
362 | QString statement; | 352 | QString statement; | ||
363 | statement.reserve(1024); | 353 | statement.reserve(1024); | ||
364 | buildQuery(&statement); | 354 | buildQuery(&statement); | ||
365 | 355 | | |||
366 | #ifndef QUERYBUILDER_UNITTEST | 356 | #ifndef QUERYBUILDER_UNITTEST | ||
367 | auto query = QueryCache::query(statement); | 357 | auto query = QueryCache::query(statement); | ||
Show All 39 Lines | 396 | } else { | |||
407 | StorageDebugger::instance()->incSequence(); | 397 | StorageDebugger::instance()->incSequence(); | ||
408 | if (isBatch) { | 398 | if (isBatch) { | ||
409 | ret = mQuery.execBatch(); | 399 | ret = mQuery.execBatch(); | ||
410 | } else { | 400 | } else { | ||
411 | ret = mQuery.exec(); | 401 | ret = mQuery.exec(); | ||
412 | } | 402 | } | ||
413 | } | 403 | } | ||
414 | 404 | | |||
415 | // Add the query to DataStore so that we can replay it in case transaction deadlocks. | | |||
416 | // The method does nothing when this query is not executed within a transaction. | | |||
417 | // We don't care whether the query was successful or not. In case of error, the caller | | |||
418 | // will rollback the transaction anyway, and all cached queries will be removed. | | |||
419 | DataStore::self()->addQueryToTransaction(statement, mBindValues, isBatch); | | |||
420 | | ||||
421 | if (!ret) { | 405 | if (!ret) { | ||
406 | bool needsRetry = false; | ||||
422 | // Handle transaction deadlocks and timeouts by attempting to replay the transaction. | 407 | // Handle transaction deadlocks and timeouts by attempting to replay the transaction. | ||
423 | if (mDatabaseType == DbType::PostgreSQL) { | 408 | if (mDatabaseType == DbType::PostgreSQL) { | ||
424 | const QString dbError = mQuery.lastError().databaseText(); | 409 | const QString dbError = mQuery.lastError().databaseText(); | ||
425 | if (dbError.contains(QLatin1String("40P01" /* deadlock_detected */))) { | 410 | if (dbError.contains(QLatin1String("40P01" /* deadlock_detected */))) { | ||
426 | qCWarning(AKONADISERVER_LOG) << "QueryBuilder::exec(): database reported transaction deadlock, retrying transaction"; | 411 | qCWarning(AKONADISERVER_LOG) << "QueryBuilder::exec(): database reported transaction deadlock, retrying transaction"; | ||
427 | qCWarning(AKONADISERVER_LOG) << mQuery.lastError().text(); | 412 | qCWarning(AKONADISERVER_LOG) << mQuery.lastError().text(); | ||
428 | return retryLastTransaction(); | 413 | needsRetry = true; | ||
429 | } | 414 | } | ||
430 | } else if (mDatabaseType == DbType::MySQL) { | 415 | } else if (mDatabaseType == DbType::MySQL) { | ||
431 | const QString lastErrorStr = mQuery.lastError().nativeErrorCode(); | 416 | const QString lastErrorStr = mQuery.lastError().nativeErrorCode(); | ||
432 | const int error = lastErrorStr.isEmpty() ? -1 : lastErrorStr.toInt(); | 417 | const int error = lastErrorStr.isEmpty() ? -1 : lastErrorStr.toInt(); | ||
433 | if (error == 1213 /* ER_LOCK_DEADLOCK */) { | 418 | if (error == 1213 /* ER_LOCK_DEADLOCK */) { | ||
434 | qCWarning(AKONADISERVER_LOG) << "QueryBuilder::exec(): database reported transaction deadlock, retrying transaction"; | 419 | qCWarning(AKONADISERVER_LOG) << "QueryBuilder::exec(): database reported transaction deadlock, retrying transaction"; | ||
435 | qCWarning(AKONADISERVER_LOG) << mQuery.lastError().text(); | 420 | qCWarning(AKONADISERVER_LOG) << mQuery.lastError().text(); | ||
436 | return retryLastTransaction(); | 421 | needsRetry = true; | ||
437 | } else if (error == 1205 /* ER_LOCK_WAIT_TIMEOUT */) { | 422 | } else if (error == 1205 /* ER_LOCK_WAIT_TIMEOUT */) { | ||
438 | qCWarning(AKONADISERVER_LOG) << "QueryBuilder::exec(): database reported transaction timeout, retrying transaction"; | 423 | qCWarning(AKONADISERVER_LOG) << "QueryBuilder::exec(): database reported transaction timeout, retrying transaction"; | ||
439 | qCWarning(AKONADISERVER_LOG) << mQuery.lastError().text(); | 424 | qCWarning(AKONADISERVER_LOG) << mQuery.lastError().text(); | ||
440 | return retryLastTransaction(); | 425 | // Not sure retrying helps, maybe error is good enough.... but doesn't hurt to retry a few times before giving up. | ||
426 | needsRetry = true; | ||||
441 | } | 427 | } | ||
442 | } else if (mDatabaseType == DbType::Sqlite && !DbType::isSystemSQLite(DataStore::self()->database())) { | 428 | } else if (mDatabaseType == DbType::Sqlite && !DbType::isSystemSQLite(DataStore::self()->database())) { | ||
443 | const QString lastErrorStr = mQuery.lastError().nativeErrorCode(); | 429 | const QString lastErrorStr = mQuery.lastError().nativeErrorCode(); | ||
444 | const int error = lastErrorStr.isEmpty() ? -1 : lastErrorStr.toInt(); | 430 | const int error = lastErrorStr.isEmpty() ? -1 : lastErrorStr.toInt(); | ||
445 | if (error == 6 /* SQLITE_LOCKED */) { | 431 | if (error == 6 /* SQLITE_LOCKED */) { | ||
446 | qCWarning(AKONADISERVER_LOG) << "QueryBuilder::exec(): database reported transaction deadlock, retrying transaction"; | 432 | qCWarning(AKONADISERVER_LOG) << "QueryBuilder::exec(): database reported transaction deadlock, retrying transaction"; | ||
447 | qCWarning(AKONADISERVER_LOG) << mQuery.lastError().text(); | 433 | qCWarning(AKONADISERVER_LOG) << mQuery.lastError().text(); | ||
448 | return retryLastTransaction(true); | 434 | DataStore::self()->doRollback(); | ||
435 | needsRetry = true; | ||||
449 | } else if (error == 5 /* SQLITE_BUSY */) { | 436 | } else if (error == 5 /* SQLITE_BUSY */) { | ||
450 | qCWarning(AKONADISERVER_LOG) << "QueryBuilder::exec(): database reported transaction timeout, retrying transaction"; | 437 | qCWarning(AKONADISERVER_LOG) << "QueryBuilder::exec(): database reported transaction timeout, retrying transaction"; | ||
451 | qCWarning(AKONADISERVER_LOG) << mQuery.lastError().text(); | 438 | qCWarning(AKONADISERVER_LOG) << mQuery.lastError().text(); | ||
452 | return retryLastTransaction(true); | 439 | DataStore::self()->doRollback(); | ||
440 | needsRetry = true; | ||||
453 | } | 441 | } | ||
454 | } else if (mDatabaseType == DbType::Sqlite) { | 442 | } else if (mDatabaseType == DbType::Sqlite) { | ||
455 | // We can't have a transaction deadlock in SQLite when using driver shipped | 443 | // We can't have a transaction deadlock in SQLite when using driver shipped | ||
456 | // with Qt, because it does not support concurrent transactions and DataStore | 444 | // with Qt, because it does not support concurrent transactions and DataStore | ||
457 | // serializes them through a global lock. | 445 | // serializes them through a global lock. | ||
458 | } | 446 | } | ||
459 | 447 | | |||
448 | if (needsRetry) { | ||||
449 | DataStore::self()->transactionKilledByDB(); | ||||
450 | throw DbDeadlockException(mQuery); | ||||
451 | } | ||||
452 | | ||||
460 | qCCritical(AKONADISERVER_LOG) << "DATABASE ERROR:"; | 453 | qCCritical(AKONADISERVER_LOG) << "DATABASE ERROR:"; | ||
461 | qCCritical(AKONADISERVER_LOG) << " Error code:" << mQuery.lastError().nativeErrorCode(); | 454 | qCCritical(AKONADISERVER_LOG) << " Error code:" << mQuery.lastError().nativeErrorCode(); | ||
462 | qCCritical(AKONADISERVER_LOG) << " DB error: " << mQuery.lastError().databaseText(); | 455 | qCCritical(AKONADISERVER_LOG) << " DB error: " << mQuery.lastError().databaseText(); | ||
463 | qCCritical(AKONADISERVER_LOG) << " Error text:" << mQuery.lastError().text(); | 456 | qCCritical(AKONADISERVER_LOG) << " Error text:" << mQuery.lastError().text(); | ||
464 | qCCritical(AKONADISERVER_LOG) << " Values:" << mQuery.boundValues(); | 457 | qCCritical(AKONADISERVER_LOG) << " Values:" << mQuery.boundValues(); | ||
465 | qCCritical(AKONADISERVER_LOG) << " Query:" << statement; | 458 | qCCritical(AKONADISERVER_LOG) << " Query:" << statement; | ||
466 | return false; | 459 | return false; | ||
467 | } | 460 | } | ||
▲ Show 20 Lines • Show All 175 Lines • Show Last 20 Lines |