2 * Copyright (C) 2013 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "modules/webdatabase/Database.h"
29 #include "core/dom/CrossThreadTask.h"
30 #include "core/dom/ExceptionCode.h"
31 #include "core/html/VoidCallback.h"
32 #include "modules/webdatabase/ChangeVersionData.h"
33 #include "modules/webdatabase/ChangeVersionWrapper.h"
34 #include "modules/webdatabase/DatabaseAuthorizer.h"
35 #include "modules/webdatabase/DatabaseContext.h"
36 #include "modules/webdatabase/DatabaseManager.h"
37 #include "modules/webdatabase/DatabaseTask.h"
38 #include "modules/webdatabase/DatabaseThread.h"
39 #include "modules/webdatabase/DatabaseTracker.h"
40 #include "modules/webdatabase/SQLError.h"
41 #include "modules/webdatabase/SQLTransaction.h"
42 #include "modules/webdatabase/SQLTransactionBackend.h"
43 #include "modules/webdatabase/SQLTransactionCallback.h"
44 #include "modules/webdatabase/SQLTransactionClient.h"
45 #include "modules/webdatabase/SQLTransactionCoordinator.h"
46 #include "modules/webdatabase/SQLTransactionErrorCallback.h"
47 #include "modules/webdatabase/sqlite/SQLiteStatement.h"
48 #include "modules/webdatabase/sqlite/SQLiteTransaction.h"
49 #include "platform/Logging.h"
50 #include "platform/weborigin/DatabaseIdentifier.h"
51 #include "public/platform/Platform.h"
52 #include "public/platform/WebDatabaseObserver.h"
54 // Registering "opened" databases with the DatabaseTracker
55 // =======================================================
56 // The DatabaseTracker maintains a list of databases that have been
57 // "opened" so that the client can call interrupt or delete on every database
58 // associated with a DatabaseContext.
60 // We will only call DatabaseTracker::addOpenDatabase() to add the database
61 // to the tracker as opened when we've succeeded in opening the database,
62 // and will set m_opened to true. Similarly, we only call
63 // DatabaseTracker::removeOpenDatabase() to remove the database from the
64 // tracker when we set m_opened to false in closeDatabase(). This sets up
65 // a simple symmetry between open and close operations, and a direct
66 // correlation to adding and removing databases from the tracker's list,
67 // thus ensuring that we have a correct list for the interrupt and
68 // delete operations to work on.
70 // The only databases instances not tracked by the tracker's open database
71 // list are the ones that have not been added yet, or the ones that we
72 // attempted an open on but failed to. Such instances only exist in the
73 // DatabaseServer's factory methods for creating database backends.
75 // The factory methods will either call openAndVerifyVersion() or
76 // performOpenAndVerify(). These methods will add the newly instantiated
77 // database backend if they succeed in opening the requested database.
78 // In the case of failure to open the database, the factory methods will
79 // simply discard the newly instantiated database backend when they return.
80 // The ref counting mechanims will automatically destruct the un-added
81 // (and un-returned) databases instances.
85 static const char versionKey[] = "WebKitDatabaseVersionKey";
86 static const char infoTableName[] = "__WebKitDatabaseInfoTable__";
88 static String formatErrorMessage(const char* message, int sqliteErrorCode, const char* sqliteErrorMessage)
90 return String::format("%s (%d %s)", message, sqliteErrorCode, sqliteErrorMessage);
93 static bool retrieveTextResultFromDatabase(SQLiteDatabase& db, const String& query, String& resultString)
95 SQLiteStatement statement(db, query);
96 int result = statement.prepare();
98 if (result != SQLResultOk) {
99 WTF_LOG_ERROR("Error (%i) preparing statement to read text result from database (%s)", result, query.ascii().data());
103 result = statement.step();
104 if (result == SQLResultRow) {
105 resultString = statement.getColumnText(0);
108 if (result == SQLResultDone) {
109 resultString = String();
113 WTF_LOG_ERROR("Error (%i) reading text result from database (%s)", result, query.ascii().data());
117 static bool setTextValueInDatabase(SQLiteDatabase& db, const String& query, const String& value)
119 SQLiteStatement statement(db, query);
120 int result = statement.prepare();
122 if (result != SQLResultOk) {
123 WTF_LOG_ERROR("Failed to prepare statement to set value in database (%s)", query.ascii().data());
127 statement.bindText(1, value);
129 result = statement.step();
130 if (result != SQLResultDone) {
131 WTF_LOG_ERROR("Failed to step statement to set value in database (%s)", query.ascii().data());
138 // FIXME: move all guid-related functions to a DatabaseVersionTracker class.
139 static RecursiveMutex& guidMutex()
141 AtomicallyInitializedStatic(RecursiveMutex&, mutex = *new RecursiveMutex);
145 typedef HashMap<DatabaseGuid, String> GuidVersionMap;
146 static GuidVersionMap& guidToVersionMap()
148 // Ensure the the mutex is locked.
149 ASSERT(guidMutex().locked());
150 DEFINE_STATIC_LOCAL(GuidVersionMap, map, ());
154 // NOTE: Caller must lock guidMutex().
155 static inline void updateGuidVersionMap(DatabaseGuid guid, String newVersion)
157 // Ensure the the mutex is locked.
158 ASSERT(guidMutex().locked());
160 // Note: It is not safe to put an empty string into the guidToVersionMap()
161 // map. That's because the map is cross-thread, but empty strings are
162 // per-thread. The copy() function makes a version of the string you can
163 // use on the current thread, but we need a string we can keep in a
164 // cross-thread data structure.
165 // FIXME: This is a quite-awkward restriction to have to program with.
167 // Map null string to empty string (see comment above).
168 guidToVersionMap().set(guid, newVersion.isEmpty() ? String() : newVersion.isolatedCopy());
171 typedef HashMap<DatabaseGuid, HashSet<Database*>*> GuidDatabaseMap;
172 static GuidDatabaseMap& guidToDatabaseMap()
174 // Ensure the the mutex is locked.
175 ASSERT(guidMutex().locked());
176 DEFINE_STATIC_LOCAL(GuidDatabaseMap, map, ());
180 static DatabaseGuid guidForOriginAndName(const String& origin, const String& name)
182 // Ensure the the mutex is locked.
183 ASSERT(guidMutex().locked());
185 String stringID = origin + "/" + name;
187 typedef HashMap<String, int> IDGuidMap;
188 DEFINE_STATIC_LOCAL(IDGuidMap, stringIdentifierToGUIDMap, ());
189 DatabaseGuid guid = stringIdentifierToGUIDMap.get(stringID);
191 static int currentNewGUID = 1;
192 guid = currentNewGUID++;
193 stringIdentifierToGUIDMap.set(stringID, guid);
199 Database::Database(DatabaseContext* databaseContext, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize)
200 : m_databaseContext(databaseContext)
201 , m_name(name.isolatedCopy())
202 , m_expectedVersion(expectedVersion.isolatedCopy())
203 , m_displayName(displayName.isolatedCopy())
204 , m_estimatedSize(estimatedSize)
208 , m_transactionInProgress(false)
209 , m_isTransactionQueueEnabled(true)
211 m_contextThreadSecurityOrigin = m_databaseContext->securityOrigin()->isolatedCopy();
213 m_databaseAuthorizer = DatabaseAuthorizer::create(infoTableName);
219 SafePointAwareMutexLocker locker(guidMutex());
220 m_guid = guidForOriginAndName(securityOrigin()->toString(), name);
221 HashSet<Database*>* hashSet = guidToDatabaseMap().get(m_guid);
223 hashSet = new HashSet<Database*>;
224 guidToDatabaseMap().set(m_guid, hashSet);
230 m_filename = DatabaseManager::manager().fullPathForDatabase(securityOrigin(), m_name);
232 m_databaseThreadSecurityOrigin = m_contextThreadSecurityOrigin->isolatedCopy();
233 ASSERT(m_databaseContext->databaseThread());
234 ASSERT(m_databaseContext->isContextThread());
237 Database::~Database()
239 // SQLite is "multi-thread safe", but each database handle can only be used
240 // on a single thread at a time.
242 // For Database, we open the SQLite database on the DatabaseThread, and
243 // hence we should also close it on that same thread. This means that the
244 // SQLite database need to be closed by another mechanism (see
245 // DatabaseContext::stopDatabases()). By the time we get here, the SQLite
246 // database should have already been closed.
251 void Database::trace(Visitor* visitor)
253 visitor->trace(m_databaseContext);
254 visitor->trace(m_sqliteDatabase);
255 visitor->trace(m_databaseAuthorizer);
256 visitor->trace(m_transactionQueue);
259 bool Database::openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage)
261 TaskSynchronizer synchronizer;
262 if (!databaseContext()->databaseThreadAvailable())
265 DatabaseTracker::tracker().prepareToOpenDatabase(this);
266 bool success = false;
267 OwnPtr<DatabaseOpenTask> task = DatabaseOpenTask::create(this, setVersionInNewDatabase, &synchronizer, error, errorMessage, success);
268 databaseContext()->databaseThread()->scheduleTask(task.release());
269 synchronizer.waitForTaskCompletion();
274 void Database::close()
276 ASSERT(databaseContext()->databaseThread());
277 ASSERT(databaseContext()->databaseThread()->isDatabaseThread());
280 MutexLocker locker(m_transactionInProgressMutex);
282 // Clean up transactions that have not been scheduled yet:
283 // Transaction phase 1 cleanup. See comment on "What happens if a
284 // transaction is interrupted?" at the top of SQLTransactionBackend.cpp.
285 SQLTransactionBackend* transaction = nullptr;
286 while (!m_transactionQueue.isEmpty()) {
287 transaction = m_transactionQueue.takeFirst();
288 transaction->notifyDatabaseThreadIsShuttingDown();
291 m_isTransactionQueueEnabled = false;
292 m_transactionInProgress = false;
296 databaseContext()->databaseThread()->recordDatabaseClosed(this);
299 SQLTransactionBackend* Database::runTransaction(SQLTransaction* transaction, bool readOnly, const ChangeVersionData* data)
301 MutexLocker locker(m_transactionInProgressMutex);
302 if (!m_isTransactionQueueEnabled)
305 SQLTransactionWrapper* wrapper = nullptr;
307 wrapper = ChangeVersionWrapper::create(data->oldVersion(), data->newVersion());
309 SQLTransactionBackend* transactionBackend = SQLTransactionBackend::create(this, transaction, wrapper, readOnly);
310 m_transactionQueue.append(transactionBackend);
311 if (!m_transactionInProgress)
312 scheduleTransaction();
314 return transactionBackend;
317 void Database::inProgressTransactionCompleted()
319 MutexLocker locker(m_transactionInProgressMutex);
320 m_transactionInProgress = false;
321 scheduleTransaction();
324 void Database::scheduleTransaction()
326 ASSERT(!m_transactionInProgressMutex.tryLock()); // Locked by caller.
327 SQLTransactionBackend* transaction = nullptr;
329 if (m_isTransactionQueueEnabled && !m_transactionQueue.isEmpty())
330 transaction = m_transactionQueue.takeFirst();
332 if (transaction && databaseContext()->databaseThreadAvailable()) {
333 OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
334 WTF_LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for transaction %p\n", task.get(), task->transaction());
335 m_transactionInProgress = true;
336 databaseContext()->databaseThread()->scheduleTask(task.release());
338 m_transactionInProgress = false;
342 void Database::scheduleTransactionStep(SQLTransactionBackend* transaction)
344 if (!databaseContext()->databaseThreadAvailable())
347 OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
348 WTF_LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task.get());
349 databaseContext()->databaseThread()->scheduleTask(task.release());
352 SQLTransactionClient* Database::transactionClient() const
354 return databaseContext()->databaseThread()->transactionClient();
357 SQLTransactionCoordinator* Database::transactionCoordinator() const
359 return databaseContext()->databaseThread()->transactionCoordinator();
363 const char* Database::databaseInfoTableName()
365 return infoTableName;
368 void Database::closeDatabase()
373 m_sqliteDatabase.close();
375 // See comment at the top this file regarding calling removeOpenDatabase().
376 DatabaseTracker::tracker().removeOpenDatabase(this);
378 SafePointAwareMutexLocker locker(guidMutex());
380 HashSet<Database*>* hashSet = guidToDatabaseMap().get(m_guid);
382 ASSERT(hashSet->contains(this));
383 hashSet->remove(this);
384 if (hashSet->isEmpty()) {
385 guidToDatabaseMap().remove(m_guid);
387 guidToVersionMap().remove(m_guid);
392 String Database::version() const
394 // Note: In multi-process browsers the cached value may be accurate, but we
395 // cannot read the actual version from the database without potentially
396 // inducing a deadlock.
397 // FIXME: Add an async version getter to the DatabaseAPI.
398 return getCachedVersion();
401 class DoneCreatingDatabaseOnExitCaller {
404 DoneCreatingDatabaseOnExitCaller(Database* database)
405 : m_database(database)
406 , m_openSucceeded(false)
409 ~DoneCreatingDatabaseOnExitCaller()
411 if (!m_openSucceeded)
412 DatabaseTracker::tracker().failedToOpenDatabase(m_database);
415 void setOpenSucceeded() { m_openSucceeded = true; }
418 Member<Database> m_database;
419 bool m_openSucceeded;
422 bool Database::performOpenAndVerify(bool shouldSetVersionInNewDatabase, DatabaseError& error, String& errorMessage)
424 DoneCreatingDatabaseOnExitCaller onExitCaller(this);
425 ASSERT(errorMessage.isEmpty());
426 ASSERT(error == DatabaseError::None); // Better not have any errors already.
427 // Presumed failure. We'll clear it if we succeed below.
428 error = DatabaseError::InvalidDatabaseState;
430 const int maxSqliteBusyWaitTime = 30000;
432 if (!m_sqliteDatabase.open(m_filename)) {
433 reportOpenDatabaseResult(1, InvalidStateError, m_sqliteDatabase.lastError());
434 errorMessage = formatErrorMessage("unable to open database", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
437 if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum())
438 WTF_LOG_ERROR("Unable to turn on incremental auto-vacuum (%d %s)", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
440 m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime);
442 String currentVersion;
444 SafePointAwareMutexLocker locker(guidMutex());
446 GuidVersionMap::iterator entry = guidToVersionMap().find(m_guid);
447 if (entry != guidToVersionMap().end()) {
448 // Map null string to empty string (see updateGuidVersionMap()).
449 currentVersion = entry->value.isNull() ? emptyString() : entry->value.isolatedCopy();
450 WTF_LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data());
452 // Note: In multi-process browsers the cached value may be
453 // inaccurate, but we cannot read the actual version from the
454 // database without potentially inducing a form of deadlock, a
455 // busytimeout error when trying to access the database. So we'll
456 // use the cached value if we're unable to read the value from the
457 // database file without waiting.
458 // FIXME: Add an async openDatabase method to the DatabaseAPI.
459 const int noSqliteBusyWaitTime = 0;
460 m_sqliteDatabase.setBusyTimeout(noSqliteBusyWaitTime);
461 String versionFromDatabase;
462 if (getVersionFromDatabase(versionFromDatabase, false)) {
463 currentVersion = versionFromDatabase;
464 updateGuidVersionMap(m_guid, currentVersion);
466 m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime);
468 WTF_LOG(StorageAPI, "No cached version for guid %i", m_guid);
470 SQLiteTransaction transaction(m_sqliteDatabase);
472 if (!transaction.inProgress()) {
473 reportOpenDatabaseResult(2, InvalidStateError, m_sqliteDatabase.lastError());
474 errorMessage = formatErrorMessage("unable to open database, failed to start transaction", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
475 m_sqliteDatabase.close();
479 String tableName(infoTableName);
480 if (!m_sqliteDatabase.tableExists(tableName)) {
483 if (!m_sqliteDatabase.executeCommand("CREATE TABLE " + tableName + " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) {
484 reportOpenDatabaseResult(3, InvalidStateError, m_sqliteDatabase.lastError());
485 errorMessage = formatErrorMessage("unable to open database, failed to create 'info' table", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
486 transaction.rollback();
487 m_sqliteDatabase.close();
490 } else if (!getVersionFromDatabase(currentVersion, false)) {
491 reportOpenDatabaseResult(4, InvalidStateError, m_sqliteDatabase.lastError());
492 errorMessage = formatErrorMessage("unable to open database, failed to read current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
493 transaction.rollback();
494 m_sqliteDatabase.close();
498 if (currentVersion.length()) {
499 WTF_LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data());
500 } else if (!m_new || shouldSetVersionInNewDatabase) {
501 WTF_LOG(StorageAPI, "Setting version %s in database %s that was just created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data());
502 if (!setVersionInDatabase(m_expectedVersion, false)) {
503 reportOpenDatabaseResult(5, InvalidStateError, m_sqliteDatabase.lastError());
504 errorMessage = formatErrorMessage("unable to open database, failed to write current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
505 transaction.rollback();
506 m_sqliteDatabase.close();
509 currentVersion = m_expectedVersion;
511 updateGuidVersionMap(m_guid, currentVersion);
512 transaction.commit();
516 if (currentVersion.isNull()) {
517 WTF_LOG(StorageAPI, "Database %s does not have its version set", databaseDebugName().ascii().data());
521 // If the expected version isn't the empty string, ensure that the current
522 // database version we have matches that version. Otherwise, set an
524 // If the expected version is the empty string, then we always return with
525 // whatever version of the database we have.
526 if ((!m_new || shouldSetVersionInNewDatabase) && m_expectedVersion.length() && m_expectedVersion != currentVersion) {
527 reportOpenDatabaseResult(6, InvalidStateError, 0);
528 errorMessage = "unable to open database, version mismatch, '" + m_expectedVersion + "' does not match the currentVersion of '" + currentVersion + "'";
529 m_sqliteDatabase.close();
533 ASSERT(m_databaseAuthorizer);
534 m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer.get());
536 // See comment at the top this file regarding calling addOpenDatabase().
537 DatabaseTracker::tracker().addOpenDatabase(this);
541 error = DatabaseError::None; // Clear the presumed error from above.
542 onExitCaller.setOpenSucceeded();
544 if (m_new && !shouldSetVersionInNewDatabase) {
545 // The caller provided a creationCallback which will set the expected
547 m_expectedVersion = "";
550 reportOpenDatabaseResult(0, -1, 0); // OK
552 if (databaseContext()->databaseThread())
553 databaseContext()->databaseThread()->recordDatabaseOpen(this);
557 String Database::stringIdentifier() const
559 // Return a deep copy for ref counting thread safety
560 return m_name.isolatedCopy();
563 String Database::displayName() const
565 // Return a deep copy for ref counting thread safety
566 return m_displayName.isolatedCopy();
569 unsigned long Database::estimatedSize() const
571 return m_estimatedSize;
574 String Database::fileName() const
576 // Return a deep copy for ref counting thread safety
577 return m_filename.isolatedCopy();
580 bool Database::getVersionFromDatabase(String& version, bool shouldCacheVersion)
582 String query(String("SELECT value FROM ") + infoTableName + " WHERE key = '" + versionKey + "';");
584 m_databaseAuthorizer->disable();
586 bool result = retrieveTextResultFromDatabase(m_sqliteDatabase, query, version);
588 if (shouldCacheVersion)
589 setCachedVersion(version);
591 WTF_LOG_ERROR("Failed to retrieve version from database %s", databaseDebugName().ascii().data());
594 m_databaseAuthorizer->enable();
599 bool Database::setVersionInDatabase(const String& version, bool shouldCacheVersion)
601 // The INSERT will replace an existing entry for the database with the new
602 // version number, due to the UNIQUE ON CONFLICT REPLACE clause in the
603 // CREATE statement (see Database::performOpenAndVerify()).
604 String query(String("INSERT INTO ") + infoTableName + " (key, value) VALUES ('" + versionKey + "', ?);");
606 m_databaseAuthorizer->disable();
608 bool result = setTextValueInDatabase(m_sqliteDatabase, query, version);
610 if (shouldCacheVersion)
611 setCachedVersion(version);
613 WTF_LOG_ERROR("Failed to set version %s in database (%s)", version.ascii().data(), query.ascii().data());
616 m_databaseAuthorizer->enable();
621 void Database::setExpectedVersion(const String& version)
623 m_expectedVersion = version.isolatedCopy();
626 String Database::getCachedVersion() const
628 SafePointAwareMutexLocker locker(guidMutex());
629 return guidToVersionMap().get(m_guid).isolatedCopy();
632 void Database::setCachedVersion(const String& actualVersion)
634 // Update the in memory database version map.
635 SafePointAwareMutexLocker locker(guidMutex());
636 updateGuidVersionMap(m_guid, actualVersion);
639 bool Database::getActualVersionForTransaction(String& actualVersion)
641 ASSERT(m_sqliteDatabase.transactionInProgress());
642 // Note: In multi-process browsers the cached value may be inaccurate. So we
643 // retrieve the value from the database and update the cached value here.
644 return getVersionFromDatabase(actualVersion, true);
647 void Database::disableAuthorizer()
649 ASSERT(m_databaseAuthorizer);
650 m_databaseAuthorizer->disable();
653 void Database::enableAuthorizer()
655 ASSERT(m_databaseAuthorizer);
656 m_databaseAuthorizer->enable();
659 void Database::setAuthorizerPermissions(int permissions)
661 ASSERT(m_databaseAuthorizer);
662 m_databaseAuthorizer->setPermissions(permissions);
665 bool Database::lastActionChangedDatabase()
667 ASSERT(m_databaseAuthorizer);
668 return m_databaseAuthorizer->lastActionChangedDatabase();
671 bool Database::lastActionWasInsert()
673 ASSERT(m_databaseAuthorizer);
674 return m_databaseAuthorizer->lastActionWasInsert();
677 void Database::resetDeletes()
679 ASSERT(m_databaseAuthorizer);
680 m_databaseAuthorizer->resetDeletes();
683 bool Database::hadDeletes()
685 ASSERT(m_databaseAuthorizer);
686 return m_databaseAuthorizer->hadDeletes();
689 void Database::resetAuthorizer()
691 if (m_databaseAuthorizer)
692 m_databaseAuthorizer->reset();
695 unsigned long long Database::maximumSize() const
697 return DatabaseTracker::tracker().getMaxSizeForDatabase(this);
700 void Database::incrementalVacuumIfNeeded()
702 int64_t freeSpaceSize = m_sqliteDatabase.freeSpaceSize();
703 int64_t totalSize = m_sqliteDatabase.totalSize();
704 if (totalSize <= 10 * freeSpaceSize) {
705 int result = m_sqliteDatabase.runIncrementalVacuumCommand();
706 reportVacuumDatabaseResult(result);
707 if (result != SQLResultOk)
708 logErrorMessage(formatErrorMessage("error vacuuming database", result, m_sqliteDatabase.lastErrorMsg()));
712 // These are used to generate histograms of errors seen with websql.
713 // See about:histograms in chromium.
714 void Database::reportOpenDatabaseResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode)
716 if (Platform::current()->databaseObserver()) {
717 Platform::current()->databaseObserver()->reportOpenDatabaseResult(
718 createDatabaseIdentifierFromSecurityOrigin(securityOrigin()),
719 stringIdentifier(), errorSite, webSqlErrorCode, sqliteErrorCode);
723 void Database::reportChangeVersionResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode)
725 if (Platform::current()->databaseObserver()) {
726 Platform::current()->databaseObserver()->reportChangeVersionResult(
727 createDatabaseIdentifierFromSecurityOrigin(securityOrigin()),
728 stringIdentifier(), errorSite, webSqlErrorCode, sqliteErrorCode);
732 void Database::reportStartTransactionResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode)
734 if (Platform::current()->databaseObserver()) {
735 Platform::current()->databaseObserver()->reportStartTransactionResult(
736 createDatabaseIdentifierFromSecurityOrigin(securityOrigin()),
737 stringIdentifier(), errorSite, webSqlErrorCode, sqliteErrorCode);
741 void Database::reportCommitTransactionResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode)
743 if (Platform::current()->databaseObserver()) {
744 Platform::current()->databaseObserver()->reportCommitTransactionResult(
745 createDatabaseIdentifierFromSecurityOrigin(securityOrigin()),
746 stringIdentifier(), errorSite, webSqlErrorCode, sqliteErrorCode);
750 void Database::reportExecuteStatementResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode)
752 if (Platform::current()->databaseObserver()) {
753 Platform::current()->databaseObserver()->reportExecuteStatementResult(
754 createDatabaseIdentifierFromSecurityOrigin(securityOrigin()),
755 stringIdentifier(), errorSite, webSqlErrorCode, sqliteErrorCode);
759 void Database::reportVacuumDatabaseResult(int sqliteErrorCode)
761 if (Platform::current()->databaseObserver()) {
762 Platform::current()->databaseObserver()->reportVacuumDatabaseResult(
763 createDatabaseIdentifierFromSecurityOrigin(securityOrigin()),
764 stringIdentifier(), sqliteErrorCode);
768 void Database::logErrorMessage(const String& message)
770 executionContext()->addConsoleMessage(ConsoleMessage::create(StorageMessageSource, ErrorMessageLevel, message));
773 ExecutionContext* Database::executionContext() const
775 return databaseContext()->executionContext();
778 void Database::closeImmediately()
780 ASSERT(executionContext()->isContextThread());
781 if (databaseContext()->databaseThreadAvailable() && opened()) {
782 logErrorMessage("forcibly closing database");
783 databaseContext()->databaseThread()->scheduleTask(DatabaseCloseTask::create(this, 0));
787 void Database::changeVersion(
788 const String& oldVersion,
789 const String& newVersion,
790 SQLTransactionCallback* callback,
791 SQLTransactionErrorCallback* errorCallback,
792 VoidCallback* successCallback)
794 ChangeVersionData data(oldVersion, newVersion);
795 runTransaction(callback, errorCallback, successCallback, false, &data);
798 void Database::transaction(
799 SQLTransactionCallback* callback,
800 SQLTransactionErrorCallback* errorCallback,
801 VoidCallback* successCallback)
803 runTransaction(callback, errorCallback, successCallback, false);
806 void Database::readTransaction(
807 SQLTransactionCallback* callback,
808 SQLTransactionErrorCallback* errorCallback,
809 VoidCallback* successCallback)
811 runTransaction(callback, errorCallback, successCallback, true);
814 static void callTransactionErrorCallback(ExecutionContext*, SQLTransactionErrorCallback* callback, PassOwnPtr<SQLErrorData> errorData)
816 callback->handleEvent(SQLError::create(*errorData));
819 void Database::runTransaction(
820 SQLTransactionCallback* callback,
821 SQLTransactionErrorCallback* errorCallback,
822 VoidCallback* successCallback,
824 const ChangeVersionData* changeVersionData)
826 // FIXME: Rather than passing errorCallback to SQLTransaction and then
827 // sometimes firing it ourselves, this code should probably be pushed down
828 // into Database so that we only create the SQLTransaction if we're
829 // actually going to run it.
831 SQLTransactionErrorCallback* originalErrorCallback = errorCallback;
833 SQLTransaction* transaction = SQLTransaction::create(this, callback, successCallback, errorCallback, readOnly);
834 SQLTransactionBackend* transactionBackend = runTransaction(transaction, readOnly, changeVersionData);
835 if (!transactionBackend) {
836 SQLTransactionErrorCallback* callback = transaction->releaseErrorCallback();
837 ASSERT(callback == originalErrorCallback);
839 OwnPtr<SQLErrorData> error = SQLErrorData::create(SQLError::UNKNOWN_ERR, "database has been closed");
840 executionContext()->postTask(createCrossThreadTask(&callTransactionErrorCallback, callback, error.release()));
845 // This object is constructed in a database thread, and destructed in the
847 class DeliverPendingCallbackTask final : public ExecutionContextTask {
849 static PassOwnPtr<DeliverPendingCallbackTask> create(SQLTransaction* transaction)
851 return adoptPtr(new DeliverPendingCallbackTask(transaction));
854 virtual void performTask(ExecutionContext*) override
856 m_transaction->performPendingCallback();
860 DeliverPendingCallbackTask(SQLTransaction* transaction)
861 : m_transaction(transaction)
865 CrossThreadPersistent<SQLTransaction> m_transaction;
868 void Database::scheduleTransactionCallback(SQLTransaction* transaction)
870 executionContext()->postTask(DeliverPendingCallbackTask::create(transaction));
873 Vector<String> Database::performGetTableNames()
877 SQLiteStatement statement(sqliteDatabase(), "SELECT name FROM sqlite_master WHERE type='table';");
878 if (statement.prepare() != SQLResultOk) {
879 WTF_LOG_ERROR("Unable to retrieve list of tables for database %s", databaseDebugName().ascii().data());
881 return Vector<String>();
884 Vector<String> tableNames;
886 while ((result = statement.step()) == SQLResultRow) {
887 String name = statement.getColumnText(0);
888 if (name != databaseInfoTableName())
889 tableNames.append(name);
894 if (result != SQLResultDone) {
895 WTF_LOG_ERROR("Error getting tables for database %s", databaseDebugName().ascii().data());
896 return Vector<String>();
902 Vector<String> Database::tableNames()
904 // FIXME: Not using isolatedCopy on these strings looks ok since threads
905 // take strict turns in dealing with them. However, if the code changes,
906 // this may not be true anymore.
907 Vector<String> result;
908 TaskSynchronizer synchronizer;
909 if (!databaseContext()->databaseThreadAvailable())
912 OwnPtr<DatabaseTableNamesTask> task = DatabaseTableNamesTask::create(this, &synchronizer, result);
913 databaseContext()->databaseThread()->scheduleTask(task.release());
914 synchronizer.waitForTaskCompletion();
919 SecurityOrigin* Database::securityOrigin() const
921 if (executionContext()->isContextThread())
922 return m_contextThreadSecurityOrigin.get();
923 if (databaseContext()->databaseThread()->isDatabaseThread())
924 return m_databaseThreadSecurityOrigin.get();