2 * Copyright (C) 2016 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. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #include "SQLiteIDBBackingStore.h"
29 #if ENABLE(INDEXED_DATABASE)
31 #include "FileSystem.h"
32 #include "IDBBindingUtilities.h"
33 #include "IDBDatabaseException.h"
34 #include "IDBGetAllRecordsData.h"
35 #include "IDBGetAllResult.h"
36 #include "IDBGetRecordData.h"
37 #include "IDBGetResult.h"
38 #include "IDBIterateCursorData.h"
39 #include "IDBKeyData.h"
40 #include "IDBObjectStoreInfo.h"
41 #include "IDBSerialization.h"
42 #include "IDBTransactionInfo.h"
46 #include "SQLiteDatabase.h"
47 #include "SQLiteFileSystem.h"
48 #include "SQLiteIDBCursor.h"
49 #include "SQLiteStatement.h"
50 #include "SQLiteTransaction.h"
51 #include "ThreadSafeDataBuffer.h"
52 #include <heap/HeapInlines.h>
53 #include <heap/StrongInlines.h>
54 #include <runtime/AuxiliaryBarrierInlines.h>
55 #include <runtime/JSCJSValueInlines.h>
56 #include <runtime/JSGlobalObject.h>
57 #include <runtime/StructureInlines.h>
58 #include <wtf/NeverDestroyed.h>
65 // Current version of the metadata schema being used in the metadata database.
66 static const int currentMetadataVersion = 1;
68 static int idbKeyCollate(int aLength, const void* aBuffer, int bLength, const void* bBuffer)
71 if (!deserializeIDBKeyData(static_cast<const uint8_t*>(aBuffer), aLength, a)) {
72 LOG_ERROR("Unable to deserialize key A in collation function.");
74 // There's no way to indicate an error to SQLite - we have to return a sorting decision.
75 // We arbitrarily choose "A > B"
78 if (!deserializeIDBKeyData(static_cast<const uint8_t*>(bBuffer), bLength, b)) {
79 LOG_ERROR("Unable to deserialize key B in collation function.");
81 // There's no way to indicate an error to SQLite - we have to return a sorting decision.
82 // We arbitrarily choose "A > B"
89 static const String v1RecordsTableSchema(const String& tableName)
91 return makeString("CREATE TABLE ", tableName, " (objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, key TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, value NOT NULL ON CONFLICT FAIL)");
94 static const String& v1RecordsTableSchema()
96 static NeverDestroyed<WTF::String> v1RecordsTableSchemaString(v1RecordsTableSchema("Records"));
97 return v1RecordsTableSchemaString;
100 static const String& v1RecordsTableSchemaAlternate()
102 static NeverDestroyed<WTF::String> v1RecordsTableSchemaString(v1RecordsTableSchema("\"Records\""));
103 return v1RecordsTableSchemaString;
106 static const String v2RecordsTableSchema(const String& tableName)
108 return makeString("CREATE TABLE ", tableName, " (objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, key TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL, value NOT NULL ON CONFLICT FAIL)");
111 static const String& v2RecordsTableSchema()
113 static NeverDestroyed<WTF::String> v2RecordsTableSchemaString(v2RecordsTableSchema("Records"));
114 return v2RecordsTableSchemaString;
117 static const String& v2RecordsTableSchemaAlternate()
119 static NeverDestroyed<WTF::String> v2RecordsTableSchemaString(v2RecordsTableSchema("\"Records\""));
120 return v2RecordsTableSchemaString;
123 static const String v3RecordsTableSchema(const String& tableName)
125 return makeString("CREATE TABLE ", tableName, " (objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, key TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL, value NOT NULL ON CONFLICT FAIL, recordID INTEGER PRIMARY KEY)");
128 static const String& v3RecordsTableSchema()
130 static NeverDestroyed<WTF::String> v3RecordsTableSchemaString(v3RecordsTableSchema("Records"));
131 return v3RecordsTableSchemaString;
134 static const String& v3RecordsTableSchemaAlternate()
136 static NeverDestroyed<WTF::String> v3RecordsTableSchemaString(v3RecordsTableSchema("\"Records\""));
137 return v3RecordsTableSchemaString;
140 static const String v1IndexRecordsTableSchema(const String& tableName)
142 return makeString("CREATE TABLE ", tableName, " (indexID INTEGER NOT NULL ON CONFLICT FAIL, objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, key TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL, value NOT NULL ON CONFLICT FAIL)");
145 static const String& v1IndexRecordsTableSchema()
147 static NeverDestroyed<WTF::String> v1IndexRecordsTableSchemaString(v1IndexRecordsTableSchema("IndexRecords"));
148 return v1IndexRecordsTableSchemaString;
151 static const String& v1IndexRecordsTableSchemaAlternate()
153 static NeverDestroyed<WTF::String> v1IndexRecordsTableSchemaString(v1IndexRecordsTableSchema("\"IndexRecords\""));
154 return v1IndexRecordsTableSchemaString;
157 static const String v2IndexRecordsTableSchema(const String& tableName)
159 return makeString("CREATE TABLE ", tableName, " (indexID INTEGER NOT NULL ON CONFLICT FAIL, objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, key TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL, value TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL)");
162 static const String& v2IndexRecordsTableSchema()
164 static NeverDestroyed<WTF::String> v2IndexRecordsTableSchemaString(v2IndexRecordsTableSchema("IndexRecords"));
165 return v2IndexRecordsTableSchemaString;
168 static const String& v2IndexRecordsTableSchemaAlternate()
170 static NeverDestroyed<WTF::String> v2IndexRecordsTableSchemaString(v2IndexRecordsTableSchema("\"IndexRecords\""));
171 return v2IndexRecordsTableSchemaString;
174 static const String v3IndexRecordsTableSchema(const String& tableName)
176 return makeString("CREATE TABLE ", tableName, " (indexID INTEGER NOT NULL ON CONFLICT FAIL, objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, key TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL, value TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL, objectStoreRecordID INTEGER NOT NULL ON CONFLICT FAIL)");
179 static const String v3IndexRecordsTableSchema()
181 static NeverDestroyed<WTF::String> indexRecordsTableSchemaString = v3IndexRecordsTableSchema("IndexRecords");
182 return indexRecordsTableSchemaString;
185 static const String v3IndexRecordsTableSchemaAlternate()
187 static NeverDestroyed<WTF::String> indexRecordsTableSchemaString = v3IndexRecordsTableSchema("\"IndexRecords\"");
188 return indexRecordsTableSchemaString;
191 static const String& v1IndexRecordsIndexSchema()
193 static NeverDestroyed<WTF::String> indexRecordsIndexSchemaString("CREATE INDEX IndexRecordsIndex ON IndexRecords (key)");
194 return indexRecordsIndexSchemaString;
197 static const String blobRecordsTableSchema(const String& tableName)
199 return makeString("CREATE TABLE ", tableName, " (objectStoreRow INTEGER NOT NULL ON CONFLICT FAIL, blobURL TEXT NOT NULL ON CONFLICT FAIL)");
202 static const String& blobRecordsTableSchema()
204 static NeverDestroyed<String> blobRecordsTableSchemaString(blobRecordsTableSchema("BlobRecords"));
205 return blobRecordsTableSchemaString;
208 static const String& blobRecordsTableSchemaAlternate()
210 static NeverDestroyed<String> blobRecordsTableSchemaString(blobRecordsTableSchema("\"BlobRecords\""));
211 return blobRecordsTableSchemaString;
214 static const String blobFilesTableSchema(const String& tableName)
216 return makeString("CREATE TABLE ", tableName, " (blobURL TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT FAIL, fileName TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT FAIL)");
219 static const String& blobFilesTableSchema()
221 static NeverDestroyed<String> blobFilesTableSchemaString(blobFilesTableSchema("BlobFiles"));
222 return blobFilesTableSchemaString;
225 static const String& blobFilesTableSchemaAlternate()
227 static NeverDestroyed<String> blobFilesTableSchemaString(blobFilesTableSchema("\"BlobFiles\""));
228 return blobFilesTableSchemaString;
231 SQLiteIDBBackingStore::SQLiteIDBBackingStore(const IDBDatabaseIdentifier& identifier, const String& databaseRootDirectory, IDBBackingStoreTemporaryFileHandler& fileHandler)
232 : m_identifier(identifier)
233 , m_temporaryFileHandler(fileHandler)
235 m_absoluteDatabaseDirectory = identifier.databaseDirectoryRelativeToRoot(databaseRootDirectory);
238 SQLiteIDBBackingStore::~SQLiteIDBBackingStore()
244 JSLockHolder locker(m_vm.get());
245 m_globalObject.clear();
251 void SQLiteIDBBackingStore::initializeVM()
254 ASSERT(!m_globalObject);
257 JSLockHolder locker(m_vm.get());
258 m_globalObject.set(*m_vm, JSGlobalObject::create(*m_vm, JSGlobalObject::createStructure(*m_vm, jsNull())));
262 VM& SQLiteIDBBackingStore::vm()
268 JSGlobalObject& SQLiteIDBBackingStore::globalObject()
271 return **m_globalObject;
274 static bool createOrMigrateRecordsTableIfNecessary(SQLiteDatabase& database)
276 String currentSchema;
278 // Fetch the schema for an existing records table.
279 SQLiteStatement statement(database, "SELECT type, sql FROM sqlite_master WHERE tbl_name='Records'");
280 if (statement.prepare() != SQLITE_OK) {
281 LOG_ERROR("Unable to prepare statement to fetch schema for the Records table.");
285 int sqliteResult = statement.step();
287 // If there is no Records table at all, create it and then bail.
288 if (sqliteResult == SQLITE_DONE) {
289 if (!database.executeCommand(v3RecordsTableSchema())) {
290 LOG_ERROR("Could not create Records table in database (%i) - %s", database.lastError(), database.lastErrorMsg());
297 if (sqliteResult != SQLITE_ROW) {
298 LOG_ERROR("Error executing statement to fetch schema for the Records table.");
302 currentSchema = statement.getColumnText(1);
305 ASSERT(!currentSchema.isEmpty());
307 // If the schema in the backing store is the current schema, we're done.
308 if (currentSchema == v3RecordsTableSchema() || currentSchema == v3RecordsTableSchemaAlternate())
311 // If the record table is not the current schema then it must be one of the previous schemas.
312 // If it is not then the database is in an unrecoverable state and this should be considered a fatal error.
313 if (currentSchema != v1RecordsTableSchema() && currentSchema != v1RecordsTableSchemaAlternate()
314 && currentSchema != v2RecordsTableSchema() && currentSchema != v2RecordsTableSchemaAlternate())
315 RELEASE_ASSERT_NOT_REACHED();
317 SQLiteTransaction transaction(database);
320 // Create a temporary table with the correct schema and migrate all existing content over.
321 if (!database.executeCommand(v3RecordsTableSchema("_Temp_Records"))) {
322 LOG_ERROR("Could not create temporary records table in database (%i) - %s", database.lastError(), database.lastErrorMsg());
326 if (!database.executeCommand("INSERT INTO _Temp_Records (objectStoreID, key, value) SELECT objectStoreID, CAST(key AS TEXT), value FROM Records")) {
327 LOG_ERROR("Could not migrate existing Records content (%i) - %s", database.lastError(), database.lastErrorMsg());
331 if (!database.executeCommand("DROP TABLE Records")) {
332 LOG_ERROR("Could not drop existing Records table (%i) - %s", database.lastError(), database.lastErrorMsg());
336 if (!database.executeCommand("ALTER TABLE _Temp_Records RENAME TO Records")) {
337 LOG_ERROR("Could not rename temporary Records table (%i) - %s", database.lastError(), database.lastErrorMsg());
341 transaction.commit();
346 bool SQLiteIDBBackingStore::ensureValidBlobTables()
349 ASSERT(m_sqliteDB->isOpen());
351 String currentSchema;
353 // Fetch the schema for an existing blob record table.
354 SQLiteStatement statement(*m_sqliteDB, "SELECT type, sql FROM sqlite_master WHERE tbl_name='BlobRecords'");
355 if (statement.prepare() != SQLITE_OK) {
356 LOG_ERROR("Unable to prepare statement to fetch schema for the BlobRecords table.");
360 int sqliteResult = statement.step();
362 // If there is no BlobRecords table at all, create it..
363 if (sqliteResult == SQLITE_DONE) {
364 if (!m_sqliteDB->executeCommand(blobRecordsTableSchema())) {
365 LOG_ERROR("Could not create BlobRecords table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
369 currentSchema = blobRecordsTableSchema();
370 } else if (sqliteResult != SQLITE_ROW) {
371 LOG_ERROR("Error executing statement to fetch schema for the BlobRecords table.");
374 currentSchema = statement.getColumnText(1);
377 if (currentSchema != blobRecordsTableSchema() && currentSchema != blobRecordsTableSchemaAlternate()) {
378 LOG_ERROR("Invalid BlobRecords table schema found");
383 // Fetch the schema for an existing blob file table.
384 SQLiteStatement statement(*m_sqliteDB, "SELECT type, sql FROM sqlite_master WHERE tbl_name='BlobFiles'");
385 if (statement.prepare() != SQLITE_OK) {
386 LOG_ERROR("Unable to prepare statement to fetch schema for the BlobFiles table.");
390 int sqliteResult = statement.step();
392 // If there is no BlobFiles table at all, create it and then bail.
393 if (sqliteResult == SQLITE_DONE) {
394 if (!m_sqliteDB->executeCommand(blobFilesTableSchema())) {
395 LOG_ERROR("Could not create BlobFiles table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
402 if (sqliteResult != SQLITE_ROW) {
403 LOG_ERROR("Error executing statement to fetch schema for the BlobFiles table.");
407 currentSchema = statement.getColumnText(1);
410 if (currentSchema != blobFilesTableSchema() && currentSchema != blobFilesTableSchemaAlternate()) {
411 LOG_ERROR("Invalid BlobFiles table schema found");
418 bool SQLiteIDBBackingStore::ensureValidRecordsTable()
421 ASSERT(m_sqliteDB->isOpen());
423 if (!createOrMigrateRecordsTableIfNecessary(*m_sqliteDB))
426 // Whether the updated records table already existed or if it was just created and the data migrated over,
427 // make sure the uniqueness index exists.
428 if (!m_sqliteDB->executeCommand("CREATE UNIQUE INDEX IF NOT EXISTS RecordsIndex ON Records (objectStoreID, key);")) {
429 LOG_ERROR("Could not create RecordsIndex on Records table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
436 bool SQLiteIDBBackingStore::ensureValidIndexRecordsTable()
439 ASSERT(m_sqliteDB->isOpen());
441 String currentSchema;
443 // Fetch the schema for an existing index record table.
444 SQLiteStatement statement(*m_sqliteDB, "SELECT type, sql FROM sqlite_master WHERE tbl_name='IndexRecords'");
445 if (statement.prepare() != SQLITE_OK) {
446 LOG_ERROR("Unable to prepare statement to fetch schema for the IndexRecords table.");
450 int sqliteResult = statement.step();
452 // If there is no IndexRecords table at all, create it and then bail.
453 if (sqliteResult == SQLITE_DONE) {
454 if (!m_sqliteDB->executeCommand(v3IndexRecordsTableSchema())) {
455 LOG_ERROR("Could not create IndexRecords table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
462 if (sqliteResult != SQLITE_ROW) {
463 LOG_ERROR("Error executing statement to fetch schema for the IndexRecords table.");
467 currentSchema = statement.getColumnText(1);
470 ASSERT(!currentSchema.isEmpty());
472 // If the schema in the backing store is the current schema, we're done.
473 if (currentSchema == v3IndexRecordsTableSchema() || currentSchema == v3IndexRecordsTableSchemaAlternate())
476 // If the record table is not the current schema then it must be one of the previous schemas.
477 // If it is not then the database is in an unrecoverable state and this should be considered a fatal error.
478 if (currentSchema != v1IndexRecordsTableSchema() && currentSchema != v1IndexRecordsTableSchemaAlternate()
479 && currentSchema != v2IndexRecordsTableSchema() && currentSchema != v2IndexRecordsTableSchemaAlternate())
480 RELEASE_ASSERT_NOT_REACHED();
482 SQLiteTransaction transaction(*m_sqliteDB);
485 // Create a temporary table with the correct schema and migrate all existing content over.
486 if (!m_sqliteDB->executeCommand(v3IndexRecordsTableSchema("_Temp_IndexRecords"))) {
487 LOG_ERROR("Could not create temporary index records table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
491 if (!m_sqliteDB->executeCommand("INSERT INTO _Temp_IndexRecords SELECT IndexRecords.indexID, IndexRecords.objectStoreID, IndexRecords.key, IndexRecords.value, Records.rowid FROM IndexRecords INNER JOIN Records ON Records.key = IndexRecords.value AND Records.objectStoreID = IndexRecords.objectStoreID")) {
492 LOG_ERROR("Could not migrate existing IndexRecords content (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
496 if (!m_sqliteDB->executeCommand("DROP TABLE IndexRecords")) {
497 LOG_ERROR("Could not drop existing IndexRecords table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
501 if (!m_sqliteDB->executeCommand("ALTER TABLE _Temp_IndexRecords RENAME TO IndexRecords")) {
502 LOG_ERROR("Could not rename temporary IndexRecords table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
506 transaction.commit();
511 bool SQLiteIDBBackingStore::ensureValidIndexRecordsIndex()
514 ASSERT(m_sqliteDB->isOpen());
516 String currentSchema;
518 // Fetch the schema for an existing index record index.
519 SQLiteStatement statement(*m_sqliteDB, "SELECT sql FROM sqlite_master WHERE name='IndexRecordsIndex'");
520 if (statement.prepare() != SQLITE_OK) {
521 LOG_ERROR("Unable to prepare statement to fetch schema for the IndexRecordsIndex index.");
525 int sqliteResult = statement.step();
527 // If there is no IndexRecordsIndex index at all, create it and then bail.
528 if (sqliteResult == SQLITE_DONE) {
529 if (!m_sqliteDB->executeCommand(v1IndexRecordsIndexSchema())) {
530 LOG_ERROR("Could not create IndexRecordsIndex index in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
537 if (sqliteResult != SQLITE_ROW) {
538 LOG_ERROR("Error executing statement to fetch schema for the IndexRecordsIndex index.");
542 currentSchema = statement.getColumnText(0);
545 ASSERT(!currentSchema.isEmpty());
547 // If the schema in the backing store is the current schema, we're done.
548 if (currentSchema == v1IndexRecordsIndexSchema())
551 // There is currently no outdated schema for the IndexRecordsIndex, so any other existing schema means this database is invalid.
555 std::unique_ptr<IDBDatabaseInfo> SQLiteIDBBackingStore::createAndPopulateInitialDatabaseInfo()
558 ASSERT(m_sqliteDB->isOpen());
560 if (!m_sqliteDB->executeCommand("CREATE TABLE IDBDatabaseInfo (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, value TEXT NOT NULL ON CONFLICT FAIL);")) {
561 LOG_ERROR("Could not create IDBDatabaseInfo table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
566 if (!m_sqliteDB->executeCommand("CREATE TABLE ObjectStoreInfo (id INTEGER PRIMARY KEY NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT FAIL, name TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT FAIL, keyPath BLOB NOT NULL ON CONFLICT FAIL, autoInc INTEGER NOT NULL ON CONFLICT FAIL, maxIndexID INTEGER NOT NULL ON CONFLICT FAIL);")) {
567 LOG_ERROR("Could not create ObjectStoreInfo table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
572 if (!m_sqliteDB->executeCommand("CREATE TABLE IndexInfo (id INTEGER NOT NULL ON CONFLICT FAIL, name TEXT NOT NULL ON CONFLICT FAIL, objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, keyPath BLOB NOT NULL ON CONFLICT FAIL, isUnique INTEGER NOT NULL ON CONFLICT FAIL, multiEntry INTEGER NOT NULL ON CONFLICT FAIL);")) {
573 LOG_ERROR("Could not create IndexInfo table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
578 if (!m_sqliteDB->executeCommand("CREATE TABLE KeyGenerators (objectStoreID INTEGER NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, currentKey INTEGER NOT NULL ON CONFLICT FAIL);")) {
579 LOG_ERROR("Could not create KeyGenerators table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
585 SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO IDBDatabaseInfo VALUES ('MetadataVersion', ?);"));
586 if (sql.prepare() != SQLITE_OK
587 || sql.bindInt(1, currentMetadataVersion) != SQLITE_OK
588 || sql.step() != SQLITE_DONE) {
589 LOG_ERROR("Could not insert database metadata version into IDBDatabaseInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
595 SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO IDBDatabaseInfo VALUES ('DatabaseName', ?);"));
596 if (sql.prepare() != SQLITE_OK
597 || sql.bindText(1, m_identifier.databaseName()) != SQLITE_OK
598 || sql.step() != SQLITE_DONE) {
599 LOG_ERROR("Could not insert database name into IDBDatabaseInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
605 // Database versions are defined to be a uin64_t in the spec but sqlite3 doesn't support native binding of unsigned integers.
606 // Therefore we'll store the version as a String.
607 SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO IDBDatabaseInfo VALUES ('DatabaseVersion', ?);"));
608 if (sql.prepare() != SQLITE_OK
609 || sql.bindText(1, String::number(0)) != SQLITE_OK
610 || sql.step() != SQLITE_DONE) {
611 LOG_ERROR("Could not insert default version into IDBDatabaseInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
617 if (!m_sqliteDB->executeCommand(ASCIILiteral("INSERT INTO IDBDatabaseInfo VALUES ('MaxObjectStoreID', 1);"))) {
618 LOG_ERROR("Could not insert default version into IDBDatabaseInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
623 // This initial database info matches the default values we just put into the metadata database.
624 return std::make_unique<IDBDatabaseInfo>(m_identifier.databaseName(), 0);
627 std::unique_ptr<IDBDatabaseInfo> SQLiteIDBBackingStore::extractExistingDatabaseInfo()
631 if (!m_sqliteDB->tableExists(ASCIILiteral("IDBDatabaseInfo")))
636 SQLiteStatement sql(*m_sqliteDB, "SELECT value FROM IDBDatabaseInfo WHERE key = 'DatabaseName';");
637 if (sql.isColumnNull(0))
639 databaseName = sql.getColumnText(0);
640 if (databaseName != m_identifier.databaseName()) {
641 LOG_ERROR("Database name in the info database ('%s') does not match the expected name ('%s')", databaseName.utf8().data(), m_identifier.databaseName().utf8().data());
645 uint64_t databaseVersion;
647 SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT value FROM IDBDatabaseInfo WHERE key = 'DatabaseVersion';"));
648 if (sql.isColumnNull(0))
650 String stringVersion = sql.getColumnText(0);
652 databaseVersion = stringVersion.toUInt64Strict(&ok);
654 LOG_ERROR("Database version on disk ('%s') does not cleanly convert to an unsigned 64-bit integer version", stringVersion.utf8().data());
659 auto databaseInfo = std::make_unique<IDBDatabaseInfo>(databaseName, databaseVersion);
662 SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT id, name, keyPath, autoInc, maxIndexID FROM ObjectStoreInfo;"));
663 if (sql.prepare() != SQLITE_OK)
666 int result = sql.step();
667 while (result == SQLITE_ROW) {
668 uint64_t objectStoreID = sql.getColumnInt64(0);
669 String objectStoreName = sql.getColumnText(1);
671 Vector<char> keyPathBuffer;
672 sql.getColumnBlobAsVector(2, keyPathBuffer);
674 std::optional<IDBKeyPath> objectStoreKeyPath;
675 if (!deserializeIDBKeyPath(reinterpret_cast<const uint8_t*>(keyPathBuffer.data()), keyPathBuffer.size(), objectStoreKeyPath)) {
676 LOG_ERROR("Unable to extract key path from database");
680 bool autoIncrement = sql.getColumnInt(3);
682 databaseInfo->addExistingObjectStore({ objectStoreID, objectStoreName, WTFMove(objectStoreKeyPath), autoIncrement });
687 if (result != SQLITE_DONE) {
688 LOG_ERROR("Error fetching object store info from database on disk");
694 SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT id, name, objectStoreID, keyPath, isUnique, multiEntry FROM IndexInfo;"));
695 if (sql.prepare() != SQLITE_OK)
698 int result = sql.step();
699 while (result == SQLITE_ROW) {
700 uint64_t indexID = sql.getColumnInt64(0);
701 String indexName = sql.getColumnText(1);
702 uint64_t objectStoreID = sql.getColumnInt64(2);
704 Vector<char> keyPathBuffer;
705 sql.getColumnBlobAsVector(3, keyPathBuffer);
707 std::optional<IDBKeyPath> indexKeyPath;
708 if (!deserializeIDBKeyPath(reinterpret_cast<const uint8_t*>(keyPathBuffer.data()), keyPathBuffer.size(), indexKeyPath)) {
709 LOG_ERROR("Unable to extract key path from database");
713 LOG_ERROR("Unable to extract key path from database");
717 bool unique = sql.getColumnInt(4);
718 bool multiEntry = sql.getColumnInt(5);
720 auto objectStore = databaseInfo->infoForExistingObjectStore(objectStoreID);
722 LOG_ERROR("Found index referring to a non-existant object store");
726 objectStore->addExistingIndex({ indexID, objectStoreID, indexName, WTFMove(indexKeyPath.value()), unique, multiEntry });
731 if (result != SQLITE_DONE) {
732 LOG_ERROR("Error fetching index info from database on disk");
740 String SQLiteIDBBackingStore::databaseNameFromEncodedFilename(const String& encodedName)
742 if (equal(encodedName, ASCIILiteral("%00")))
745 String partiallyDecoded = encodedName;
746 partiallyDecoded.replace(ASCIILiteral("%2E"), ASCIILiteral("."));
748 return decodeFromFilename(partiallyDecoded);
751 String SQLiteIDBBackingStore::filenameForDatabaseName() const
753 ASSERT(!m_identifier.databaseName().isNull());
755 if (m_identifier.databaseName().isEmpty())
758 String filename = encodeForFileName(m_identifier.databaseName());
759 filename.replace('.', "%2E");
764 String SQLiteIDBBackingStore::fullDatabaseDirectory() const
766 ASSERT(!m_identifier.databaseName().isNull());
768 return pathByAppendingComponent(m_absoluteDatabaseDirectory, filenameForDatabaseName());
771 String SQLiteIDBBackingStore::fullDatabasePath() const
773 ASSERT(!m_identifier.databaseName().isNull());
775 return pathByAppendingComponent(fullDatabaseDirectory(), "IndexedDB.sqlite3");
778 IDBError SQLiteIDBBackingStore::getOrEstablishDatabaseInfo(IDBDatabaseInfo& info)
780 LOG(IndexedDB, "SQLiteIDBBackingStore::getOrEstablishDatabaseInfo - database %s", m_identifier.databaseName().utf8().data());
782 if (m_databaseInfo) {
783 info = *m_databaseInfo;
787 makeAllDirectories(fullDatabaseDirectory());
788 String dbFilename = fullDatabasePath();
790 m_sqliteDB = std::make_unique<SQLiteDatabase>();
791 if (!m_sqliteDB->open(dbFilename)) {
792 LOG_ERROR("Failed to open SQLite database at path '%s'", dbFilename.utf8().data());
797 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to open database file on disk") };
799 m_sqliteDB->setCollationFunction("IDBKEY", [this](int aLength, const void* a, int bLength, const void* b) {
800 return idbKeyCollate(aLength, a, bLength, b);
803 if (!ensureValidRecordsTable()) {
804 LOG_ERROR("Error creating or migrating Records table in database");
806 return { IDBDatabaseException::UnknownError, ASCIILiteral("Error creating or migrating Records table in database") };
809 if (!ensureValidIndexRecordsTable()) {
810 LOG_ERROR("Error creating or migrating Index Records table in database");
812 return { IDBDatabaseException::UnknownError, ASCIILiteral("Error creating or migrating Index Records table in database") };
815 if (!ensureValidIndexRecordsIndex()) {
816 LOG_ERROR("Error creating or migrating Index Records index in database");
818 return { IDBDatabaseException::UnknownError, ASCIILiteral("Error creating or migrating Index Records index in database") };
821 if (!ensureValidBlobTables()) {
822 LOG_ERROR("Error creating or confirming Blob Records tables in database");
824 return { IDBDatabaseException::UnknownError, ASCIILiteral("Error creating or confirming Blob Records tables in database") };
827 auto databaseInfo = extractExistingDatabaseInfo();
829 databaseInfo = createAndPopulateInitialDatabaseInfo();
832 LOG_ERROR("Unable to establish IDB database at path '%s'", dbFilename.utf8().data());
834 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to establish IDB database file") };
837 m_databaseInfo = WTFMove(databaseInfo);
838 info = *m_databaseInfo;
842 IDBError SQLiteIDBBackingStore::beginTransaction(const IDBTransactionInfo& info)
844 LOG(IndexedDB, "SQLiteIDBBackingStore::beginTransaction - %s", info.identifier().loggingString().utf8().data());
847 ASSERT(m_sqliteDB->isOpen());
848 ASSERT(m_databaseInfo);
850 auto addResult = m_transactions.add(info.identifier(), nullptr);
851 if (!addResult.isNewEntry) {
852 LOG_ERROR("Attempt to establish transaction identifier that already exists");
853 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to establish transaction identifier that already exists") };
856 addResult.iterator->value = std::make_unique<SQLiteIDBTransaction>(*this, info);
858 auto error = addResult.iterator->value->begin(*m_sqliteDB);
859 if (error.isNull() && info.mode() == IDBTransactionMode::Versionchange) {
860 m_originalDatabaseInfoBeforeVersionChange = std::make_unique<IDBDatabaseInfo>(*m_databaseInfo);
862 SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("UPDATE IDBDatabaseInfo SET value = ? where key = 'DatabaseVersion';"));
863 if (sql.prepare() != SQLITE_OK
864 || sql.bindText(1, String::number(info.newVersion())) != SQLITE_OK
865 || sql.step() != SQLITE_DONE)
866 error = { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to store new database version in database") };
872 IDBError SQLiteIDBBackingStore::abortTransaction(const IDBResourceIdentifier& identifier)
874 LOG(IndexedDB, "SQLiteIDBBackingStore::abortTransaction - %s", identifier.loggingString().utf8().data());
877 ASSERT(m_sqliteDB->isOpen());
879 auto transaction = m_transactions.take(identifier);
881 LOG_ERROR("Attempt to commit a transaction that hasn't been established");
882 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to abort a transaction that hasn't been established") };
885 if (transaction->mode() == IDBTransactionMode::Versionchange && m_originalDatabaseInfoBeforeVersionChange)
886 m_databaseInfo = WTFMove(m_originalDatabaseInfoBeforeVersionChange);
888 return transaction->abort();
891 IDBError SQLiteIDBBackingStore::commitTransaction(const IDBResourceIdentifier& identifier)
893 LOG(IndexedDB, "SQLiteIDBBackingStore::commitTransaction - %s", identifier.loggingString().utf8().data());
896 ASSERT(m_sqliteDB->isOpen());
898 auto transaction = m_transactions.take(identifier);
900 LOG_ERROR("Attempt to commit a transaction that hasn't been established");
901 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to commit a transaction that hasn't been established") };
904 auto error = transaction->commit();
905 if (!error.isNull()) {
906 if (transaction->mode() == IDBTransactionMode::Versionchange) {
907 ASSERT(m_originalDatabaseInfoBeforeVersionChange);
908 m_databaseInfo = WTFMove(m_originalDatabaseInfoBeforeVersionChange);
911 m_originalDatabaseInfoBeforeVersionChange = nullptr;
916 IDBError SQLiteIDBBackingStore::createObjectStore(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo& info)
918 LOG(IndexedDB, "SQLiteIDBBackingStore::createObjectStore - adding OS %s with ID %" PRIu64, info.name().utf8().data(), info.identifier());
921 ASSERT(m_sqliteDB->isOpen());
923 auto* transaction = m_transactions.get(transactionIdentifier);
924 if (!transaction || !transaction->inProgress()) {
925 LOG_ERROR("Attempt to create an object store without an in-progress transaction");
926 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to create an object store without an in-progress transaction") };
928 if (transaction->mode() != IDBTransactionMode::Versionchange) {
929 LOG_ERROR("Attempt to create an object store in a non-version-change transaction");
930 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to create an object store in a non-version-change transaction") };
933 RefPtr<SharedBuffer> keyPathBlob = serializeIDBKeyPath(info.keyPath());
935 LOG_ERROR("Unable to serialize IDBKeyPath to save in database for new object store");
936 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKeyPath to save in database for new object store") };
940 auto* sql = cachedStatement(SQL::CreateObjectStoreInfo, ASCIILiteral("INSERT INTO ObjectStoreInfo VALUES (?, ?, ?, ?, ?);"));
942 || sql->bindInt64(1, info.identifier()) != SQLITE_OK
943 || sql->bindText(2, info.name()) != SQLITE_OK
944 || sql->bindBlob(3, keyPathBlob->data(), keyPathBlob->size()) != SQLITE_OK
945 || sql->bindInt(4, info.autoIncrement()) != SQLITE_OK
946 || sql->bindInt64(5, info.maxIndexID()) != SQLITE_OK
947 || sql->step() != SQLITE_DONE) {
948 LOG_ERROR("Could not add object store '%s' to ObjectStoreInfo table (%i) - %s", info.name().utf8().data(), m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
949 return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not create object store") };
954 auto* sql = cachedStatement(SQL::CreateObjectStoreKeyGenerator, ASCIILiteral("INSERT INTO KeyGenerators VALUES (?, 0);"));
956 || sql->bindInt64(1, info.identifier()) != SQLITE_OK
957 || sql->step() != SQLITE_DONE) {
958 LOG_ERROR("Could not seed initial key generator value for ObjectStoreInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
959 return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not seed initial key generator value for object store") };
963 m_databaseInfo->addExistingObjectStore(info);
968 IDBError SQLiteIDBBackingStore::deleteObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier)
970 LOG(IndexedDB, "SQLiteIDBBackingStore::deleteObjectStore - object store %" PRIu64, objectStoreIdentifier);
973 ASSERT(m_sqliteDB->isOpen());
975 auto* transaction = m_transactions.get(transactionIdentifier);
976 if (!transaction || !transaction->inProgress()) {
977 LOG_ERROR("Attempt to delete an object store without an in-progress transaction");
978 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete an object store without an in-progress transaction") };
980 if (transaction->mode() != IDBTransactionMode::Versionchange) {
981 LOG_ERROR("Attempt to delete an object store in a non-version-change transaction");
982 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete an object store in a non-version-change transaction") };
985 // Delete the ObjectStore record
987 auto* sql = cachedStatement(SQL::DeleteObjectStoreInfo, ASCIILiteral("DELETE FROM ObjectStoreInfo WHERE id = ?;"));
989 || sql->bindInt64(1, objectStoreIdentifier) != SQLITE_OK
990 || sql->step() != SQLITE_DONE) {
991 LOG_ERROR("Could not delete object store id %" PRIi64 " from ObjectStoreInfo table (%i) - %s", objectStoreIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
992 return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not delete object store") };
996 // Delete the ObjectStore's key generator record if there is one.
998 auto* sql = cachedStatement(SQL::DeleteObjectStoreKeyGenerator, ASCIILiteral("DELETE FROM KeyGenerators WHERE objectStoreID = ?;"));
1000 || sql->bindInt64(1, objectStoreIdentifier) != SQLITE_OK
1001 || sql->step() != SQLITE_DONE) {
1002 LOG_ERROR("Could not delete object store from KeyGenerators table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1003 return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not delete key generator for deleted object store") };
1007 // Delete all associated records
1009 auto* sql = cachedStatement(SQL::DeleteObjectStoreRecords, ASCIILiteral("DELETE FROM Records WHERE objectStoreID = ?;"));
1011 || sql->bindInt64(1, objectStoreIdentifier) != SQLITE_OK
1012 || sql->step() != SQLITE_DONE) {
1013 LOG_ERROR("Could not delete records for object store %" PRIi64 " (%i) - %s", objectStoreIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1014 return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not delete records for deleted object store") };
1018 // Delete all associated Indexes
1020 auto* sql = cachedStatement(SQL::DeleteObjectStoreIndexInfo, ASCIILiteral("DELETE FROM IndexInfo WHERE objectStoreID = ?;"));
1022 || sql->bindInt64(1, objectStoreIdentifier) != SQLITE_OK
1023 || sql->step() != SQLITE_DONE) {
1024 LOG_ERROR("Could not delete index from IndexInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1025 return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not delete IDBIndex for deleted object store") };
1029 // Delete all associated Index records
1031 auto* sql = cachedStatement(SQL::DeleteObjectStoreIndexRecords, ASCIILiteral("DELETE FROM IndexRecords WHERE objectStoreID = ?;"));
1033 || sql->bindInt64(1, objectStoreIdentifier) != SQLITE_OK
1034 || sql->step() != SQLITE_DONE) {
1035 LOG_ERROR("Could not delete index records(%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1036 return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not delete IDBIndex records for deleted object store") };
1040 // Delete all unused Blob URL records.
1042 auto* sql = cachedStatement(SQL::DeleteObjectStoreBlobRecords, ASCIILiteral("DELETE FROM BlobRecords WHERE objectStoreRow NOT IN (SELECT recordID FROM Records)"));
1044 || sql->step() != SQLITE_DONE) {
1045 LOG_ERROR("Could not delete Blob URL records(%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1046 return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not delete stored blob records for deleted object store") };
1050 // Delete all unused Blob File records.
1051 auto error = deleteUnusedBlobFileRecords(*transaction);
1052 if (!error.isNull())
1055 m_databaseInfo->deleteObjectStore(objectStoreIdentifier);
1060 IDBError SQLiteIDBBackingStore::renameObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const String& newName)
1062 LOG(IndexedDB, "SQLiteIDBBackingStore::renameObjectStore - object store %" PRIu64, objectStoreIdentifier);
1065 ASSERT(m_sqliteDB->isOpen());
1067 auto* transaction = m_transactions.get(transactionIdentifier);
1068 if (!transaction || !transaction->inProgress()) {
1069 LOG_ERROR("Attempt to rename an object store without an in-progress transaction");
1070 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to rename an object store without an in-progress transaction") };
1072 if (transaction->mode() != IDBTransactionMode::Versionchange) {
1073 LOG_ERROR("Attempt to rename an object store in a non-version-change transaction");
1074 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to rename an object store in a non-version-change transaction") };
1078 auto* sql = cachedStatement(SQL::RenameObjectStore, ASCIILiteral("UPDATE ObjectStoreInfo SET name = ? WHERE id = ?;"));
1080 || sql->bindText(1, newName) != SQLITE_OK
1081 || sql->bindInt64(2, objectStoreIdentifier) != SQLITE_OK
1082 || sql->step() != SQLITE_DONE) {
1083 LOG_ERROR("Could not update name for object store id %" PRIi64 " in ObjectStoreInfo table (%i) - %s", objectStoreIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1084 return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not rename object store") };
1088 m_databaseInfo->renameObjectStore(objectStoreIdentifier, newName);
1093 IDBError SQLiteIDBBackingStore::clearObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID)
1095 LOG(IndexedDB, "SQLiteIDBBackingStore::clearObjectStore - object store %" PRIu64, objectStoreID);
1098 ASSERT(m_sqliteDB->isOpen());
1100 auto* transaction = m_transactions.get(transactionIdentifier);
1101 if (!transaction || !transaction->inProgress()) {
1102 LOG_ERROR("Attempt to clear an object store without an in-progress transaction");
1103 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to clear an object store without an in-progress transaction") };
1105 if (transaction->mode() == IDBTransactionMode::Readonly) {
1106 LOG_ERROR("Attempt to clear an object store in a read-only transaction");
1107 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to clear an object store in a read-only transaction") };
1111 auto* sql = cachedStatement(SQL::ClearObjectStoreRecords, ASCIILiteral("DELETE FROM Records WHERE objectStoreID = ?;"));
1113 || sql->bindInt64(1, objectStoreID) != SQLITE_OK
1114 || sql->step() != SQLITE_DONE) {
1115 LOG_ERROR("Could not clear records from object store id %" PRIi64 " (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1116 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to clear object store") };
1121 auto* sql = cachedStatement(SQL::ClearObjectStoreIndexRecords, ASCIILiteral("DELETE FROM IndexRecords WHERE objectStoreID = ?;"));
1123 || sql->bindInt64(1, objectStoreID) != SQLITE_OK
1124 || sql->step() != SQLITE_DONE) {
1125 LOG_ERROR("Could not delete records from index record store id %" PRIi64 " (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1126 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to delete index records while clearing object store") };
1130 transaction->notifyCursorsOfChanges(objectStoreID);
1135 IDBError SQLiteIDBBackingStore::createIndex(const IDBResourceIdentifier& transactionIdentifier, const IDBIndexInfo& info)
1137 LOG(IndexedDB, "SQLiteIDBBackingStore::createIndex - ObjectStore %" PRIu64 ", Index %" PRIu64, info.objectStoreIdentifier(), info.identifier());
1139 ASSERT(m_sqliteDB->isOpen());
1141 auto* transaction = m_transactions.get(transactionIdentifier);
1142 if (!transaction || !transaction->inProgress()) {
1143 LOG_ERROR("Attempt to create an index without an in-progress transaction");
1144 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to create an index without an in-progress transaction") };
1146 if (transaction->mode() != IDBTransactionMode::Versionchange) {
1147 LOG_ERROR("Attempt to create an index in a non-version-change transaction");
1148 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to create an index in a non-version-change transaction") };
1151 RefPtr<SharedBuffer> keyPathBlob = serializeIDBKeyPath(info.keyPath());
1153 LOG_ERROR("Unable to serialize IDBKeyPath to save in database");
1154 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKeyPath to create index in database") };
1157 auto* sql = cachedStatement(SQL::CreateIndexInfo, ASCIILiteral("INSERT INTO IndexInfo VALUES (?, ?, ?, ?, ?, ?);"));
1159 || sql->bindInt64(1, info.identifier()) != SQLITE_OK
1160 || sql->bindText(2, info.name()) != SQLITE_OK
1161 || sql->bindInt64(3, info.objectStoreIdentifier()) != SQLITE_OK
1162 || sql->bindBlob(4, keyPathBlob->data(), keyPathBlob->size()) != SQLITE_OK
1163 || sql->bindInt(5, info.unique()) != SQLITE_OK
1164 || sql->bindInt(6, info.multiEntry()) != SQLITE_OK
1165 || sql->step() != SQLITE_DONE) {
1166 LOG_ERROR("Could not add index '%s' to IndexInfo table (%i) - %s", info.name().utf8().data(), m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1167 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to create index in database") };
1170 // Write index records for any records that already exist in this object store.
1172 auto cursor = transaction->maybeOpenBackingStoreCursor(info.objectStoreIdentifier(), 0, IDBKeyRangeData::allKeys());
1175 LOG_ERROR("Cannot open cursor to populate indexes in database");
1176 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to populate indexes in database") };
1179 while (!cursor->currentKey().isNull()) {
1180 auto& key = cursor->currentKey();
1181 auto* value = cursor->currentValue();
1182 ThreadSafeDataBuffer valueBuffer = value ? value->data() : ThreadSafeDataBuffer();
1184 ASSERT(cursor->currentRecordRowID());
1186 IDBError error = updateOneIndexForAddRecord(info, key, valueBuffer, cursor->currentRecordRowID());
1187 if (!error.isNull()) {
1188 auto* sql = cachedStatement(SQL::DeleteIndexInfo, ASCIILiteral("DELETE FROM IndexInfo WHERE id = ? AND objectStoreID = ?;"));
1190 || sql->bindInt64(1, info.identifier()) != SQLITE_OK
1191 || sql->bindInt64(2, info.objectStoreIdentifier()) != SQLITE_OK
1192 || sql->step() != SQLITE_DONE) {
1193 LOG_ERROR("Index creation failed due to uniqueness constraint failure, but there was an error deleting the Index record from the database");
1194 return { IDBDatabaseException::UnknownError, ASCIILiteral("Index creation failed due to uniqueness constraint failure, but there was an error deleting the Index record from the database") };
1200 if (!cursor->advance(1)) {
1201 LOG_ERROR("Error advancing cursor while indexing existing records for new index.");
1202 return { IDBDatabaseException::UnknownError, ASCIILiteral("Error advancing cursor while indexing existing records for new index") };
1206 auto* objectStore = m_databaseInfo->infoForExistingObjectStore(info.objectStoreIdentifier());
1207 ASSERT(objectStore);
1208 objectStore->addExistingIndex(info);
1213 IDBError SQLiteIDBBackingStore::uncheckedHasIndexRecord(const IDBIndexInfo& info, const IDBKeyData& indexKey, bool& hasRecord)
1217 RefPtr<SharedBuffer> indexKeyBuffer = serializeIDBKeyData(indexKey);
1218 if (!indexKeyBuffer) {
1219 LOG_ERROR("Unable to serialize index key to be stored in the database");
1220 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKey to check for index record in database") };
1223 auto* sql = cachedStatement(SQL::HasIndexRecord, ASCIILiteral("SELECT rowid FROM IndexRecords WHERE indexID = ? AND objectStoreID = ? AND key = CAST(? AS TEXT);"));
1225 || sql->bindInt64(1, info.identifier()) != SQLITE_OK
1226 || sql->bindInt64(2, info.objectStoreIdentifier()) != SQLITE_OK
1227 || sql->bindBlob(3, indexKeyBuffer->data(), indexKeyBuffer->size()) != SQLITE_OK) {
1228 LOG_ERROR("Error checking for index record in database");
1229 return { IDBDatabaseException::UnknownError, ASCIILiteral("Error checking for index record in database") };
1232 int sqlResult = sql->step();
1233 if (sqlResult == SQLITE_OK || sqlResult == SQLITE_DONE)
1236 if (sqlResult != SQLITE_ROW) {
1237 // There was an error fetching the record from the database.
1238 LOG_ERROR("Could not check if key exists in index (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1239 return { IDBDatabaseException::UnknownError, ASCIILiteral("Error checking for existence of IDBKey in index") };
1246 IDBError SQLiteIDBBackingStore::uncheckedPutIndexKey(const IDBIndexInfo& info, const IDBKeyData& key, const IndexKey& indexKey, int64_t recordID)
1248 LOG(IndexedDB, "SQLiteIDBBackingStore::uncheckedPutIndexKey - (%" PRIu64 ") %s, %s", info.identifier(), key.loggingString().utf8().data(), indexKey.asOneKey().loggingString().utf8().data());
1250 Vector<IDBKeyData> indexKeys;
1251 if (info.multiEntry())
1252 indexKeys = indexKey.multiEntry();
1254 indexKeys.append(indexKey.asOneKey());
1256 if (info.unique()) {
1259 for (auto& indexKey : indexKeys) {
1260 if (!indexKey.isValid())
1262 error = uncheckedHasIndexRecord(info, indexKey, hasRecord);
1263 if (!error.isNull())
1266 return IDBError(IDBDatabaseException::ConstraintError);
1270 for (auto& indexKey : indexKeys) {
1271 if (!indexKey.isValid())
1273 auto error = uncheckedPutIndexRecord(info.objectStoreIdentifier(), info.identifier(), key, indexKey, recordID);
1274 if (!error.isNull()) {
1275 LOG_ERROR("Unable to put index record for newly created index");
1283 IDBError SQLiteIDBBackingStore::uncheckedPutIndexRecord(int64_t objectStoreID, int64_t indexID, const WebCore::IDBKeyData& keyValue, const WebCore::IDBKeyData& indexKey, int64_t recordID)
1285 LOG(IndexedDB, "SQLiteIDBBackingStore::uncheckedPutIndexRecord - %s, %s", keyValue.loggingString().utf8().data(), indexKey.loggingString().utf8().data());
1287 RefPtr<SharedBuffer> indexKeyBuffer = serializeIDBKeyData(indexKey);
1288 if (!indexKeyBuffer) {
1289 LOG_ERROR("Unable to serialize index key to be stored in the database");
1290 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize index key to be stored in the database") };
1293 RefPtr<SharedBuffer> valueBuffer = serializeIDBKeyData(keyValue);
1295 LOG_ERROR("Unable to serialize the value to be stored in the database");
1296 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize value to be stored in the database") };
1300 auto* sql = cachedStatement(SQL::PutIndexRecord, ASCIILiteral("INSERT INTO IndexRecords VALUES (?, ?, CAST(? AS TEXT), CAST(? AS TEXT), ?);"));
1302 || sql->bindInt64(1, indexID) != SQLITE_OK
1303 || sql->bindInt64(2, objectStoreID) != SQLITE_OK
1304 || sql->bindBlob(3, indexKeyBuffer->data(), indexKeyBuffer->size()) != SQLITE_OK
1305 || sql->bindBlob(4, valueBuffer->data(), valueBuffer->size()) != SQLITE_OK
1306 || sql->bindInt64(5, recordID) != SQLITE_OK
1307 || sql->step() != SQLITE_DONE) {
1308 LOG_ERROR("Could not put index record for index %" PRIi64 " in object store %" PRIi64 " in Records table (%i) - %s", indexID, objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1309 return { IDBDatabaseException::UnknownError, ASCIILiteral("Error putting index record into database") };
1317 IDBError SQLiteIDBBackingStore::deleteIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier)
1319 LOG(IndexedDB, "SQLiteIDBBackingStore::deleteIndex - object store %" PRIu64, objectStoreIdentifier);
1322 ASSERT(m_sqliteDB->isOpen());
1324 auto* transaction = m_transactions.get(transactionIdentifier);
1325 if (!transaction || !transaction->inProgress()) {
1326 LOG_ERROR("Attempt to delete index without an in-progress transaction");
1327 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete index without an in-progress transaction") };
1330 if (transaction->mode() != IDBTransactionMode::Versionchange) {
1331 LOG_ERROR("Attempt to delete index during a non-version-change transaction");
1332 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete index during a non-version-change transaction") };
1336 auto* sql = cachedStatement(SQL::DeleteIndexInfo, ASCIILiteral("DELETE FROM IndexInfo WHERE id = ? AND objectStoreID = ?;"));
1338 || sql->bindInt64(1, indexIdentifier) != SQLITE_OK
1339 || sql->bindInt64(2, objectStoreIdentifier) != SQLITE_OK
1340 || sql->step() != SQLITE_DONE) {
1341 LOG_ERROR("Could not delete index id %" PRIi64 " from IndexInfo table (%i) - %s", objectStoreIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1342 return { IDBDatabaseException::UnknownError, ASCIILiteral("Error deleting index from database") };
1347 auto* sql = cachedStatement(SQL::DeleteIndexRecords, ASCIILiteral("DELETE FROM IndexRecords WHERE indexID = ? AND objectStoreID = ?;"));
1349 || sql->bindInt64(1, indexIdentifier) != SQLITE_OK
1350 || sql->bindInt64(2, objectStoreIdentifier) != SQLITE_OK
1351 || sql->step() != SQLITE_DONE) {
1352 LOG_ERROR("Could not delete index records for index id %" PRIi64 " from IndexRecords table (%i) - %s", indexIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1353 return { IDBDatabaseException::UnknownError, ASCIILiteral("Error deleting index records from database") };
1357 auto* objectStore = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
1358 ASSERT(objectStore);
1359 objectStore->deleteIndex(indexIdentifier);
1364 IDBError SQLiteIDBBackingStore::renameIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName)
1366 LOG(IndexedDB, "SQLiteIDBBackingStore::renameIndex - object store %" PRIu64 ", index %" PRIu64, objectStoreIdentifier, indexIdentifier);
1369 ASSERT(m_sqliteDB->isOpen());
1371 auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
1372 if (!objectStoreInfo)
1373 return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not rename index") };
1375 auto* indexInfo = objectStoreInfo->infoForExistingIndex(indexIdentifier);
1377 return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not rename index") };
1379 auto* transaction = m_transactions.get(transactionIdentifier);
1380 if (!transaction || !transaction->inProgress()) {
1381 LOG_ERROR("Attempt to rename an index without an in-progress transaction");
1382 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to rename an index without an in-progress transaction") };
1385 if (transaction->mode() != IDBTransactionMode::Versionchange) {
1386 LOG_ERROR("Attempt to rename an index in a non-version-change transaction");
1387 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to rename an index in a non-version-change transaction") };
1391 auto* sql = cachedStatement(SQL::RenameIndex, ASCIILiteral("UPDATE IndexInfo SET name = ? WHERE objectStoreID = ? AND id = ?;"));
1393 || sql->bindText(1, newName) != SQLITE_OK
1394 || sql->bindInt64(2, objectStoreIdentifier) != SQLITE_OK
1395 || sql->bindInt64(3, indexIdentifier) != SQLITE_OK
1396 || sql->step() != SQLITE_DONE) {
1397 LOG_ERROR("Could not update name for index id (%" PRIi64 ", %" PRIi64 ") in IndexInfo table (%i) - %s", objectStoreIdentifier, indexIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1398 return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not rename index") };
1402 indexInfo->rename(newName);
1407 IDBError SQLiteIDBBackingStore::keyExistsInObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID, const IDBKeyData& keyData, bool& keyExists)
1409 LOG(IndexedDB, "SQLiteIDBBackingStore::keyExistsInObjectStore - key %s, object store %" PRIu64, keyData.loggingString().utf8().data(), objectStoreID);
1412 ASSERT(m_sqliteDB->isOpen());
1416 auto* transaction = m_transactions.get(transactionIdentifier);
1417 if (!transaction || !transaction->inProgress()) {
1418 LOG_ERROR("Attempt to see if key exists in objectstore without an in-progress transaction");
1419 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to see if key exists in objectstore without an in-progress transaction") };
1422 RefPtr<SharedBuffer> keyBuffer = serializeIDBKeyData(keyData);
1424 LOG_ERROR("Unable to serialize IDBKey to check for existence in object store");
1425 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKey to check for existence in object store") };
1427 auto* sql = cachedStatement(SQL::KeyExistsInObjectStore, ASCIILiteral("SELECT key FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT) LIMIT 1;"));
1429 || sql->bindInt64(1, objectStoreID) != SQLITE_OK
1430 || sql->bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK) {
1431 LOG_ERROR("Could not get record from object store %" PRIi64 " from Records table (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1432 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to check for existence of IDBKey in object store") };
1435 int sqlResult = sql->step();
1436 if (sqlResult == SQLITE_OK || sqlResult == SQLITE_DONE)
1439 if (sqlResult != SQLITE_ROW) {
1440 // There was an error fetching the record from the database.
1441 LOG_ERROR("Could not check if key exists in object store (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1442 return { IDBDatabaseException::UnknownError, ASCIILiteral("Error checking for existence of IDBKey in object store") };
1449 IDBError SQLiteIDBBackingStore::deleteUnusedBlobFileRecords(SQLiteIDBTransaction& transaction)
1451 LOG(IndexedDB, "SQLiteIDBBackingStore::deleteUnusedBlobFileRecords");
1453 // Gather the set of blob URLs and filenames that are no longer in use.
1454 HashSet<String> removedBlobFilenames;
1456 auto* sql = cachedStatement(SQL::GetUnusedBlobFilenames, ASCIILiteral("SELECT fileName FROM BlobFiles WHERE blobURL NOT IN (SELECT blobURL FROM BlobRecords)"));
1459 LOG_ERROR("Error deleting stored blobs (%i) (Could not gather unused blobURLs) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1460 return { IDBDatabaseException::UnknownError, ASCIILiteral("Error deleting stored blobs") };
1463 int result = sql->step();
1464 while (result == SQLITE_ROW) {
1465 removedBlobFilenames.add(sql->getColumnText(0));
1466 result = sql->step();
1469 if (result != SQLITE_DONE) {
1470 LOG_ERROR("Error deleting stored blobs (%i) (Could not gather unused blobURLs) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1471 return { IDBDatabaseException::UnknownError, ASCIILiteral("Error deleting stored blobs") };
1475 // Remove the blob records that are no longer in use.
1476 if (!removedBlobFilenames.isEmpty()) {
1477 auto* sql = cachedStatement(SQL::DeleteUnusedBlobs, ASCIILiteral("DELETE FROM BlobFiles WHERE blobURL NOT IN (SELECT blobURL FROM BlobRecords)"));
1480 || sql->step() != SQLITE_DONE) {
1481 LOG_ERROR("Error deleting stored blobs (%i) (Could not delete blobFile records) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1482 return { IDBDatabaseException::UnknownError, ASCIILiteral("Error deleting stored blobs") };
1486 for (auto& file : removedBlobFilenames)
1487 transaction.addRemovedBlobFile(file);
1492 IDBError SQLiteIDBBackingStore::deleteRecord(SQLiteIDBTransaction& transaction, int64_t objectStoreID, const IDBKeyData& keyData)
1494 LOG(IndexedDB, "SQLiteIDBBackingStore::deleteRecord - key %s, object store %" PRIu64, keyData.loggingString().utf8().data(), objectStoreID);
1497 ASSERT(m_sqliteDB->isOpen());
1498 ASSERT(transaction.inProgress());
1499 ASSERT(transaction.mode() != IDBTransactionMode::Readonly);
1500 UNUSED_PARAM(transaction);
1502 RefPtr<SharedBuffer> keyBuffer = serializeIDBKeyData(keyData);
1504 LOG_ERROR("Unable to serialize IDBKeyData to be removed from the database");
1505 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKeyData to be removed from the database") };
1508 // Get the record ID
1511 auto* sql = cachedStatement(SQL::GetObjectStoreRecordID, ASCIILiteral("SELECT recordID FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT);"));
1514 || sql->bindInt64(1, objectStoreID) != SQLITE_OK
1515 || sql->bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK) {
1516 LOG_ERROR("Could not delete record from object store %" PRIi64 " (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1517 return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete record from object store") };
1520 int result = sql->step();
1522 // If there's no record ID, there's no record to delete.
1523 if (result == SQLITE_DONE)
1526 if (result != SQLITE_ROW) {
1527 LOG_ERROR("Could not delete record from object store %" PRIi64 " (%i) (unable to fetch record ID) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1528 return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete record from object store") };
1531 recordID = sql->getColumnInt64(0);
1535 LOG_ERROR("Could not delete record from object store %" PRIi64 " (%i) (record ID is invalid) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1536 return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete record from object store") };
1539 // Delete the blob records for this object store record.
1541 auto* sql = cachedStatement(SQL::DeleteBlobRecord, ASCIILiteral("DELETE FROM BlobRecords WHERE objectStoreRow = ?;"));
1544 || sql->bindInt64(1, recordID) != SQLITE_OK
1545 || sql->step() != SQLITE_DONE) {
1546 LOG_ERROR("Could not delete record from object store %" PRIi64 " (%i) (Could not delete BlobRecords records) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1547 return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete record from object store") };
1551 auto error = deleteUnusedBlobFileRecords(transaction);
1552 if (!error.isNull())
1555 // Delete record from object store
1557 auto* sql = cachedStatement(SQL::DeleteObjectStoreRecord, ASCIILiteral("DELETE FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT);"));
1560 || sql->bindInt64(1, objectStoreID) != SQLITE_OK
1561 || sql->bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK
1562 || sql->step() != SQLITE_DONE) {
1563 LOG_ERROR("Could not delete record from object store %" PRIi64 " (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1564 return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete record from object store") };
1568 // Delete record from indexes store
1570 auto* sql = cachedStatement(SQL::DeleteObjectStoreIndexRecord, ASCIILiteral("DELETE FROM IndexRecords WHERE objectStoreID = ? AND value = CAST(? AS TEXT);"));
1573 || sql->bindInt64(1, objectStoreID) != SQLITE_OK
1574 || sql->bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK
1575 || sql->step() != SQLITE_DONE) {
1576 LOG_ERROR("Could not delete record from indexes for object store %" PRIi64 " (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1577 return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete index entries for object store record") };
1584 IDBError SQLiteIDBBackingStore::deleteRange(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID, const IDBKeyRangeData& keyRange)
1586 LOG(IndexedDB, "SQLiteIDBBackingStore::deleteRange - range %s, object store %" PRIu64, keyRange.loggingString().utf8().data(), objectStoreID);
1589 ASSERT(m_sqliteDB->isOpen());
1591 auto* transaction = m_transactions.get(transactionIdentifier);
1592 if (!transaction || !transaction->inProgress()) {
1593 LOG_ERROR("Attempt to delete range from database without an in-progress transaction");
1594 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete range from database without an in-progress transaction") };
1596 if (transaction->mode() == IDBTransactionMode::Readonly) {
1597 LOG_ERROR("Attempt to delete records from an object store in a read-only transaction");
1598 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete records from an object store in a read-only transaction") };
1601 // If the range to delete is exactly one key we can delete it right now.
1602 if (keyRange.isExactlyOneKey()) {
1603 auto error = deleteRecord(*transaction, objectStoreID, keyRange.lowerKey);
1604 if (!error.isNull()) {
1605 LOG_ERROR("Failed to delete record for key '%s'", keyRange.lowerKey.loggingString().utf8().data());
1609 transaction->notifyCursorsOfChanges(objectStoreID);
1614 auto cursor = transaction->maybeOpenBackingStoreCursor(objectStoreID, 0, keyRange);
1616 LOG_ERROR("Cannot open cursor to delete range of records from the database");
1617 return { IDBDatabaseException::UnknownError, ASCIILiteral("Cannot open cursor to delete range of records from the database") };
1620 Vector<IDBKeyData> keys;
1621 while (!cursor->didComplete() && !cursor->didError()) {
1622 keys.append(cursor->currentKey());
1626 if (cursor->didError()) {
1627 LOG_ERROR("Cursor failed while accumulating range of records from the database");
1628 return { IDBDatabaseException::UnknownError, ASCIILiteral("Cursor failed while accumulating range of records from the database") };
1632 for (auto& key : keys) {
1633 error = deleteRecord(*transaction, objectStoreID, key);
1634 if (!error.isNull()) {
1635 LOG_ERROR("deleteRange: Error deleting keys in range");
1640 transaction->notifyCursorsOfChanges(objectStoreID);
1645 IDBError SQLiteIDBBackingStore::updateOneIndexForAddRecord(const IDBIndexInfo& info, const IDBKeyData& key, const ThreadSafeDataBuffer& value, int64_t recordID)
1647 JSLockHolder locker(vm());
1649 auto jsValue = deserializeIDBValueToJSValue(*globalObject().globalExec(), value);
1650 if (jsValue.isUndefinedOrNull())
1654 generateIndexKeyForValue(*m_globalObject->globalExec(), info, jsValue, indexKey);
1656 if (indexKey.isNull())
1659 return uncheckedPutIndexKey(info, key, indexKey, recordID);
1662 IDBError SQLiteIDBBackingStore::updateAllIndexesForAddRecord(const IDBObjectStoreInfo& info, const IDBKeyData& key, const ThreadSafeDataBuffer& value, int64_t recordID)
1664 JSLockHolder locker(vm());
1666 auto jsValue = deserializeIDBValueToJSValue(*globalObject().globalExec(), value);
1667 if (jsValue.isUndefinedOrNull())
1671 bool anyRecordsSucceeded = false;
1672 for (auto& index : info.indexMap().values()) {
1674 generateIndexKeyForValue(*m_globalObject->globalExec(), index, jsValue, indexKey);
1676 if (indexKey.isNull())
1679 error = uncheckedPutIndexKey(index, key, indexKey, recordID);
1680 if (!error.isNull())
1683 anyRecordsSucceeded = true;
1686 if (!error.isNull() && anyRecordsSucceeded) {
1687 RefPtr<SharedBuffer> keyBuffer = serializeIDBKeyData(key);
1689 auto* sql = cachedStatement(SQL::DeleteObjectStoreIndexRecord, ASCIILiteral("DELETE FROM IndexRecords WHERE objectStoreID = ? AND value = CAST(? AS TEXT);"));
1692 || sql->bindInt64(1, info.identifier()) != SQLITE_OK
1693 || sql->bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK
1694 || sql->step() != SQLITE_DONE) {
1695 LOG_ERROR("Adding one Index record failed, but failed to remove all others that previously succeeded");
1696 return { IDBDatabaseException::UnknownError, ASCIILiteral("Adding one Index record failed, but failed to remove all others that previously succeeded") };
1703 IDBError SQLiteIDBBackingStore::addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo& objectStoreInfo, const IDBKeyData& keyData, const IDBValue& value)
1705 LOG(IndexedDB, "SQLiteIDBBackingStore::addRecord - key %s, object store %" PRIu64, keyData.loggingString().utf8().data(), objectStoreInfo.identifier());
1708 ASSERT(m_sqliteDB->isOpen());
1709 ASSERT(value.data().data());
1710 ASSERT(value.blobURLs().size() == value.blobFilePaths().size());
1712 auto* transaction = m_transactions.get(transactionIdentifier);
1713 if (!transaction || !transaction->inProgress()) {
1714 LOG_ERROR("Attempt to store a record in an object store without an in-progress transaction");
1715 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to store a record in an object store without an in-progress transaction") };
1717 if (transaction->mode() == IDBTransactionMode::Readonly) {
1718 LOG_ERROR("Attempt to store a record in an object store in a read-only transaction");
1719 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to store a record in an object store in a read-only transaction") };
1722 RefPtr<SharedBuffer> keyBuffer = serializeIDBKeyData(keyData);
1724 LOG_ERROR("Unable to serialize IDBKey to be stored in an object store");
1725 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKey to be stored in an object store") };
1728 int64_t recordID = 0;
1730 auto* sql = cachedStatement(SQL::AddObjectStoreRecord, ASCIILiteral("INSERT INTO Records VALUES (?, CAST(? AS TEXT), ?, NULL);"));
1732 || sql->bindInt64(1, objectStoreInfo.identifier()) != SQLITE_OK
1733 || sql->bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK
1734 || sql->bindBlob(3, value.data().data()->data(), value.data().data()->size()) != SQLITE_OK
1735 || sql->step() != SQLITE_DONE) {
1736 LOG_ERROR("Could not put record for object store %" PRIi64 " in Records table (%i) - %s", objectStoreInfo.identifier(), m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1737 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to store record in object store") };
1740 recordID = m_sqliteDB->lastInsertRowID();
1743 auto error = updateAllIndexesForAddRecord(objectStoreInfo, keyData, value.data(), recordID);
1745 if (!error.isNull()) {
1746 auto* sql = cachedStatement(SQL::DeleteObjectStoreRecord, ASCIILiteral("DELETE FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT);"));
1748 || sql->bindInt64(1, objectStoreInfo.identifier()) != SQLITE_OK
1749 || sql->bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK
1750 || sql->step() != SQLITE_DONE) {
1751 LOG_ERROR("Indexing new object store record failed, but unable to remove the object store record itself");
1752 return { IDBDatabaseException::UnknownError, ASCIILiteral("Indexing new object store record failed, but unable to remove the object store record itself") };
1758 const Vector<String>& blobURLs = value.blobURLs();
1759 const Vector<String>& blobFiles = value.blobFilePaths();
1760 for (size_t i = 0; i < blobURLs.size(); ++i) {
1761 auto& url = blobURLs[i];
1763 auto* sql = cachedStatement(SQL::AddBlobRecord, ASCIILiteral("INSERT INTO BlobRecords VALUES (?, ?);"));
1765 || sql->bindInt64(1, recordID) != SQLITE_OK
1766 || sql->bindText(2, url) != SQLITE_OK
1767 || sql->step() != SQLITE_DONE) {
1768 LOG_ERROR("Unable to record Blob record in database");
1769 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to record Blob record in database") };
1772 int64_t potentialFileNameInteger = m_sqliteDB->lastInsertRowID();
1774 // If we already have a file for this blobURL, nothing left to do.
1776 auto* sql = cachedStatement(SQL::BlobFilenameForBlobURL, ASCIILiteral("SELECT fileName FROM BlobFiles WHERE blobURL = ?;"));
1778 || sql->bindText(1, url) != SQLITE_OK) {
1779 LOG_ERROR("Unable to examine Blob filenames in database");
1780 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to examine Blob filenames in database") };
1783 int result = sql->step();
1784 if (result != SQLITE_ROW && result != SQLITE_DONE) {
1785 LOG_ERROR("Unable to examine Blob filenames in database");
1786 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to examine Blob filenames in database") };
1789 if (result == SQLITE_ROW)
1793 // We don't already have a file for this blobURL, so commit our file as a unique filename
1794 String storedFilename = String::format("%" PRId64 ".blob", potentialFileNameInteger);
1796 auto* sql = cachedStatement(SQL::AddBlobFilename, ASCIILiteral("INSERT INTO BlobFiles VALUES (?, ?);"));
1798 || sql->bindText(1, url) != SQLITE_OK
1799 || sql->bindText(2, storedFilename) != SQLITE_OK
1800 || sql->step() != SQLITE_DONE) {
1801 LOG_ERROR("Unable to record Blob file record in database");
1802 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to record Blob file record in database") };
1806 transaction->addBlobFile(blobFiles[i], storedFilename);
1809 transaction->notifyCursorsOfChanges(objectStoreInfo.identifier());
1814 IDBError SQLiteIDBBackingStore::getBlobRecordsForObjectStoreRecord(int64_t objectStoreRecord, Vector<String>& blobURLs, Vector<String>& blobFilePaths)
1816 ASSERT(objectStoreRecord);
1818 HashSet<String> blobURLSet;
1820 auto* sql = cachedStatement(SQL::GetBlobURL, ASCIILiteral("SELECT blobURL FROM BlobRecords WHERE objectStoreRow = ?"));
1822 || sql->bindInt64(1, objectStoreRecord) != SQLITE_OK) {
1823 LOG_ERROR("Could not prepare statement to fetch blob URLs for object store record (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1824 return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to look up blobURL records in object store by key range") };
1827 int sqlResult = sql->step();
1828 if (sqlResult == SQLITE_OK || sqlResult == SQLITE_DONE) {
1829 // There are no blobURLs in the database for this object store record.
1833 while (sqlResult == SQLITE_ROW) {
1834 blobURLSet.add(sql->getColumnText(0));
1835 sqlResult = sql->step();
1838 if (sqlResult != SQLITE_DONE) {
1839 LOG_ERROR("Could not fetch blob URLs for object store record (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1840 return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to look up blobURL records in object store by key range") };
1844 ASSERT(!blobURLSet.isEmpty());
1845 String databaseDirectory = fullDatabaseDirectory();
1846 for (auto& blobURL : blobURLSet) {
1847 auto* sql = cachedStatement(SQL::BlobFilenameForBlobURL, ASCIILiteral("SELECT fileName FROM BlobFiles WHERE blobURL = ?;"));
1849 || sql->bindText(1, blobURL) != SQLITE_OK) {
1850 LOG_ERROR("Could not prepare statement to fetch blob filename for object store record (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1851 return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to look up blobURL records in object store by key range") };
1854 if (sql->step() != SQLITE_ROW) {
1855 LOG_ERROR("Entry for blob filename for blob url %s does not exist (%i) - %s", blobURL.utf8().data(), m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1856 return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to look up blobURL records in object store by key range") };
1859 blobURLs.append(blobURL);
1861 String fileName = sql->getColumnText(0);
1862 blobFilePaths.append(pathByAppendingComponent(databaseDirectory, fileName));
1868 IDBError SQLiteIDBBackingStore::getRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID, const IDBKeyRangeData& keyRange, IDBGetRecordDataType type, IDBGetResult& resultValue)
1870 LOG(IndexedDB, "SQLiteIDBBackingStore::getRecord - key range %s, object store %" PRIu64, keyRange.loggingString().utf8().data(), objectStoreID);
1873 ASSERT(m_sqliteDB->isOpen());
1875 auto* transaction = m_transactions.get(transactionIdentifier);
1876 if (!transaction || !transaction->inProgress()) {
1877 LOG_ERROR("Attempt to get a record from database without an in-progress transaction");
1878 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to get a record from database without an in-progress transaction") };
1881 auto key = keyRange.lowerKey;
1883 key = IDBKeyData::minimum();
1884 RefPtr<SharedBuffer> lowerBuffer = serializeIDBKeyData(key);
1886 LOG_ERROR("Unable to serialize lower IDBKey in lookup range");
1887 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize lower IDBKey in lookup range") };
1890 key = keyRange.upperKey;
1892 key = IDBKeyData::maximum();
1893 RefPtr<SharedBuffer> upperBuffer = serializeIDBKeyData(key);
1895 LOG_ERROR("Unable to serialize upper IDBKey in lookup range");
1896 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize upper IDBKey in lookup range") };
1899 int64_t recordID = 0;
1900 ThreadSafeDataBuffer resultBuffer;
1902 static NeverDestroyed<ASCIILiteral> lowerOpenUpperOpen("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
1903 static NeverDestroyed<ASCIILiteral> lowerOpenUpperClosed("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
1904 static NeverDestroyed<ASCIILiteral> lowerClosedUpperOpen("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
1905 static NeverDestroyed<ASCIILiteral> lowerClosedUpperClosed("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
1907 static NeverDestroyed<ASCIILiteral> lowerOpenUpperOpenKeyOnly("SELECT key FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
1908 static NeverDestroyed<ASCIILiteral> lowerOpenUpperClosedKeyOnly("SELECT key FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
1909 static NeverDestroyed<ASCIILiteral> lowerClosedUpperOpenKeyOnly("SELECT key FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
1910 static NeverDestroyed<ASCIILiteral> lowerClosedUpperClosedKeyOnly("SELECT key FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
1912 SQLiteStatement* sql = nullptr;
1915 case IDBGetRecordDataType::KeyAndValue:
1916 if (keyRange.lowerOpen) {
1917 if (keyRange.upperOpen)
1918 sql = cachedStatement(SQL::GetValueRecordsLowerOpenUpperOpen, lowerOpenUpperOpen.get());
1920 sql = cachedStatement(SQL::GetValueRecordsLowerOpenUpperClosed, lowerOpenUpperClosed.get());
1922 if (keyRange.upperOpen)
1923 sql = cachedStatement(SQL::GetValueRecordsLowerClosedUpperOpen, lowerClosedUpperOpen.get());
1925 sql = cachedStatement(SQL::GetValueRecordsLowerClosedUpperClosed, lowerClosedUpperClosed.get());
1928 case IDBGetRecordDataType::KeyOnly:
1929 if (keyRange.lowerOpen) {
1930 if (keyRange.upperOpen)
1931 sql = cachedStatement(SQL::GetKeyRecordsLowerOpenUpperOpen, lowerOpenUpperOpenKeyOnly.get());
1933 sql = cachedStatement(SQL::GetKeyRecordsLowerOpenUpperClosed, lowerOpenUpperClosedKeyOnly.get());
1935 if (keyRange.upperOpen)
1936 sql = cachedStatement(SQL::GetKeyRecordsLowerClosedUpperOpen, lowerClosedUpperOpenKeyOnly.get());
1938 sql = cachedStatement(SQL::GetKeyRecordsLowerClosedUpperClosed, lowerClosedUpperClosedKeyOnly.get());
1943 || sql->bindInt64(1, objectStoreID) != SQLITE_OK
1944 || sql->bindBlob(2, lowerBuffer->data(), lowerBuffer->size()) != SQLITE_OK
1945 || sql->bindBlob(3, upperBuffer->data(), upperBuffer->size()) != SQLITE_OK) {
1946 LOG_ERROR("Could not get key range record from object store %" PRIi64 " from Records table (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1947 return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to look up record in object store by key range") };
1950 int sqlResult = sql->step();
1952 if (sqlResult == SQLITE_OK || sqlResult == SQLITE_DONE) {
1953 // There was no record for the key in the database.
1956 if (sqlResult != SQLITE_ROW) {
1957 // There was an error fetching the record from the database.
1958 LOG_ERROR("Could not get record from object store %" PRIi64 " from Records table (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
1959 return { IDBDatabaseException::UnknownError, ASCIILiteral("Error looking up record in object store by key range") };
1962 Vector<uint8_t> buffer;
1963 sql->getColumnBlobAsVector(0, buffer);
1964 resultBuffer = ThreadSafeDataBuffer::adoptVector(buffer);
1966 if (type == IDBGetRecordDataType::KeyAndValue)
1967 recordID = sql->getColumnInt64(1);
1970 if (type == IDBGetRecordDataType::KeyOnly) {
1971 auto* vector = resultBuffer.data();
1973 LOG_ERROR("Unable to deserialize key data from database for IDBObjectStore.getKey()");
1974 return { IDBDatabaseException::UnknownError, ASCIILiteral("Error extracting key data from database executing IDBObjectStore.getKey()") };
1978 if (!deserializeIDBKeyData(vector->data(), vector->size(), keyData)) {
1979 LOG_ERROR("Unable to deserialize key data from database for IDBObjectStore.getKey()");
1980 return { IDBDatabaseException::UnknownError, ASCIILiteral("Error extracting key data from database executing IDBObjectStore.getKey()") };
1983 resultValue = { keyData };
1988 Vector<String> blobURLs, blobFilePaths;
1989 auto error = getBlobRecordsForObjectStoreRecord(recordID, blobURLs, blobFilePaths);
1990 ASSERT(blobURLs.size() == blobFilePaths.size());
1992 if (!error.isNull())
1995 resultValue = { { resultBuffer, WTFMove(blobURLs), WTFMove(blobFilePaths) } };
1999 IDBError SQLiteIDBBackingStore::getAllRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData& getAllRecordsData, IDBGetAllResult& result)
2001 return getAllRecordsData.indexIdentifier ? getAllIndexRecords(transactionIdentifier, getAllRecordsData, result) : getAllObjectStoreRecords(transactionIdentifier, getAllRecordsData, result);
2004 SQLiteStatement* SQLiteIDBBackingStore::cachedStatementForGetAllObjectStoreRecords(const IDBGetAllRecordsData& getAllRecordsData)
2006 static NeverDestroyed<ASCIILiteral> lowerOpenUpperOpenKey("SELECT key FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
2007 static NeverDestroyed<ASCIILiteral> lowerOpenUpperClosedKey("SELECT key FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
2008 static NeverDestroyed<ASCIILiteral> lowerClosedUpperOpenKey("SELECT key FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
2009 static NeverDestroyed<ASCIILiteral> lowerClosedUpperClosedKey("SELECT key FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
2010 static NeverDestroyed<ASCIILiteral> lowerOpenUpperOpenValue("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
2011 static NeverDestroyed<ASCIILiteral> lowerOpenUpperClosedValue("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
2012 static NeverDestroyed<ASCIILiteral> lowerClosedUpperOpenValue("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
2013 static NeverDestroyed<ASCIILiteral> lowerClosedUpperClosedValue("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
2015 if (getAllRecordsData.getAllType == IndexedDB::GetAllType::Keys) {
2016 if (getAllRecordsData.keyRangeData.lowerOpen) {
2017 if (getAllRecordsData.keyRangeData.upperOpen)
2018 return cachedStatement(SQL::GetAllKeyRecordsLowerOpenUpperOpen, lowerOpenUpperOpenKey.get());
2019 return cachedStatement(SQL::GetAllKeyRecordsLowerOpenUpperClosed, lowerOpenUpperClosedKey.get());
2022 if (getAllRecordsData.keyRangeData.upperOpen)
2023 return cachedStatement(SQL::GetAllKeyRecordsLowerClosedUpperOpen, lowerClosedUpperOpenKey.get());
2024 return cachedStatement(SQL::GetAllKeyRecordsLowerClosedUpperClosed, lowerClosedUpperClosedKey.get());
2027 if (getAllRecordsData.keyRangeData.lowerOpen) {
2028 if (getAllRecordsData.keyRangeData.upperOpen)
2029 return cachedStatement(SQL::GetValueRecordsLowerOpenUpperOpen, lowerOpenUpperOpenValue.get());
2030 return cachedStatement(SQL::GetValueRecordsLowerOpenUpperClosed, lowerOpenUpperClosedValue.get());
2033 if (getAllRecordsData.keyRangeData.upperOpen)
2034 return cachedStatement(SQL::GetValueRecordsLowerClosedUpperOpen, lowerClosedUpperOpenValue.get());
2035 return cachedStatement(SQL::GetValueRecordsLowerClosedUpperClosed, lowerClosedUpperClosedValue.get());
2038 IDBError SQLiteIDBBackingStore::getAllObjectStoreRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData& getAllRecordsData, IDBGetAllResult& result)
2040 LOG(IndexedDB, "SQLiteIDBBackingStore::getAllObjectStoreRecords");
2043 ASSERT(m_sqliteDB->isOpen());
2045 auto* transaction = m_transactions.get(transactionIdentifier);
2046 if (!transaction || !transaction->inProgress()) {
2047 LOG_ERROR("Attempt to get records from database without an in-progress transaction");
2048 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to get records from database without an in-progress transaction") };
2051 auto key = getAllRecordsData.keyRangeData.lowerKey;
2053 key = IDBKeyData::minimum();
2054 auto lowerBuffer = serializeIDBKeyData(key);
2056 LOG_ERROR("Unable to serialize lower IDBKey in lookup range");
2057 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize lower IDBKey in lookup range") };
2060 key = getAllRecordsData.keyRangeData.upperKey;
2062 key = IDBKeyData::maximum();
2063 auto upperBuffer = serializeIDBKeyData(key);
2065 LOG_ERROR("Unable to serialize upper IDBKey in lookup range");
2066 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize upper IDBKey in lookup range") };
2069 auto* sql = cachedStatementForGetAllObjectStoreRecords(getAllRecordsData);
2071 || sql->bindInt64(1, getAllRecordsData.objectStoreIdentifier) != SQLITE_OK
2072 || sql->bindBlob(2, lowerBuffer->data(), lowerBuffer->size()) != SQLITE_OK
2073 || sql->bindBlob(3, upperBuffer->data(), upperBuffer->size()) != SQLITE_OK) {
2074 LOG_ERROR("Could not get key range record from object store %" PRIi64 " from Records table (%i) - %s", getAllRecordsData.objectStoreIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
2075 return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to look up record in object store by key range") };
2078 result = { getAllRecordsData.getAllType };
2080 uint32_t targetResults;
2081 if (getAllRecordsData.count && getAllRecordsData.count.value())
2082 targetResults = getAllRecordsData.count.value();
2084 targetResults = std::numeric_limits<uint32_t>::max();
2086 int sqlResult = sql->step();
2087 uint32_t returnedResults = 0;
2089 while (sqlResult == SQLITE_ROW && returnedResults < targetResults) {
2090 if (getAllRecordsData.getAllType == IndexedDB::GetAllType::Values) {
2091 Vector<uint8_t> buffer;
2092 sql->getColumnBlobAsVector(0, buffer);
2093 ThreadSafeDataBuffer resultBuffer = ThreadSafeDataBuffer::adoptVector(buffer);
2095 auto recordID = sql->getColumnInt64(1);
2098 Vector<String> blobURLs, blobFilePaths;
2099 auto error = getBlobRecordsForObjectStoreRecord(recordID, blobURLs, blobFilePaths);
2100 ASSERT(blobURLs.size() == blobFilePaths.size());
2102 if (!error.isNull())
2105 result.addValue({ resultBuffer, WTFMove(blobURLs), WTFMove(blobFilePaths) });
2107 Vector<uint8_t> keyData;
2109 sql->getColumnBlobAsVector(0, keyData);
2111 if (!deserializeIDBKeyData(keyData.data(), keyData.size(), key)) {
2112 LOG_ERROR("Unable to deserialize key data from database while getting all key records");
2113 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to deserialize key data while getting all key records") };
2116 result.addKey(WTFMove(key));
2120 sqlResult = sql->step();
2123 if (sqlResult == SQLITE_OK || sqlResult == SQLITE_DONE || sqlResult == SQLITE_ROW) {
2124 // Finished getting results
2128 // There was an error fetching records from the database.
2129 LOG_ERROR("Could not get record from object store %" PRIi64 " from Records table (%i) - %s", getAllRecordsData.objectStoreIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
2130 return { IDBDatabaseException::UnknownError, ASCIILiteral("Error looking up record in object store by key range") };
2133 IDBError SQLiteIDBBackingStore::getAllIndexRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData& getAllRecordsData, IDBGetAllResult& result)
2135 LOG(IndexedDB, "SQLiteIDBBackingStore::getAllIndexRecords - %s", getAllRecordsData.keyRangeData.loggingString().utf8().data());
2138 ASSERT(m_sqliteDB->isOpen());
2140 auto* transaction = m_transactions.get(transactionIdentifier);
2141 if (!transaction || !transaction->inProgress()) {
2142 LOG_ERROR("Attempt to get all index records from database without an in-progress transaction");
2143 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to get all index records from database without an in-progress transaction") };
2146 auto cursor = transaction->maybeOpenBackingStoreCursor(getAllRecordsData.objectStoreIdentifier, getAllRecordsData.indexIdentifier, getAllRecordsData.keyRangeData);
2148 LOG_ERROR("Cannot open cursor to perform index gets in database");
2149 return { IDBDatabaseException::UnknownError, ASCIILiteral("Cannot open cursor to perform index gets in database") };
2152 if (cursor->didError()) {
2153 LOG_ERROR("Cursor failed while looking up index records in database");
2154 return { IDBDatabaseException::UnknownError, ASCIILiteral("Cursor failed while looking up index records in database") };
2157 result = { getAllRecordsData.getAllType };
2158 uint32_t currentCount = 0;
2159 uint32_t targetCount = getAllRecordsData.count ? getAllRecordsData.count.value() : 0;
2161 targetCount = std::numeric_limits<uint32_t>::max();
2162 while (!cursor->didComplete() && !cursor->didError() && currentCount < targetCount) {
2163 if (getAllRecordsData.getAllType == IndexedDB::GetAllType::Keys) {
2164 IDBKeyData keyCopy = cursor->currentPrimaryKey();
2165 result.addKey(WTFMove(keyCopy));
2167 result.addValue(cursor->currentValue() ? *cursor->currentValue() : IDBValue());
2173 if (cursor->didError()) {
2174 LOG_ERROR("Cursor failed while looking up index records in database");
2175 return { IDBDatabaseException::UnknownError, ASCIILiteral("Cursor failed while looking up index records in database") };
2181 IDBError SQLiteIDBBackingStore::getIndexRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID, uint64_t indexID, IndexedDB::IndexRecordType type, const IDBKeyRangeData& range, IDBGetResult& getResult)
2183 LOG(IndexedDB, "SQLiteIDBBackingStore::getIndexRecord - %s", range.loggingString().utf8().data());
2186 ASSERT(m_sqliteDB->isOpen());
2188 auto* transaction = m_transactions.get(transactionIdentifier);
2189 if (!transaction || !transaction->inProgress()) {
2190 LOG_ERROR("Attempt to get an index record from database without an in-progress transaction");
2191 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to get an index record from database without an in-progress transaction") };
2194 if (range.isExactlyOneKey())
2195 return uncheckedGetIndexRecordForOneKey(indexID, objectStoreID, type, range.lowerKey, getResult);
2197 auto cursor = transaction->maybeOpenBackingStoreCursor(objectStoreID, indexID, range);
2199 LOG_ERROR("Cannot open cursor to perform index get in database");
2200 return { IDBDatabaseException::UnknownError, ASCIILiteral("Cannot open cursor to perform index get in database") };
2203 if (cursor->didError()) {
2204 LOG_ERROR("Cursor failed while looking up index record in database");
2205 return { IDBDatabaseException::UnknownError, ASCIILiteral("Cursor failed while looking up index record in database") };
2208 if (cursor->didComplete())
2211 if (type == IndexedDB::IndexRecordType::Key)
2212 getResult = { cursor->currentPrimaryKey() };
2214 getResult = { cursor->currentValue() ? *cursor->currentValue() : IDBValue(), cursor->currentPrimaryKey() };
2220 IDBError SQLiteIDBBackingStore::uncheckedGetIndexRecordForOneKey(int64_t indexID, int64_t objectStoreID, IndexedDB::IndexRecordType type, const IDBKeyData& key, IDBGetResult& getResult)
2222 LOG(IndexedDB, "SQLiteIDBBackingStore::uncheckedGetIndexRecordForOneKey");
2224 ASSERT(key.isValid() && key.type() != KeyType::Max && key.type() != KeyType::Min);
2226 RefPtr<SharedBuffer> buffer = serializeIDBKeyData(key);
2228 LOG_ERROR("Unable to serialize IDBKey to look up one index record");
2229 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKey to look up one index record") };
2232 auto* sql = cachedStatement(SQL::GetIndexRecordForOneKey, ASCIILiteral("SELECT IndexRecords.value, Records.value, Records.recordID FROM Records INNER JOIN IndexRecords ON Records.recordID = IndexRecords.objectStoreRecordID WHERE IndexRecords.indexID = ? AND IndexRecords.objectStoreID = ? AND IndexRecords.key = CAST(? AS TEXT) ORDER BY IndexRecords.key, IndexRecords.value"));
2235 || sql->bindInt64(1, indexID) != SQLITE_OK
2236 || sql->bindInt64(2, objectStoreID) != SQLITE_OK
2237 || sql->bindBlob(3, buffer->data(), buffer->size()) != SQLITE_OK) {
2238 LOG_ERROR("Unable to lookup index record in database");
2239 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to lookup index record in database") };
2242 int result = sql->step();
2243 if (result != SQLITE_ROW && result != SQLITE_DONE) {
2244 LOG_ERROR("Unable to lookup index record in database");
2245 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to lookup index record in database") };
2248 if (result == SQLITE_DONE)
2251 IDBKeyData objectStoreKey;
2252 Vector<uint8_t> keyVector;
2253 sql->getColumnBlobAsVector(0, keyVector);
2255 if (!deserializeIDBKeyData(keyVector.data(), keyVector.size(), objectStoreKey)) {
2256 LOG_ERROR("Unable to deserialize key looking up index record in database");
2257 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to deserialize key looking up index record in database") };
2260 if (type == IndexedDB::IndexRecordType::Key) {
2261 getResult = { objectStoreKey };
2265 sql->getColumnBlobAsVector(1, keyVector);
2267 int64_t recordID = sql->getColumnInt64(2);
2268 Vector<String> blobURLs, blobFilePaths;
2269 auto error = getBlobRecordsForObjectStoreRecord(recordID, blobURLs, blobFilePaths);
2270 ASSERT(blobURLs.size() == blobFilePaths.size());
2272 if (!error.isNull())
2275 getResult = { { ThreadSafeDataBuffer::adoptVector(keyVector), WTFMove(blobURLs), WTFMove(blobFilePaths) }, objectStoreKey };
2279 IDBError SQLiteIDBBackingStore::getCount(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData& range, uint64_t& outCount)
2281 LOG(IndexedDB, "SQLiteIDBBackingStore::getCount - object store %" PRIu64, objectStoreIdentifier);
2283 ASSERT(m_sqliteDB->isOpen());
2287 auto* transaction = m_transactions.get(transactionIdentifier);
2288 if (!transaction || !transaction->inProgress()) {
2289 LOG_ERROR("Attempt to get count from database without an in-progress transaction");
2290 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to get count from database without an in-progress transaction") };
2293 auto cursor = transaction->maybeOpenBackingStoreCursor(objectStoreIdentifier, indexIdentifier, range);
2295 LOG_ERROR("Cannot open cursor to populate indexes in database");
2296 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to populate indexes in database") };
2299 while (cursor->advance(1))
2305 IDBError SQLiteIDBBackingStore::uncheckedGetKeyGeneratorValue(int64_t objectStoreID, uint64_t& outValue)
2307 auto* sql = cachedStatement(SQL::GetKeyGeneratorValue, ASCIILiteral("SELECT currentKey FROM KeyGenerators WHERE objectStoreID = ?;"));
2309 || sql->bindInt64(1, objectStoreID) != SQLITE_OK) {
2310 LOG_ERROR("Could not retrieve currentKey from KeyGenerators table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
2311 return { IDBDatabaseException::UnknownError, ASCIILiteral("Error getting current key generator value from database") };
2313 int result = sql->step();
2314 if (result != SQLITE_ROW) {
2315 LOG_ERROR("Could not retreive key generator value for object store, but it should be there.");
2316 return { IDBDatabaseException::UnknownError, ASCIILiteral("Error finding current key generator value in database") };
2319 int64_t value = sql->getColumnInt64(0);
2321 return { IDBDatabaseException::ConstraintError, "Current key generator value from database is invalid" };
2327 IDBError SQLiteIDBBackingStore::uncheckedSetKeyGeneratorValue(int64_t objectStoreID, uint64_t value)
2329 auto* sql = cachedStatement(SQL::SetKeyGeneratorValue, ASCIILiteral("INSERT INTO KeyGenerators VALUES (?, ?);"));
2331 || sql->bindInt64(1, objectStoreID) != SQLITE_OK
2332 || sql->bindInt64(2, value) != SQLITE_OK
2333 || sql->step() != SQLITE_DONE) {
2334 LOG_ERROR("Could not update key generator value (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
2335 return { IDBDatabaseException::ConstraintError, "Error storing new key generator value in database" };
2341 IDBError SQLiteIDBBackingStore::generateKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID, uint64_t& generatedKey)
2343 LOG(IndexedDB, "SQLiteIDBBackingStore::generateKeyNumber");
2346 ASSERT(m_sqliteDB->isOpen());
2348 // The IndexedDatabase spec defines the max key generator value as 2^53;
2349 static uint64_t maxGeneratorValue = 0x20000000000000;
2351 auto* transaction = m_transactions.get(transactionIdentifier);
2352 if (!transaction || !transaction->inProgress()) {
2353 LOG_ERROR("Attempt to generate key in database without an in-progress transaction");
2354 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to generate key in database without an in-progress transaction") };
2356 if (transaction->mode() == IDBTransactionMode::Readonly) {
2357 LOG_ERROR("Attempt to generate key in a read-only transaction");
2358 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to generate key in a read-only transaction") };
2361 uint64_t currentValue;
2362 auto error = uncheckedGetKeyGeneratorValue(objectStoreID, currentValue);
2363 if (!error.isNull())
2366 if (currentValue + 1 > maxGeneratorValue)
2367 return { IDBDatabaseException::ConstraintError, "Cannot generate new key value over 2^53 for object store operation" };
2369 generatedKey = currentValue + 1;
2370 return uncheckedSetKeyGeneratorValue(objectStoreID, generatedKey);
2373 IDBError SQLiteIDBBackingStore::revertGeneratedKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID, uint64_t newKeyNumber)
2375 LOG(IndexedDB, "SQLiteIDBBackingStore::revertGeneratedKeyNumber - object store %" PRIu64 ", reverted number %" PRIu64, objectStoreID, newKeyNumber);
2378 ASSERT(m_sqliteDB->isOpen());
2380 auto* transaction = m_transactions.get(transactionIdentifier);
2381 if (!transaction || !transaction->inProgress()) {
2382 LOG_ERROR("Attempt to revert key generator value in database without an in-progress transaction");
2383 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to revert key generator value in database without an in-progress transaction") };
2385 if (transaction->mode() == IDBTransactionMode::Readonly) {
2386 LOG_ERROR("Attempt to revert key generator value in a read-only transaction");
2387 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to revert key generator value in a read-only transaction") };
2390 ASSERT(newKeyNumber);
2391 return uncheckedSetKeyGeneratorValue(objectStoreID, newKeyNumber - 1);
2394 IDBError SQLiteIDBBackingStore::maybeUpdateKeyGeneratorNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID, double newKeyNumber)
2396 LOG(IndexedDB, "SQLiteIDBBackingStore::maybeUpdateKeyGeneratorNumber");
2399 ASSERT(m_sqliteDB->isOpen());
2401 auto* transaction = m_transactions.get(transactionIdentifier);
2402 if (!transaction || !transaction->inProgress()) {
2403 LOG_ERROR("Attempt to update key generator value in database without an in-progress transaction");
2404 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to update key generator value in database without an in-progress transaction") };
2406 if (transaction->mode() == IDBTransactionMode::Readonly) {
2407 LOG_ERROR("Attempt to update key generator value in a read-only transaction");
2408 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to update key generator value in a read-only transaction") };
2411 uint64_t currentValue;
2412 auto error = uncheckedGetKeyGeneratorValue(objectStoreID, currentValue);
2413 if (!error.isNull())
2416 if (newKeyNumber <= currentValue)
2419 uint64_t newKeyInteger(newKeyNumber);
2420 if (newKeyInteger <= uint64_t(newKeyNumber))
2423 ASSERT(newKeyInteger > uint64_t(newKeyNumber));
2425 return uncheckedSetKeyGeneratorValue(objectStoreID, newKeyInteger - 1);
2428 IDBError SQLiteIDBBackingStore::openCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo& info, IDBGetResult& result)
2431 ASSERT(m_sqliteDB->isOpen());
2433 auto* transaction = m_transactions.get(transactionIdentifier);
2434 if (!transaction || !transaction->inProgress()) {
2435 LOG_ERROR("Attempt to open a cursor in database without an in-progress transaction");
2436 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to open a cursor in database without an in-progress transaction") };
2439 auto* cursor = transaction->maybeOpenCursor(info);
2441 LOG_ERROR("Unable to open cursor");
2442 return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to open cursor") };
2445 m_cursors.set(cursor->identifier(), cursor);
2447 cursor->currentData(result);
2451 IDBError SQLiteIDBBackingStore::iterateCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData& data, IDBGetResult& result)
2453 LOG(IndexedDB, "SQLiteIDBBackingStore::iterateCursor");
2456 ASSERT(m_sqliteDB->isOpen());
2458 auto* cursor = m_cursors.get(cursorIdentifier);
2460 LOG_ERROR("Attempt to iterate a cursor that doesn't exist");
2461 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to iterate a cursor that doesn't exist") };
2464 ASSERT_UNUSED(transactionIdentifier, cursor->transaction()->transactionIdentifier() == transactionIdentifier);
2466 if (!cursor->transaction() || !cursor->transaction()->inProgress()) {
2467 LOG_ERROR("Attempt to iterate a cursor without an in-progress transaction");
2468 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to iterate a cursor without an in-progress transaction") };
2471 auto key = data.keyData;
2472 auto primaryKey = data.primaryKeyData;
2473 auto count = data.count;
2475 if (key.isValid()) {
2476 if (!cursor->iterate(key, primaryKey)) {
2477 LOG_ERROR("Attempt to iterate cursor failed");
2478 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to iterate cursor failed") };
2481 ASSERT(!primaryKey.isValid());
2484 if (!cursor->advance(count)) {
2485 LOG_ERROR("Attempt to advance cursor failed");
2486 return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to advance cursor failed") };
2490 cursor->currentData(result);
2494 bool SQLiteIDBBackingStore::prefetchCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier)
2496 LOG(IndexedDB, "SQLiteIDBBackingStore::prefetchCursor");
2499 ASSERT(m_sqliteDB->isOpen());
2501 auto* cursor = m_cursors.get(cursorIdentifier);
2502 if (!cursor || !cursor->transaction() || !cursor->transaction()->inProgress())
2505 ASSERT_UNUSED(transactionIdentifier, cursor->transaction()->transactionIdentifier() == transactionIdentifier);
2507 return cursor->prefetch();
2510 IDBObjectStoreInfo* SQLiteIDBBackingStore::infoForObjectStore(uint64_t objectStoreIdentifier)
2512 ASSERT(m_databaseInfo);
2513 return m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
2516 void SQLiteIDBBackingStore::deleteBackingStore()
2518 String dbFilename = fullDatabasePath();
2520 LOG(IndexedDB, "SQLiteIDBBackingStore::deleteBackingStore deleting file '%s' on disk", dbFilename.utf8().data());
2522 Vector<String> blobFiles;
2524 bool errored = true;
2527 SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT fileName FROM BlobFiles;"));
2528 if (sql.prepare() == SQLITE_OK) {
2529 int result = sql.step();
2530 while (result == SQLITE_ROW) {
2531 blobFiles.append(sql.getColumnText(0));
2532 result = sql.step();
2535 if (result == SQLITE_DONE)
2541 LOG_ERROR("Error getting all blob filenames to be deleted");
2544 String databaseDirectory = fullDatabaseDirectory();
2545 for (auto& file : blobFiles) {
2546 String fullPath = pathByAppendingComponent(databaseDirectory, file);
2547 if (!deleteFile(fullPath))
2548 LOG_ERROR("Error deleting blob file %s", fullPath.utf8().data());
2554 SQLiteFileSystem::deleteDatabaseFile(dbFilename);
2555 SQLiteFileSystem::deleteEmptyDatabaseDirectory(fullDatabaseDirectory());
2556 SQLiteFileSystem::deleteEmptyDatabaseDirectory(m_absoluteDatabaseDirectory);
2559 void SQLiteIDBBackingStore::unregisterCursor(SQLiteIDBCursor& cursor)
2561 ASSERT(m_cursors.contains(cursor.identifier()));
2562 m_cursors.remove(cursor.identifier());
2565 SQLiteStatement* SQLiteIDBBackingStore::cachedStatement(SQLiteIDBBackingStore::SQL sql, const char* statement)
2567 if (sql >= SQL::Count) {
2568 LOG_ERROR("Invalid SQL statement ID passed to cachedStatement()");
2572 if (m_cachedStatements[static_cast<size_t>(sql)]) {
2573 if (m_cachedStatements[static_cast<size_t>(sql)]->reset() == SQLITE_OK)
2574 return m_cachedStatements[static_cast<size_t>(sql)].get();
2575 m_cachedStatements[static_cast<size_t>(sql)] = nullptr;
2579 m_cachedStatements[static_cast<size_t>(sql)] = std::make_unique<SQLiteStatement>(*m_sqliteDB, statement);
2580 if (m_cachedStatements[static_cast<size_t>(sql)]->prepare() != SQLITE_OK)
2581 m_cachedStatements[static_cast<size_t>(sql)] = nullptr;
2584 return m_cachedStatements[static_cast<size_t>(sql)].get();
2587 void SQLiteIDBBackingStore::closeSQLiteDB()
2589 for (size_t i = 0; i < static_cast<int>(SQL::Count); ++i)
2590 m_cachedStatements[i] = nullptr;
2593 m_sqliteDB->close();
2595 m_sqliteDB = nullptr;
2598 } // namespace IDBServer
2599 } // namespace WebCore
2601 #endif // ENABLE(INDEXED_DATABASE)