2 * Copyright (C) 2010 Google 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
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "IDBDatabaseBackendImpl.h"
29 #if ENABLE(INDEXED_DATABASE)
31 #include "CrossThreadTask.h"
32 #include "DOMStringList.h"
33 #include "IDBDatabaseException.h"
34 #include "IDBFactoryBackendImpl.h"
35 #include "IDBObjectStoreBackendImpl.h"
36 #include "IDBSQLiteDatabase.h"
37 #include "IDBTransactionBackendImpl.h"
38 #include "IDBTransactionCoordinator.h"
39 #include "SQLiteStatement.h"
40 #include "SQLiteTransaction.h"
44 static bool extractMetaData(SQLiteDatabase& sqliteDatabase, const String& name, String& foundVersion, int64& foundId)
46 SQLiteStatement databaseQuery(sqliteDatabase, "SELECT id, version FROM Databases WHERE name = ?");
47 if (databaseQuery.prepare() != SQLResultOk) {
51 databaseQuery.bindText(1, name);
52 if (databaseQuery.step() != SQLResultRow)
55 foundId = databaseQuery.getColumnInt64(0);
56 foundVersion = databaseQuery.getColumnText(1);
58 if (databaseQuery.step() == SQLResultRow)
63 static bool setMetaData(SQLiteDatabase& sqliteDatabase, const String& name, const String& version, int64_t& rowId)
65 ASSERT(!name.isNull());
66 ASSERT(!version.isNull());
68 String sql = rowId != IDBDatabaseBackendImpl::InvalidId ? "UPDATE Databases SET name = ?, version = ? WHERE id = ?"
69 : "INSERT INTO Databases (name, description, version) VALUES (?, '', ?)";
70 SQLiteStatement query(sqliteDatabase, sql);
71 if (query.prepare() != SQLResultOk) {
76 query.bindText(1, name);
77 query.bindText(2, version);
78 if (rowId != IDBDatabaseBackendImpl::InvalidId)
79 query.bindInt64(3, rowId);
81 if (query.step() != SQLResultDone)
84 if (rowId == IDBDatabaseBackendImpl::InvalidId)
85 rowId = sqliteDatabase.lastInsertRowID();
90 IDBDatabaseBackendImpl::IDBDatabaseBackendImpl(const String& name, IDBSQLiteDatabase* sqliteDatabase, IDBTransactionCoordinator* coordinator, IDBFactoryBackendImpl* factory, const String& uniqueIdentifier)
91 : m_sqliteDatabase(sqliteDatabase)
95 , m_identifier(uniqueIdentifier)
97 , m_transactionCoordinator(coordinator)
99 ASSERT(!m_name.isNull());
101 bool success = extractMetaData(m_sqliteDatabase->db(), m_name, m_version, m_id);
102 ASSERT_UNUSED(success, success == (m_id != InvalidId));
103 if (!setMetaData(m_sqliteDatabase->db(), m_name, m_version, m_id))
104 ASSERT_NOT_REACHED(); // FIXME: Need better error handling.
108 IDBDatabaseBackendImpl::~IDBDatabaseBackendImpl()
110 m_factory->removeIDBDatabaseBackend(m_identifier);
113 SQLiteDatabase& IDBDatabaseBackendImpl::sqliteDatabase() const
115 return m_sqliteDatabase->db();
118 PassRefPtr<DOMStringList> IDBDatabaseBackendImpl::objectStoreNames() const
120 RefPtr<DOMStringList> objectStoreNames = DOMStringList::create();
121 for (ObjectStoreMap::const_iterator it = m_objectStores.begin(); it != m_objectStores.end(); ++it)
122 objectStoreNames->append(it->first);
123 return objectStoreNames.release();
126 PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::createObjectStore(const String& name, const String& keyPath, bool autoIncrement, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
128 ASSERT(transactionPtr->mode() == IDBTransaction::VERSION_CHANGE);
130 if (m_objectStores.contains(name)) {
131 ec = IDBDatabaseException::CONSTRAINT_ERR;
135 RefPtr<IDBObjectStoreBackendImpl> objectStore = IDBObjectStoreBackendImpl::create(m_sqliteDatabase.get(), name, keyPath, autoIncrement);
136 ASSERT(objectStore->name() == name);
138 RefPtr<IDBDatabaseBackendImpl> database = this;
139 RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr;
140 if (!transaction->scheduleTask(createCallbackTask(&IDBDatabaseBackendImpl::createObjectStoreInternal, database, objectStore, transaction),
141 createCallbackTask(&IDBDatabaseBackendImpl::removeObjectStoreFromMap, database, objectStore))) {
142 ec = IDBDatabaseException::NOT_ALLOWED_ERR;
146 m_objectStores.set(name, objectStore);
147 return objectStore.release();
150 void IDBDatabaseBackendImpl::createObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBTransactionBackendInterface> transaction)
152 SQLiteStatement insert(database->sqliteDatabase(), "INSERT INTO ObjectStores (name, keyPath, doAutoIncrement, databaseId) VALUES (?, ?, ?, ?)");
153 if (insert.prepare() != SQLResultOk) {
154 transaction->abort();
157 insert.bindText(1, objectStore->name());
158 insert.bindText(2, objectStore->keyPath());
159 insert.bindInt(3, static_cast<int>(objectStore->autoIncrement()));
160 insert.bindInt64(4, database->id());
161 if (insert.step() != SQLResultDone) {
162 transaction->abort();
165 int64_t id = database->sqliteDatabase().lastInsertRowID();
166 objectStore->setId(id);
167 transaction->didCompleteTaskEvents();
170 PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::objectStore(const String& name)
172 return m_objectStores.get(name);
175 static void doDelete(SQLiteDatabase& db, const char* sql, int64_t id)
177 SQLiteStatement deleteQuery(db, sql);
178 bool ok = deleteQuery.prepare() == SQLResultOk;
179 ASSERT_UNUSED(ok, ok); // FIXME: Better error handling.
180 deleteQuery.bindInt64(1, id);
181 ok = deleteQuery.step() == SQLResultDone;
182 ASSERT_UNUSED(ok, ok); // FIXME: Better error handling.
185 void IDBDatabaseBackendImpl::deleteObjectStore(const String& name, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
187 RefPtr<IDBObjectStoreBackendImpl> objectStore = m_objectStores.get(name);
189 ec = IDBDatabaseException::NOT_FOUND_ERR;
192 RefPtr<IDBDatabaseBackendImpl> database = this;
193 RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr;
194 if (!transaction->scheduleTask(createCallbackTask(&IDBDatabaseBackendImpl::deleteObjectStoreInternal, database, objectStore, transaction),
195 createCallbackTask(&IDBDatabaseBackendImpl::addObjectStoreToMap, database, objectStore))) {
196 ec = IDBDatabaseException::NOT_ALLOWED_ERR;
199 m_objectStores.remove(name);
202 void IDBDatabaseBackendImpl::deleteObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBTransactionBackendInterface> transaction)
204 doDelete(database->sqliteDatabase(), "DELETE FROM ObjectStores WHERE id = ?", objectStore->id());
205 doDelete(database->sqliteDatabase(), "DELETE FROM ObjectStoreData WHERE objectStoreId = ?", objectStore->id());
206 doDelete(database->sqliteDatabase(), "DELETE FROM IndexData WHERE indexId IN (SELECT id FROM Indexes WHERE objectStoreId = ?)", objectStore->id());
207 doDelete(database->sqliteDatabase(), "DELETE FROM Indexes WHERE objectStoreId = ?", objectStore->id());
209 transaction->didCompleteTaskEvents();
212 void IDBDatabaseBackendImpl::setVersion(const String& version, PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode& ec)
214 RefPtr<IDBDatabaseBackendImpl> database = this;
215 RefPtr<IDBCallbacks> callbacks = prpCallbacks;
216 RefPtr<DOMStringList> objectStoreNames = DOMStringList::create();
217 RefPtr<IDBTransactionBackendInterface> transaction = IDBTransactionBackendImpl::create(objectStoreNames.get(), IDBTransaction::VERSION_CHANGE, this);
218 if (!transaction->scheduleTask(createCallbackTask(&IDBDatabaseBackendImpl::setVersionInternal, database, version, callbacks, transaction),
219 createCallbackTask(&IDBDatabaseBackendImpl::resetVersion, database, m_version))) {
220 ec = IDBDatabaseException::NOT_ALLOWED_ERR;
224 void IDBDatabaseBackendImpl::setVersionInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, const String& version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction)
226 int64_t databaseId = database->id();
227 database->m_version = version;
228 if (!setMetaData(database->m_sqliteDatabase->db(), database->m_name, database->m_version, databaseId)) {
229 // FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors.
230 callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage."));
231 transaction->abort();
234 callbacks->onSuccess(transaction);
237 PassRefPtr<IDBTransactionBackendInterface> IDBDatabaseBackendImpl::transaction(DOMStringList* objectStoreNames, unsigned short mode, ExceptionCode& ec)
239 for (size_t i = 0; i < objectStoreNames->length(); ++i) {
240 if (!m_objectStores.contains(objectStoreNames->item(i))) {
241 ec = IDBDatabaseException::NOT_FOUND_ERR;
246 // FIXME: Return not allowed err if close has been called.
247 return IDBTransactionBackendImpl::create(objectStoreNames, mode, this);
250 void IDBDatabaseBackendImpl::close()
255 void IDBDatabaseBackendImpl::loadObjectStores()
257 SQLiteStatement objectStoresQuery(sqliteDatabase(), "SELECT id, name, keyPath, doAutoIncrement FROM ObjectStores WHERE databaseId = ?");
258 bool ok = objectStoresQuery.prepare() == SQLResultOk;
259 ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
261 objectStoresQuery.bindInt64(1, m_id);
263 while (objectStoresQuery.step() == SQLResultRow) {
264 int64_t id = objectStoresQuery.getColumnInt64(0);
265 String name = objectStoresQuery.getColumnText(1);
266 String keyPath = objectStoresQuery.getColumnText(2);
267 bool autoIncrement = !!objectStoresQuery.getColumnInt(3);
269 m_objectStores.set(name, IDBObjectStoreBackendImpl::create(m_sqliteDatabase.get(), id, name, keyPath, autoIncrement));
273 void IDBDatabaseBackendImpl::removeObjectStoreFromMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore)
275 ASSERT(database->m_objectStores.contains(objectStore->name()));
276 database->m_objectStores.remove(objectStore->name());
279 void IDBDatabaseBackendImpl::addObjectStoreToMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore)
281 RefPtr<IDBObjectStoreBackendImpl> objectStorePtr = objectStore;
282 ASSERT(!database->m_objectStores.contains(objectStorePtr->name()));
283 database->m_objectStores.set(objectStorePtr->name(), objectStorePtr);
286 void IDBDatabaseBackendImpl::resetVersion(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, const String& version)
288 database->m_version = version;
292 } // namespace WebCore
294 #endif // ENABLE(INDEXED_DATABASE)