IDB: Support createIndex/deleteIndex
[WebKit-https.git] / Source / WebKit2 / DatabaseProcess / IndexedDB / sqlite / UniqueIDBDatabaseBackingStoreSQLite.cpp
1 /*
2  * Copyright (C) 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
24  */
25
26 #include "config.h"
27 #include "UniqueIDBDatabaseBackingStoreSQLite.h"
28
29 #if ENABLE(INDEXED_DATABASE) && ENABLE(DATABASE_PROCESS)
30
31 #include "ArgumentDecoder.h"
32 #include "IDBSerialization.h"
33 #include "SQLiteIDBTransaction.h"
34 #include <WebCore/FileSystem.h>
35 #include <WebCore/IDBDatabaseMetadata.h>
36 #include <WebCore/IDBKeyData.h>
37 #include <WebCore/IDBKeyRange.h>
38 #include <WebCore/SQLiteDatabase.h>
39 #include <WebCore/SQLiteStatement.h>
40 #include <WebCore/SharedBuffer.h>
41 #include <wtf/MainThread.h>
42
43 using namespace WebCore;
44
45 namespace WebKit {
46
47 // Current version of the metadata schema being used in the metadata database.
48 static const int currentMetadataVersion = 1;
49
50 static int64_t generateDatabaseId()
51 {
52     static int64_t databaseID = 0;
53
54     ASSERT(!isMainThread());
55     return ++databaseID;
56 }
57
58 UniqueIDBDatabaseBackingStoreSQLite::UniqueIDBDatabaseBackingStoreSQLite(const UniqueIDBDatabaseIdentifier& identifier, const String& databaseDirectory)
59     : m_identifier(identifier)
60     , m_absoluteDatabaseDirectory(databaseDirectory)
61 {
62     // The backing store is meant to be created and used entirely on a background thread.
63     ASSERT(!isMainThread());
64 }
65
66 UniqueIDBDatabaseBackingStoreSQLite::~UniqueIDBDatabaseBackingStoreSQLite()
67 {
68     ASSERT(!isMainThread());
69 }
70
71 std::unique_ptr<WebCore::IDBDatabaseMetadata> UniqueIDBDatabaseBackingStoreSQLite::createAndPopulateInitialMetadata()
72 {
73     ASSERT(!isMainThread());
74     ASSERT(m_sqliteDB);
75     ASSERT(m_sqliteDB->isOpen());
76
77     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);")) {
78         LOG_ERROR("Could not create IDBDatabaseInfo table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
79         m_sqliteDB = nullptr;
80         return nullptr;
81     }
82
83     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);")) {
84         LOG_ERROR("Could not create ObjectStoreInfo table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
85         m_sqliteDB = nullptr;
86         return nullptr;
87     }
88
89     if (!m_sqliteDB->executeCommand("CREATE TABLE IndexInfo (id INTEGER PRIMARY KEY NOT NULL ON CONFLICT FAIL, name TEXT NOT NULL ON CONFLICT FAIL UNIQUE 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);")) {
90         LOG_ERROR("Could not create IndexInfo table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
91         m_sqliteDB = nullptr;
92         return nullptr;
93     }
94
95     if (!m_sqliteDB->executeCommand("CREATE TABLE Records (objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, key BLOB COLLATE IDBKEY NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, value BLOB NOT NULL ON CONFLICT FAIL);")) {
96         LOG_ERROR("Could not create Records table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
97         m_sqliteDB = nullptr;
98         return nullptr;
99     }
100
101     {
102         SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO IDBDatabaseInfo VALUES ('MetadataVersion', ?);"));
103         if (sql.prepare() != SQLResultOk
104             || sql.bindInt(1, currentMetadataVersion) != SQLResultOk
105             || sql.step() != SQLResultDone) {
106             LOG_ERROR("Could not insert database metadata version into IDBDatabaseInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
107             m_sqliteDB = nullptr;
108             return nullptr;
109         }
110     }
111     {
112         SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO IDBDatabaseInfo VALUES ('DatabaseName', ?);"));
113         if (sql.prepare() != SQLResultOk
114             || sql.bindText(1, m_identifier.databaseName()) != SQLResultOk
115             || sql.step() != SQLResultDone) {
116             LOG_ERROR("Could not insert database name into IDBDatabaseInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
117             m_sqliteDB = nullptr;
118             return nullptr;
119         }
120     }
121     {
122         // Database versions are defined to be a uin64_t in the spec but sqlite3 doesn't support native binding of unsigned integers.
123         // Therefore we'll store the version as a String.
124         SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO IDBDatabaseInfo VALUES ('DatabaseVersion', ?);"));
125         if (sql.prepare() != SQLResultOk
126             || sql.bindText(1, String::number(0)) != SQLResultOk
127             || sql.step() != SQLResultDone) {
128             LOG_ERROR("Could not insert default version into IDBDatabaseInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
129             m_sqliteDB = nullptr;
130             return nullptr;
131         }
132     }
133
134     if (!m_sqliteDB->executeCommand(ASCIILiteral("INSERT INTO IDBDatabaseInfo VALUES ('MaxObjectStoreID', 1);"))) {
135         LOG_ERROR("Could not insert default version into IDBDatabaseInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
136         m_sqliteDB = nullptr;
137         return nullptr;
138     }
139
140     // This initial metadata matches the default values we just put into the metadata database.
141     auto metadata = std::make_unique<IDBDatabaseMetadata>();
142     metadata->name = m_identifier.databaseName();
143     metadata->version = 0;
144     metadata->maxObjectStoreId = 1;
145
146     return metadata;
147 }
148
149 std::unique_ptr<IDBDatabaseMetadata> UniqueIDBDatabaseBackingStoreSQLite::extractExistingMetadata()
150 {
151     ASSERT(!isMainThread());
152     ASSERT(m_sqliteDB);
153
154     if (!m_sqliteDB->tableExists(ASCIILiteral("IDBDatabaseInfo")))
155         return nullptr;
156
157     auto metadata = std::make_unique<IDBDatabaseMetadata>();
158     {
159         SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT value FROM IDBDatabaseInfo WHERE key = 'MetadataVersion';"));
160         if (sql.isColumnNull(0))
161             return nullptr;
162         metadata->version = sql.getColumnInt(0);
163     }
164     {
165         SQLiteStatement sql(*m_sqliteDB, "SELECT value FROM IDBDatabaseInfo WHERE key = 'DatabaseName';");
166         if (sql.isColumnNull(0))
167             return nullptr;
168         metadata->name = sql.getColumnText(0);
169         if (metadata->name != m_identifier.databaseName()) {
170             LOG_ERROR("Database name in the metadata database ('%s') does not match the expected name ('%s')", metadata->name.utf8().data(), m_identifier.databaseName().utf8().data());
171             return nullptr;
172         }
173     }
174     {
175         SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT value FROM IDBDatabaseInfo WHERE key = 'DatabaseVersion';"));
176         if (sql.isColumnNull(0))
177             return nullptr;
178         String stringVersion = sql.getColumnText(0);
179         bool ok;
180         metadata->version = stringVersion.toUInt64Strict(&ok);
181         if (!ok) {
182             LOG_ERROR("Database version on disk ('%s') does not cleanly convert to an unsigned 64-bit integer version", stringVersion.utf8().data());
183             return nullptr;
184         }
185     }
186
187     {
188         SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT id, name, keyPath, autoInc, maxIndexID FROM ObjectStoreInfo;"));
189         if (sql.prepare() != SQLResultOk)
190             return nullptr;
191
192         int result = sql.step();
193         while (result == SQLResultRow) {
194             IDBObjectStoreMetadata metadata;
195             metadata.id = sql.getColumnInt64(0);
196             metadata.name = sql.getColumnText(1);
197
198             int keyPathSize;
199             const uint8_t* keyPathBuffer = static_cast<const uint8_t*>(sql.getColumnBlob(2, keyPathSize));
200
201             std::unique_ptr<IDBKeyPath> keyPath = deserializeIDBKeyPath(keyPathBuffer, keyPathSize);
202
203             if (!keyPath) {
204                 LOG_ERROR("Unable to extract key path metadata from database");
205                 return nullptr;
206             }
207
208             metadata.keyPath = *keyPath;
209             metadata.autoIncrement = sql.getColumnInt(3);
210             metadata.maxIndexId = sql.getColumnInt64(4);
211
212             result = sql.step();
213         }
214
215         if (result != SQLResultDone) {
216             LOG_ERROR("Error fetching object store metadata from database on disk");
217             return nullptr;
218         }
219     }
220
221     // FIXME: Once we save indexes we need to extract their metadata, also.
222
223     return metadata;
224 }
225
226 std::unique_ptr<SQLiteDatabase> UniqueIDBDatabaseBackingStoreSQLite::openSQLiteDatabaseAtPath(const String& path)
227 {
228     ASSERT(!isMainThread());
229
230     auto sqliteDatabase = std::make_unique<SQLiteDatabase>();
231     if (!sqliteDatabase->open(path)) {
232         LOG_ERROR("Failed to open SQLite database at path '%s'", path.utf8().data());
233         return nullptr;
234     }
235
236     // Since a WorkQueue isn't bound to a specific thread, we have to disable threading checks
237     // even though we never access the database from different threads simultaneously.
238     sqliteDatabase->disableThreadingChecks();
239
240     return sqliteDatabase;
241 }
242
243 std::unique_ptr<IDBDatabaseMetadata> UniqueIDBDatabaseBackingStoreSQLite::getOrEstablishMetadata()
244 {
245     ASSERT(!isMainThread());
246
247     String dbFilename = pathByAppendingComponent(m_absoluteDatabaseDirectory, "IndexedDB.sqlite3");
248     m_sqliteDB = openSQLiteDatabaseAtPath(dbFilename);
249     if (!m_sqliteDB)
250         return nullptr;
251
252     RefPtr<UniqueIDBDatabaseBackingStoreSQLite> protector(this);
253     m_sqliteDB->setCollationFunction("IDBKEY", [this](int aLength, const void* a, int bLength, const void* b) {
254         return collate(aLength, a, bLength, b);
255     });
256
257     std::unique_ptr<IDBDatabaseMetadata> metadata = extractExistingMetadata();
258     if (!metadata)
259         metadata = createAndPopulateInitialMetadata();
260
261     if (!metadata)
262         LOG_ERROR("Unable to establish IDB database at path '%s'", dbFilename.utf8().data());
263
264     // The database id is a runtime concept and doesn't need to be stored in the metadata database.
265     metadata->id = generateDatabaseId();
266
267     return metadata;
268 }
269
270 bool UniqueIDBDatabaseBackingStoreSQLite::establishTransaction(const IDBTransactionIdentifier& identifier, const Vector<int64_t>&, WebCore::IndexedDB::TransactionMode mode)
271 {
272     ASSERT(!isMainThread());
273
274     if (!m_transactions.add(identifier, SQLiteIDBTransaction::create(identifier, mode)).isNewEntry) {
275         LOG_ERROR("Attempt to establish transaction identifier that already exists");
276         return false;
277     }
278
279     return true;
280 }
281
282 bool UniqueIDBDatabaseBackingStoreSQLite::beginTransaction(const IDBTransactionIdentifier& identifier)
283 {
284     ASSERT(!isMainThread());
285
286     SQLiteIDBTransaction* transaction = m_transactions.get(identifier);
287     if (!transaction) {
288         LOG_ERROR("Attempt to begin a transaction that hasn't been established");
289         return false;
290     }
291
292     return transaction->begin(*m_sqliteDB);
293 }
294
295 bool UniqueIDBDatabaseBackingStoreSQLite::commitTransaction(const IDBTransactionIdentifier& identifier)
296 {
297     ASSERT(!isMainThread());
298
299     SQLiteIDBTransaction* transaction = m_transactions.get(identifier);
300     if (!transaction) {
301         LOG_ERROR("Attempt to commit a transaction that hasn't been established");
302         return false;
303     }
304
305     return transaction->commit();
306 }
307
308 bool UniqueIDBDatabaseBackingStoreSQLite::resetTransaction(const IDBTransactionIdentifier& identifier)
309 {
310     ASSERT(!isMainThread());
311
312     SQLiteIDBTransaction* transaction = m_transactions.get(identifier);
313     if (!transaction) {
314         LOG_ERROR("Attempt to reset a transaction that hasn't been established");
315         return false;
316     }
317
318     return transaction->reset();
319 }
320
321 bool UniqueIDBDatabaseBackingStoreSQLite::rollbackTransaction(const IDBTransactionIdentifier& identifier)
322 {
323     ASSERT(!isMainThread());
324
325     SQLiteIDBTransaction* transaction = m_transactions.get(identifier);
326     if (!transaction) {
327         LOG_ERROR("Attempt to rollback a transaction that hasn't been established");
328         return false;
329     }
330
331     return transaction->rollback();
332 }
333
334 bool UniqueIDBDatabaseBackingStoreSQLite::changeDatabaseVersion(const IDBTransactionIdentifier& identifier, uint64_t newVersion)
335 {
336     ASSERT(!isMainThread());
337     ASSERT(m_sqliteDB);
338     ASSERT(m_sqliteDB->isOpen());
339
340     SQLiteIDBTransaction* transaction = m_transactions.get(identifier);
341     if (!transaction || !transaction->inProgress()) {
342         LOG_ERROR("Attempt to change database version with an established, in-progress transaction");
343         return false;
344     }
345     if (transaction->mode() != IndexedDB::TransactionMode::VersionChange) {
346         LOG_ERROR("Attempt to change database version during a non version-change transaction");
347         return false;
348     }
349
350     {
351         SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("UPDATE IDBDatabaseInfo SET value = ? where key = 'DatabaseVersion';"));
352         if (sql.prepare() != SQLResultOk
353             || sql.bindText(1, String::number(newVersion)) != SQLResultOk
354             || sql.step() != SQLResultDone) {
355             LOG_ERROR("Could not update database version in IDBDatabaseInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
356             return false;
357         }
358     }
359
360     return true;
361 }
362
363 bool UniqueIDBDatabaseBackingStoreSQLite::createObjectStore(const IDBTransactionIdentifier& identifier, const IDBObjectStoreMetadata& metadata)
364 {
365     ASSERT(!isMainThread());
366     ASSERT(m_sqliteDB);
367     ASSERT(m_sqliteDB->isOpen());
368
369     SQLiteIDBTransaction* transaction = m_transactions.get(identifier);
370     if (!transaction || !transaction->inProgress()) {
371         LOG_ERROR("Attempt to change database version with an established, in-progress transaction");
372         return false;
373     }
374     if (transaction->mode() != IndexedDB::TransactionMode::VersionChange) {
375         LOG_ERROR("Attempt to change database version during a non version-change transaction");
376         return false;
377     }
378
379     RefPtr<SharedBuffer> keyPathBlob = serializeIDBKeyPath(metadata.keyPath);
380     if (!keyPathBlob) {
381         LOG_ERROR("Unable to serialize IDBKeyPath to save in database");
382         return false;
383     }
384
385     SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO ObjectStoreInfo VALUES (?, ?, ?, ?, ?);"));
386     if (sql.prepare() != SQLResultOk
387         || sql.bindInt64(1, metadata.id) != SQLResultOk
388         || sql.bindText(2, metadata.name) != SQLResultOk
389         || sql.bindBlob(3, keyPathBlob->data(), keyPathBlob->size()) != SQLResultOk
390         || sql.bindInt(4, metadata.autoIncrement) != SQLResultOk
391         || sql.bindInt64(5, metadata.maxIndexId) != SQLResultOk
392         || sql.step() != SQLResultDone) {
393         LOG_ERROR("Could not add object store '%s' to ObjectStoreInfo table (%i) - %s", metadata.name.utf8().data(), m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
394         return false;
395     }
396
397     return true;
398 }
399
400 bool UniqueIDBDatabaseBackingStoreSQLite::deleteObjectStore(const IDBTransactionIdentifier& identifier, int64_t objectStoreID)
401 {
402     ASSERT(!isMainThread());
403     ASSERT(m_sqliteDB);
404     ASSERT(m_sqliteDB->isOpen());
405
406     SQLiteIDBTransaction* transaction = m_transactions.get(identifier);
407     if (!transaction || !transaction->inProgress()) {
408         LOG_ERROR("Attempt to change database version with an established, in-progress transaction");
409         return false;
410     }
411     if (transaction->mode() != IndexedDB::TransactionMode::VersionChange) {
412         LOG_ERROR("Attempt to change database version during a non version-change transaction");
413         return false;
414     }
415
416     // Delete the ObjectStore record
417     {
418         SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("DELETE FROM ObjectStoreInfo WHERE id = ?;"));
419         if (sql.prepare() != SQLResultOk
420             || sql.bindInt64(1, objectStoreID) != SQLResultOk
421             || sql.step() != SQLResultDone) {
422             LOG_ERROR("Could not delete object store id %lli from ObjectStoreInfo table (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
423             return false;
424         }
425     }
426
427     // Delete all associated Index records
428     {
429         Vector<int64_t> indexIDs;
430         SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT id FROM IndexInfo WHERE objectStoreID = ?;"));
431         if (sql.prepare() != SQLResultOk
432             || sql.bindInt64(1, objectStoreID) != SQLResultOk) {
433             LOG_ERROR("Error fetching index ID records for object store id %lli from IndexInfo table (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
434             return false;
435         }
436
437         int resultCode;
438         while ((resultCode = sql.step()) == SQLResultRow)
439             indexIDs.append(sql.getColumnInt64(0));
440
441         if (resultCode != SQLResultDone) {
442             LOG_ERROR("Error fetching index ID records for object store id %lli from IndexInfo table (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
443             return false;
444         }
445
446         for (auto indexID : indexIDs) {
447             if (!deleteIndex(identifier, objectStoreID, indexID))
448                 return false;
449         }
450     }
451
452     {
453         // FIXME: Execute SQL here to drop all records related to this object store.
454     }
455
456     return true;
457 }
458
459 bool UniqueIDBDatabaseBackingStoreSQLite::clearObjectStore(const IDBTransactionIdentifier& identifier, int64_t objectStoreID)
460 {
461     ASSERT(!isMainThread());
462     ASSERT(m_sqliteDB);
463     ASSERT(m_sqliteDB->isOpen());
464
465     SQLiteIDBTransaction* transaction = m_transactions.get(identifier);
466     if (!transaction || !transaction->inProgress()) {
467         LOG_ERROR("Attempt to change database version with an establish, in-progress transaction");
468         return false;
469     }
470     if (transaction->mode() == IndexedDB::TransactionMode::ReadOnly) {
471         LOG_ERROR("Attempt to change database version during a read-only transaction");
472         return false;
473     }
474
475     {
476         SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("DELETE FROM Records WHERE objectStoreID = ?;"));
477         if (sql.prepare() != SQLResultOk
478             || sql.bindInt64(1, objectStoreID) != SQLResultOk
479             || sql.step() != SQLResultDone) {
480             LOG_ERROR("Could not delete records from object store id %lli (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
481             return false;
482         }
483     }
484
485     // FIXME <rdar://problem/15779642>: Once indexes are implemented, drop index records.
486     return true;
487 }
488
489 bool UniqueIDBDatabaseBackingStoreSQLite::createIndex(const IDBTransactionIdentifier& identifier, int64_t objectStoreID, const WebCore::IDBIndexMetadata& metadata)
490 {
491     ASSERT(!isMainThread());
492     ASSERT(m_sqliteDB);
493     ASSERT(m_sqliteDB->isOpen());
494
495     SQLiteIDBTransaction* transaction = m_transactions.get(identifier);
496     if (!transaction || !transaction->inProgress()) {
497         LOG_ERROR("Attempt to create index without an established, in-progress transaction");
498         return false;
499     }
500     if (transaction->mode() != IndexedDB::TransactionMode::VersionChange) {
501         LOG_ERROR("Attempt to create index during a non-version-change transaction");
502         return false;
503     }
504
505     RefPtr<SharedBuffer> keyPathBlob = serializeIDBKeyPath(metadata.keyPath);
506     if (!keyPathBlob) {
507         LOG_ERROR("Unable to serialize IDBKeyPath to save in database");
508         return false;
509     }
510
511     SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO IndexInfo VALUES (?, ?, ?, ?, ?, ?);"));
512     if (sql.prepare() != SQLResultOk
513         || sql.bindInt64(1, metadata.id) != SQLResultOk
514         || sql.bindText(2, metadata.name) != SQLResultOk
515         || sql.bindInt64(3, objectStoreID) != SQLResultOk
516         || sql.bindBlob(4, keyPathBlob->data(), keyPathBlob->size()) != SQLResultOk
517         || sql.bindInt(5, metadata.unique) != SQLResultOk
518         || sql.bindInt(6, metadata.multiEntry) != SQLResultOk
519         || sql.step() != SQLResultDone) {
520         LOG_ERROR("Could not add index '%s' to IndexInfo table (%i) - %s", metadata.name.utf8().data(), m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
521         return false;
522     }
523
524     return true;
525 }
526
527 bool UniqueIDBDatabaseBackingStoreSQLite::deleteIndex(const IDBTransactionIdentifier& identifier, int64_t objectStoreID, int64_t indexID)
528 {
529     ASSERT(!isMainThread());
530     ASSERT(m_sqliteDB);
531     ASSERT(m_sqliteDB->isOpen());
532
533     SQLiteIDBTransaction* transaction = m_transactions.get(identifier);
534     if (!transaction || !transaction->inProgress()) {
535         LOG_ERROR("Attempt to delete index without an established, in-progress transaction");
536         return false;
537     }
538     if (transaction->mode() != IndexedDB::TransactionMode::VersionChange) {
539         LOG_ERROR("Attempt to delete index during a non-version-change transaction");
540         return false;
541     }
542
543     {
544         SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("DELETE FROM IndexInfo WHERE id = ? AND objectStoreID = ?;"));
545         if (sql.prepare() != SQLResultOk
546             || sql.bindInt64(1, indexID) != SQLResultOk
547             || sql.bindInt64(2, objectStoreID) != SQLResultOk
548             || sql.step() != SQLResultDone) {
549             LOG_ERROR("Could not delete index id %lli from IndexInfo table (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
550             return false;
551         }
552     }
553
554     // FIXME (<rdar://problem/15905293>) - Once we store records against indexes, delete them here.
555     return true;
556 }
557
558 PassRefPtr<IDBKey> UniqueIDBDatabaseBackingStoreSQLite::generateKey(const IDBTransactionIdentifier&, int64_t objectStoreID)
559 {
560     // FIXME (<rdar://problem/15877909>): Implement
561     return nullptr;
562 }
563
564 bool UniqueIDBDatabaseBackingStoreSQLite::keyExistsInObjectStore(const IDBTransactionIdentifier&, int64_t objectStoreID, const IDBKey&, bool& keyExists)
565 {
566     // FIXME: When Get support is implemented, we need to implement this also (<rdar://problem/15779644>)
567     return false;
568 }
569
570 bool UniqueIDBDatabaseBackingStoreSQLite::putRecord(const IDBTransactionIdentifier& identifier, int64_t objectStoreID, const IDBKey& key, const uint8_t* valueBuffer, size_t valueSize)
571 {
572     ASSERT(!isMainThread());
573     ASSERT(m_sqliteDB);
574     ASSERT(m_sqliteDB->isOpen());
575
576     SQLiteIDBTransaction* transaction = m_transactions.get(identifier);
577     if (!transaction || !transaction->inProgress()) {
578         LOG_ERROR("Attempt to put a record into database without an established, in-progress transaction");
579         return false;
580     }
581     if (transaction->mode() == IndexedDB::TransactionMode::ReadOnly) {
582         LOG_ERROR("Attempt to put a record into database during read-only transaction");
583         return false;
584     }
585
586     RefPtr<SharedBuffer> keyBuffer = serializeIDBKey(key);
587     if (!keyBuffer) {
588         LOG_ERROR("Unable to serialize IDBKey to be stored in the database");
589         return false;
590     }
591     {
592         SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO Records VALUES (?, ?, ?);"));
593         if (sql.prepare() != SQLResultOk
594             || sql.bindInt64(1, objectStoreID) != SQLResultOk
595             || sql.bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLResultOk
596             || sql.bindBlob(3, valueBuffer, valueSize) != SQLResultOk
597             || sql.step() != SQLResultDone) {
598             LOG_ERROR("Could not put record for object store %lli in Records table (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
599             return false;
600         }
601     }
602
603     return true;
604 }
605
606 bool UniqueIDBDatabaseBackingStoreSQLite::updateKeyGenerator(const IDBTransactionIdentifier&, int64_t objectStoreId, const IDBKey&, bool checkCurrent)
607 {
608     // FIXME (<rdar://problem/15877909>): Implement
609     return false;
610 }
611
612 bool UniqueIDBDatabaseBackingStoreSQLite::getKeyRecordFromObjectStore(const IDBTransactionIdentifier& identifier, int64_t objectStoreID, const WebCore::IDBKey& key, RefPtr<WebCore::SharedBuffer>& result)
613 {
614     ASSERT(!isMainThread());
615     ASSERT(m_sqliteDB);
616     ASSERT(m_sqliteDB->isOpen());
617
618     SQLiteIDBTransaction* transaction = m_transactions.get(identifier);
619     if (!transaction || !transaction->inProgress()) {
620         LOG_ERROR("Attempt to put a record into database without an established, in-progress transaction");
621         return false;
622     }
623
624     RefPtr<SharedBuffer> keyBuffer = serializeIDBKey(key);
625     if (!keyBuffer) {
626         LOG_ERROR("Unable to serialize IDBKey to be stored in the database");
627         return false;
628     }
629
630     {
631         SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT value FROM Records WHERE objectStoreID = ? AND key = ?;"));
632         if (sql.prepare() != SQLResultOk
633             || sql.bindInt64(1, objectStoreID) != SQLResultOk
634             || sql.bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLResultOk) {
635             LOG_ERROR("Could not get record from object store %lli from Records table (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
636             return false;
637         }
638
639         int sqlResult = sql.step();
640         if (sqlResult == SQLResultOk || sqlResult == SQLResultDone) {
641             // There was no record for the key in the database.
642             return true;
643         }
644         if (sqlResult != SQLResultRow) {
645             // There was an error fetching the record from the database.
646             LOG_ERROR("Could not get record from object store %lli from Records table (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
647             return false;
648         }
649
650         Vector<char> buffer;
651         sql.getColumnBlobAsVector(0, buffer);
652         result = SharedBuffer::create(static_cast<const char*>(buffer.data()), buffer.size());
653     }
654
655     return true;
656 }
657
658 bool UniqueIDBDatabaseBackingStoreSQLite::getKeyRangeRecordFromObjectStore(const IDBTransactionIdentifier& identifier, int64_t objectStoreID, const WebCore::IDBKeyRange& keyRange, RefPtr<WebCore::SharedBuffer>& result, RefPtr<WebCore::IDBKey>& resultKey)
659 {
660     ASSERT(!isMainThread());
661     ASSERT(m_sqliteDB);
662     ASSERT(m_sqliteDB->isOpen());
663
664     SQLiteIDBTransaction* transaction = m_transactions.get(identifier);
665     if (!transaction || !transaction->inProgress()) {
666         LOG_ERROR("Attempt to put a record into database without an established, in-progress transaction");
667         return false;
668     }
669
670     RefPtr<SharedBuffer> lowerBuffer = serializeIDBKey(*keyRange.lower());
671     if (!lowerBuffer) {
672         LOG_ERROR("Unable to serialize IDBKey to be stored in the database");
673         return false;
674     }
675
676     RefPtr<SharedBuffer> upperBuffer = serializeIDBKey(*keyRange.upper());
677     if (!upperBuffer) {
678         LOG_ERROR("Unable to serialize IDBKey to be stored in the database");
679         return false;
680     }
681
682     {
683         SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT value FROM Records WHERE objectStoreID = ? AND key >= ? AND key <= ? ORDER BY key;"));
684         if (sql.prepare() != SQLResultOk
685             || sql.bindInt64(1, objectStoreID) != SQLResultOk
686             || sql.bindBlob(2, lowerBuffer->data(), lowerBuffer->size()) != SQLResultOk
687             || sql.bindBlob(3, upperBuffer->data(), upperBuffer->size()) != SQLResultOk) {
688             LOG_ERROR("Could not get key range record from object store %lli from Records table (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
689             return false;
690         }
691
692         int sqlResult = sql.step();
693
694         if (sqlResult == SQLResultOk || sqlResult == SQLResultDone) {
695             // There was no record for the key in the database.
696             return true;
697         }
698         if (sqlResult != SQLResultRow) {
699             // There was an error fetching the record from the database.
700             LOG_ERROR("Could not get record from object store %lli from Records table (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
701             return false;
702         }
703
704         Vector<char> buffer;
705         sql.getColumnBlobAsVector(0, buffer);
706         result = SharedBuffer::create(static_cast<const char*>(buffer.data()), buffer.size());
707     }
708
709     return true;
710 }
711
712 int UniqueIDBDatabaseBackingStoreSQLite::collate(int aLength, const void* a, int bLength, const void* b)
713 {
714     // FIXME: Implement
715     return 0;
716 }
717
718 } // namespace WebKit
719
720 #endif // ENABLE(INDEXED_DATABASE) && ENABLE(DATABASE_PROCESS)