OSDN Git Service

Merge WebKit at r78450: Initial merge by git.
[android-x86/external-webkit.git] / Source / WebCore / storage / IDBDatabaseBackendImpl.cpp
1 /*
2  * Copyright (C) 2010 Google 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  *
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.
13  *
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.
24  */
25
26 #include "config.h"
27 #include "IDBDatabaseBackendImpl.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30
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"
41
42 namespace WebCore {
43
44 static bool extractMetaData(SQLiteDatabase& sqliteDatabase, const String& name, String& foundVersion, int64& foundId)
45 {
46     SQLiteStatement databaseQuery(sqliteDatabase, "SELECT id, version FROM Databases WHERE name = ?");
47     if (databaseQuery.prepare() != SQLResultOk) {
48         ASSERT_NOT_REACHED();
49         return false;
50     }
51     databaseQuery.bindText(1, name);
52     if (databaseQuery.step() != SQLResultRow)
53         return false;
54
55     foundId = databaseQuery.getColumnInt64(0);
56     foundVersion = databaseQuery.getColumnText(1);
57
58     if (databaseQuery.step() == SQLResultRow)
59         ASSERT_NOT_REACHED();
60     return true;
61 }
62
63 static bool setMetaData(SQLiteDatabase& sqliteDatabase, const String& name, const String& version, int64_t& rowId)
64 {
65     ASSERT(!name.isNull());
66     ASSERT(!version.isNull());
67
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) {
72         ASSERT_NOT_REACHED();
73         return false;
74     }
75
76     query.bindText(1, name);
77     query.bindText(2, version);
78     if (rowId != IDBDatabaseBackendImpl::InvalidId)
79         query.bindInt64(3, rowId);
80
81     if (query.step() != SQLResultDone)
82         return false;
83
84     if (rowId == IDBDatabaseBackendImpl::InvalidId)
85         rowId = sqliteDatabase.lastInsertRowID();
86
87     return true;
88 }
89
90 IDBDatabaseBackendImpl::IDBDatabaseBackendImpl(const String& name, IDBSQLiteDatabase* sqliteDatabase, IDBTransactionCoordinator* coordinator, IDBFactoryBackendImpl* factory, const String& uniqueIdentifier)
91     : m_sqliteDatabase(sqliteDatabase)
92     , m_id(InvalidId)
93     , m_name(name)
94     , m_version("")
95     , m_identifier(uniqueIdentifier)
96     , m_factory(factory)
97     , m_transactionCoordinator(coordinator)
98 {
99     ASSERT(!m_name.isNull());
100
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.
105     loadObjectStores();
106 }
107
108 IDBDatabaseBackendImpl::~IDBDatabaseBackendImpl()
109 {
110     m_factory->removeIDBDatabaseBackend(m_identifier);
111 }
112
113 SQLiteDatabase& IDBDatabaseBackendImpl::sqliteDatabase() const
114 {
115     return m_sqliteDatabase->db();
116 }
117
118 PassRefPtr<DOMStringList> IDBDatabaseBackendImpl::objectStoreNames() const
119 {
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();
124 }
125
126 PassRefPtr<IDBObjectStoreBackendInterface>  IDBDatabaseBackendImpl::createObjectStore(const String& name, const String& keyPath, bool autoIncrement, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
127 {
128     ASSERT(transactionPtr->mode() == IDBTransaction::VERSION_CHANGE);
129
130     if (m_objectStores.contains(name)) {
131         ec = IDBDatabaseException::CONSTRAINT_ERR;
132         return 0;
133     }
134
135     RefPtr<IDBObjectStoreBackendImpl> objectStore = IDBObjectStoreBackendImpl::create(m_sqliteDatabase.get(), name, keyPath, autoIncrement);
136     ASSERT(objectStore->name() == name);
137
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;
143         return 0;
144     }
145
146     m_objectStores.set(name, objectStore);
147     return objectStore.release();
148 }
149
150 void IDBDatabaseBackendImpl::createObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore,  PassRefPtr<IDBTransactionBackendInterface> transaction)
151 {
152     SQLiteStatement insert(database->sqliteDatabase(), "INSERT INTO ObjectStores (name, keyPath, doAutoIncrement, databaseId) VALUES (?, ?, ?, ?)");
153     if (insert.prepare() != SQLResultOk) {
154         transaction->abort();
155         return;
156     }
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();
163         return;
164     }
165     int64_t id = database->sqliteDatabase().lastInsertRowID();
166     objectStore->setId(id);
167     transaction->didCompleteTaskEvents();
168 }
169
170 PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::objectStore(const String& name)
171 {
172     return m_objectStores.get(name);
173 }
174
175 static void doDelete(SQLiteDatabase& db, const char* sql, int64_t id)
176 {
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.
183 }
184
185 void IDBDatabaseBackendImpl::deleteObjectStore(const String& name, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
186 {
187     RefPtr<IDBObjectStoreBackendImpl> objectStore = m_objectStores.get(name);
188     if (!objectStore) {
189         ec = IDBDatabaseException::NOT_FOUND_ERR;
190         return;
191     }
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;
197         return;
198     }
199     m_objectStores.remove(name);
200 }
201
202 void IDBDatabaseBackendImpl::deleteObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBTransactionBackendInterface> transaction)
203 {
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());
208
209     transaction->didCompleteTaskEvents();
210 }
211
212 void IDBDatabaseBackendImpl::setVersion(const String& version, PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode& ec)
213 {
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;
221     }
222 }
223
224 void IDBDatabaseBackendImpl::setVersionInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, const String& version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction)
225 {
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();
232         return;
233     }
234     callbacks->onSuccess(transaction);
235 }
236
237 PassRefPtr<IDBTransactionBackendInterface> IDBDatabaseBackendImpl::transaction(DOMStringList* objectStoreNames, unsigned short mode, ExceptionCode& ec)
238 {
239     for (size_t i = 0; i < objectStoreNames->length(); ++i) {
240         if (!m_objectStores.contains(objectStoreNames->item(i))) {
241             ec = IDBDatabaseException::NOT_FOUND_ERR;
242             return 0;
243         }
244     }
245
246     // FIXME: Return not allowed err if close has been called.
247     return IDBTransactionBackendImpl::create(objectStoreNames, mode, this);
248 }
249
250 void IDBDatabaseBackendImpl::close()
251 {
252     // FIXME: Implement.
253 }
254
255 void IDBDatabaseBackendImpl::loadObjectStores()
256 {
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?
260
261     objectStoresQuery.bindInt64(1, m_id);
262
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);
268
269         m_objectStores.set(name, IDBObjectStoreBackendImpl::create(m_sqliteDatabase.get(), id, name, keyPath, autoIncrement));
270     }
271 }
272
273 void IDBDatabaseBackendImpl::removeObjectStoreFromMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore)
274 {
275     ASSERT(database->m_objectStores.contains(objectStore->name()));
276     database->m_objectStores.remove(objectStore->name());
277 }
278
279 void IDBDatabaseBackendImpl::addObjectStoreToMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore)
280 {
281     RefPtr<IDBObjectStoreBackendImpl> objectStorePtr = objectStore;
282     ASSERT(!database->m_objectStores.contains(objectStorePtr->name()));
283     database->m_objectStores.set(objectStorePtr->name(), objectStorePtr);
284 }
285
286 void IDBDatabaseBackendImpl::resetVersion(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, const String& version)
287 {
288     database->m_version = version;
289 }
290
291
292 } // namespace WebCore
293
294 #endif // ENABLE(INDEXED_DATABASE)