IndexedDB: Support Array-type key paths
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / IDBObjectStore.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 "IDBObjectStore.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30
31 #include "DOMStringList.h"
32 #include "IDBAny.h"
33 #include "IDBDatabaseException.h"
34 #include "IDBIndex.h"
35 #include "IDBKey.h"
36 #include "IDBKeyPath.h"
37 #include "IDBKeyRange.h"
38 #include "IDBTracing.h"
39 #include "IDBTransaction.h"
40 #include "SerializedScriptValue.h"
41 #include <wtf/UnusedParam.h>
42
43 namespace WebCore {
44
45 static const unsigned short defaultDirection = IDBCursor::NEXT;
46
47 IDBObjectStore::IDBObjectStore(PassRefPtr<IDBObjectStoreBackendInterface> idbObjectStore, IDBTransaction* transaction)
48     : m_backend(idbObjectStore)
49     , m_transaction(transaction)
50 {
51     ASSERT(m_backend);
52     ASSERT(m_transaction);
53     // We pass a reference to this object before it can be adopted.
54     relaxAdoptionRequirement();
55 }
56
57 String IDBObjectStore::name() const
58 {
59     IDB_TRACE("IDBObjectStore::name");
60     return m_backend->name();
61 }
62
63 PassRefPtr<IDBAny> IDBObjectStore::keyPath() const
64 {
65     IDB_TRACE("IDBObjectStore::keyPath");
66     return m_backend->keyPath();
67 }
68
69 PassRefPtr<DOMStringList> IDBObjectStore::indexNames() const
70 {
71     IDB_TRACE("IDBObjectStore::indexNames");
72     return m_backend->indexNames();
73 }
74
75 IDBTransaction* IDBObjectStore::transaction() const
76 {
77     IDB_TRACE("IDBObjectStore::transaction");
78     return m_transaction.get();
79 }
80
81 bool IDBObjectStore::autoIncrement() const
82 {
83     IDB_TRACE("IDBObjectStore::autoIncrement");
84     return m_backend->autoIncrement();
85 }
86
87 PassRefPtr<IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec)
88 {
89     IDB_TRACE("IDBObjectStore::get");
90
91     if (!keyRange) {
92         ec = IDBDatabaseException::DATA_ERR;
93         return 0;
94     }
95     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
96     m_backend->get(keyRange, request, m_transaction->backend(), ec);
97     if (ec) {
98         request->markEarlyDeath();
99         return 0;
100     }
101     return request.release();
102 }
103
104 PassRefPtr<IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec)
105 {
106     IDB_TRACE("IDBObjectStore::get");
107     if (!key || (key->type() == IDBKey::InvalidType)) {
108         ec = IDBDatabaseException::DATA_ERR;
109         return 0;
110     }
111
112     RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(key, ec);
113     if (ec)
114         return 0;
115     return get(context, keyRange.release(), ec);
116 }
117
118 PassRefPtr<IDBRequest> IDBObjectStore::add(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> key, ExceptionCode& ec)
119 {
120     IDB_TRACE("IDBObjectStore::add");
121     if (key && (key->type() == IDBKey::InvalidType)) {
122         ec = IDBDatabaseException::DATA_ERR;
123         return 0;
124     }
125
126     RefPtr<SerializedScriptValue> value = prpValue;
127     if (value->blobURLs().size() > 0) {
128         // FIXME: Add Blob/File/FileList support
129         ec = DATA_CLONE_ERR;
130         return 0;
131     }
132
133     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
134     m_backend->put(value, key, IDBObjectStoreBackendInterface::AddOnly, request, m_transaction->backend(), ec);
135     if (ec) {
136         request->markEarlyDeath();
137         return 0;
138     }
139     return request.release();
140 }
141
142 PassRefPtr<IDBRequest> IDBObjectStore::put(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> key, ExceptionCode& ec)
143 {
144     IDB_TRACE("IDBObjectStore::put");
145     if (key && (key->type() == IDBKey::InvalidType)) {
146         ec = IDBDatabaseException::DATA_ERR;
147         return 0;
148     }
149
150     RefPtr<SerializedScriptValue> value = prpValue;
151     if (value->blobURLs().size() > 0) {
152         // FIXME: Add Blob/File/FileList support
153         ec = DATA_CLONE_ERR;
154         return 0;
155     }
156
157     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
158     m_backend->put(value, key, IDBObjectStoreBackendInterface::AddOrUpdate, request, m_transaction->backend(), ec);
159     if (ec) {
160         request->markEarlyDeath();
161         return 0;
162     }
163     return request.release();
164 }
165
166 PassRefPtr<IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec)
167 {
168     IDB_TRACE("IDBObjectStore::delete");
169     if (!keyRange) {
170         ec = IDBDatabaseException::DATA_ERR;
171         return 0;
172     }
173
174     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
175     m_backend->deleteFunction(keyRange, request, m_transaction->backend(), ec);
176     if (ec) {
177         request->markEarlyDeath();
178         return 0;
179     }
180     return request.release();
181 }
182
183 PassRefPtr<IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec)
184 {
185     IDB_TRACE("IDBObjectStore::delete");
186     if (!key || !key->isValid()) {
187         ec = IDBDatabaseException::DATA_ERR;
188         return 0;
189     }
190
191     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
192     m_backend->deleteFunction(key, request, m_transaction->backend(), ec);
193     if (ec) {
194         request->markEarlyDeath();
195         return 0;
196     }
197     return request.release();
198 }
199
200 PassRefPtr<IDBRequest> IDBObjectStore::clear(ScriptExecutionContext* context, ExceptionCode& ec)
201 {
202     IDB_TRACE("IDBObjectStore::clear");
203     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
204     m_backend->clear(request, m_transaction->backend(), ec);
205     if (ec) {
206         request->markEarlyDeath();
207         return 0;
208     }
209     return request.release();
210 }
211
212 PassRefPtr<IDBIndex> IDBObjectStore::createIndex(const String& name, const String& keyPath, const Dictionary& options, ExceptionCode& ec)
213 {
214     return createIndex(name, IDBKeyPath(keyPath), options, ec);
215 }
216
217 PassRefPtr<IDBIndex> IDBObjectStore::createIndex(const String& name, PassRefPtr<DOMStringList> keyPath, const Dictionary& options, ExceptionCode& ec)
218 {
219     // FIXME: Binding code for DOMString[] should not match null. http://webkit.org/b/84217
220     if (!keyPath)
221         return createIndex(name, IDBKeyPath("null"), options, ec);
222     return createIndex(name, IDBKeyPath(*keyPath), options, ec);
223 }
224
225
226 PassRefPtr<IDBIndex> IDBObjectStore::createIndex(const String& name, const IDBKeyPath& keyPath, const Dictionary& options, ExceptionCode& ec)
227 {
228     IDB_TRACE("IDBObjectStore::createIndex");
229     if (!keyPath.isValid()) {
230         ec = SYNTAX_ERR;
231         return 0;
232     }
233
234     bool unique = false;
235     options.get("unique", unique);
236
237     bool multiEntry = false;
238     options.get("multiEntry", multiEntry);
239
240     if (keyPath.type() == IDBKeyPath::ArrayType && multiEntry) {
241         ec = NOT_SUPPORTED_ERR;
242         return 0;
243     }
244
245     RefPtr<IDBIndexBackendInterface> indexBackend = m_backend->createIndex(name, keyPath, unique, multiEntry, m_transaction->backend(), ec);
246     ASSERT(!indexBackend != !ec); // If we didn't get an index, we should have gotten an exception code. And vice versa.
247     if (!indexBackend)
248         return 0;
249     RefPtr<IDBIndex> index = IDBIndex::create(indexBackend.release(), this, m_transaction.get());
250     m_indexMap.set(name, index);
251     return index.release();
252 }
253
254 PassRefPtr<IDBIndex> IDBObjectStore::index(const String& name, ExceptionCode& ec)
255 {
256     IDB_TRACE("IDBObjectStore::index");
257     if (m_transaction->isFinished()) {
258         ec = IDBDatabaseException::NOT_ALLOWED_ERR;
259         return 0;
260     }
261
262     IDBIndexMap::iterator it = m_indexMap.find(name);
263     if (it != m_indexMap.end())
264         return it->second;
265
266     RefPtr<IDBIndexBackendInterface> indexBackend = m_backend->index(name, ec);
267     ASSERT(!indexBackend != !ec); // If we didn't get an index, we should have gotten an exception code. And vice versa.
268     if (!indexBackend)
269         return 0;
270
271     RefPtr<IDBIndex> index = IDBIndex::create(indexBackend.release(), this, m_transaction.get());
272     m_indexMap.set(name, index);
273     return index.release();
274 }
275
276 void IDBObjectStore::deleteIndex(const String& name, ExceptionCode& ec)
277 {
278     m_backend->deleteIndex(name, m_transaction->backend(), ec);
279 }
280
281 PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> range, const String& directionString, ExceptionCode& ec)
282 {
283     IDB_TRACE("IDBObjectStore::openCursor");
284     unsigned short direction = IDBCursor::stringToDirection(directionString, ec);
285     if (ec)
286         return 0;
287
288     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
289     request->setCursorType(IDBCursorBackendInterface::ObjectStoreCursor);
290     m_backend->openCursor(range, direction, request, m_transaction->backend(), ec);
291     if (ec) {
292         request->markEarlyDeath();
293         return 0;
294     }
295     return request.release();
296 }
297
298 PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> range, unsigned short direction, ExceptionCode& ec)
299 {
300     IDB_TRACE("IDBObjectStore::openCursor");
301     DEFINE_STATIC_LOCAL(String, consoleMessage, ("Numeric direction values are deprecated in IDBObjectStore.openCursor. Use\"next\", \"nextunique\", \"prev\", or \"prevunique\"."));
302     context->addConsoleMessage(JSMessageSource, LogMessageType, WarningMessageLevel, consoleMessage);
303     const String& directionString = IDBCursor::directionToString(direction, ec);
304     if (ec)
305         return 0;
306     return openCursor(context, range, directionString, ec);
307 }
308
309 PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, const String& direction, ExceptionCode& ec)
310 {
311     IDB_TRACE("IDBObjectStore::openCursor");
312     RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(key, ec);
313     if (ec)
314         return 0;
315     return openCursor(context, keyRange.release(), ec);
316 }
317
318 PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, unsigned short direction, ExceptionCode& ec)
319 {
320     IDB_TRACE("IDBObjectStore::openCursor");
321     RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(key, ec);
322     if (ec)
323         return 0;
324     return openCursor(context, key, direction, ec);
325 }
326
327 PassRefPtr<IDBRequest> IDBObjectStore::count(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> range, ExceptionCode& ec)
328 {
329     IDB_TRACE("IDBObjectStore::count");
330     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
331     m_backend->count(range, request, m_transaction->backend(), ec);
332     if (ec) {
333         request->markEarlyDeath();
334         return 0;
335     }
336     return request.release();
337 }
338
339 PassRefPtr<IDBRequest> IDBObjectStore::count(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec)
340 {
341     IDB_TRACE("IDBObjectStore::count");
342     RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(key, ec);
343     if (ec)
344         return 0;
345     return count(context, keyRange.release(), ec);
346 }
347
348 void IDBObjectStore::transactionFinished()
349 {
350     ASSERT(m_transaction->isFinished());
351
352     // Break reference cycles.
353     m_indexMap.clear();
354 }
355
356
357 } // namespace WebCore
358
359 #endif // ENABLE(INDEXED_DATABASE)