1 | /* |
2 | * Copyright (C) 2015 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. ``AS IS'' AND ANY |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "MemoryObjectStore.h" |
28 | |
29 | #if ENABLE(INDEXED_DATABASE) |
30 | |
31 | #include "IDBBindingUtilities.h" |
32 | #include "IDBError.h" |
33 | #include "IDBGetAllResult.h" |
34 | #include "IDBKeyRangeData.h" |
35 | #include "IDBValue.h" |
36 | #include "IndexKey.h" |
37 | #include "Logging.h" |
38 | #include "MemoryBackingStoreTransaction.h" |
39 | #include "UniqueIDBDatabase.h" |
40 | #include <JavaScriptCore/JSCJSValue.h> |
41 | #include <JavaScriptCore/JSCJSValueInlines.h> |
42 | #include <JavaScriptCore/JSLock.h> |
43 | |
44 | namespace WebCore { |
45 | using namespace JSC; |
46 | namespace IDBServer { |
47 | |
48 | Ref<MemoryObjectStore> MemoryObjectStore::create(const IDBObjectStoreInfo& info) |
49 | { |
50 | return adoptRef(*new MemoryObjectStore(info)); |
51 | } |
52 | |
53 | MemoryObjectStore::MemoryObjectStore(const IDBObjectStoreInfo& info) |
54 | : m_info(info) |
55 | { |
56 | } |
57 | |
58 | MemoryObjectStore::~MemoryObjectStore() |
59 | { |
60 | m_writeTransaction = nullptr; |
61 | } |
62 | |
63 | MemoryIndex* MemoryObjectStore::indexForIdentifier(uint64_t identifier) |
64 | { |
65 | ASSERT(identifier); |
66 | return m_indexesByIdentifier.get(identifier); |
67 | } |
68 | |
69 | void MemoryObjectStore::writeTransactionStarted(MemoryBackingStoreTransaction& transaction) |
70 | { |
71 | LOG(IndexedDB, "MemoryObjectStore::writeTransactionStarted" ); |
72 | |
73 | ASSERT(!m_writeTransaction); |
74 | m_writeTransaction = &transaction; |
75 | } |
76 | |
77 | void MemoryObjectStore::writeTransactionFinished(MemoryBackingStoreTransaction& transaction) |
78 | { |
79 | LOG(IndexedDB, "MemoryObjectStore::writeTransactionFinished" ); |
80 | |
81 | ASSERT_UNUSED(transaction, m_writeTransaction == &transaction); |
82 | m_writeTransaction = nullptr; |
83 | } |
84 | |
85 | IDBError MemoryObjectStore::createIndex(MemoryBackingStoreTransaction& transaction, const IDBIndexInfo& info) |
86 | { |
87 | LOG(IndexedDB, "MemoryObjectStore::createIndex" ); |
88 | |
89 | if (!m_writeTransaction || !m_writeTransaction->isVersionChange() || m_writeTransaction != &transaction) |
90 | return IDBError(ConstraintError); |
91 | |
92 | ASSERT(!m_indexesByIdentifier.contains(info.identifier())); |
93 | auto index = MemoryIndex::create(info, *this); |
94 | |
95 | // If there was an error populating the new index, then the current records in the object store violate its contraints |
96 | auto error = populateIndexWithExistingRecords(index.get()); |
97 | if (!error.isNull()) |
98 | return error; |
99 | |
100 | m_info.addExistingIndex(info); |
101 | transaction.addNewIndex(index.get()); |
102 | registerIndex(WTFMove(index)); |
103 | |
104 | return IDBError { }; |
105 | } |
106 | |
107 | void MemoryObjectStore::maybeRestoreDeletedIndex(Ref<MemoryIndex>&& index) |
108 | { |
109 | LOG(IndexedDB, "MemoryObjectStore::maybeRestoreDeletedIndex" ); |
110 | |
111 | if (m_info.hasIndex(index->info().name())) |
112 | return; |
113 | |
114 | m_info.addExistingIndex(index->info()); |
115 | |
116 | ASSERT(!m_indexesByIdentifier.contains(index->info().identifier())); |
117 | index->clearIndexValueStore(); |
118 | auto error = populateIndexWithExistingRecords(index.get()); |
119 | |
120 | // Since this index was installed in the object store before this transaction started, |
121 | // assuming things were in a valid state then, we should definitely be able to successfully |
122 | // repopulate the index with the object store's pre-transaction records. |
123 | ASSERT_UNUSED(error, error.isNull()); |
124 | |
125 | registerIndex(WTFMove(index)); |
126 | } |
127 | |
128 | RefPtr<MemoryIndex> MemoryObjectStore::takeIndexByIdentifier(uint64_t indexIdentifier) |
129 | { |
130 | auto indexByIdentifier = m_indexesByIdentifier.take(indexIdentifier); |
131 | if (!indexByIdentifier) |
132 | return nullptr; |
133 | |
134 | auto index = m_indexesByName.take(indexByIdentifier->info().name()); |
135 | ASSERT(index); |
136 | |
137 | return index; |
138 | } |
139 | |
140 | IDBError MemoryObjectStore::deleteIndex(MemoryBackingStoreTransaction& transaction, uint64_t indexIdentifier) |
141 | { |
142 | LOG(IndexedDB, "MemoryObjectStore::deleteIndex" ); |
143 | |
144 | if (!m_writeTransaction || !m_writeTransaction->isVersionChange() || m_writeTransaction != &transaction) |
145 | return IDBError(ConstraintError); |
146 | |
147 | auto index = takeIndexByIdentifier(indexIdentifier); |
148 | ASSERT(index); |
149 | if (!index) |
150 | return IDBError(ConstraintError); |
151 | |
152 | m_info.deleteIndex(indexIdentifier); |
153 | transaction.indexDeleted(*index); |
154 | |
155 | return IDBError { }; |
156 | } |
157 | |
158 | void MemoryObjectStore::deleteAllIndexes(MemoryBackingStoreTransaction& transaction) |
159 | { |
160 | Vector<uint64_t> indexIdentifiers; |
161 | indexIdentifiers.reserveInitialCapacity(m_indexesByName.size()); |
162 | |
163 | for (auto& index : m_indexesByName.values()) |
164 | indexIdentifiers.uncheckedAppend(index->info().identifier()); |
165 | |
166 | for (auto identifier : indexIdentifiers) |
167 | deleteIndex(transaction, identifier); |
168 | } |
169 | |
170 | bool MemoryObjectStore::containsRecord(const IDBKeyData& key) |
171 | { |
172 | if (!m_keyValueStore) |
173 | return false; |
174 | |
175 | return m_keyValueStore->contains(key); |
176 | } |
177 | |
178 | void MemoryObjectStore::clear() |
179 | { |
180 | LOG(IndexedDB, "MemoryObjectStore::clear" ); |
181 | ASSERT(m_writeTransaction); |
182 | |
183 | m_writeTransaction->objectStoreCleared(*this, WTFMove(m_keyValueStore), WTFMove(m_orderedKeys)); |
184 | for (auto& index : m_indexesByIdentifier.values()) |
185 | index->objectStoreCleared(); |
186 | |
187 | for (auto& cursor : m_cursors.values()) |
188 | cursor->objectStoreCleared(); |
189 | } |
190 | |
191 | void MemoryObjectStore::replaceKeyValueStore(std::unique_ptr<KeyValueMap>&& store, std::unique_ptr<IDBKeyDataSet>&& orderedKeys) |
192 | { |
193 | ASSERT(m_writeTransaction); |
194 | ASSERT(m_writeTransaction->isAborting()); |
195 | |
196 | m_keyValueStore = WTFMove(store); |
197 | m_orderedKeys = WTFMove(orderedKeys); |
198 | } |
199 | |
200 | void MemoryObjectStore::deleteRecord(const IDBKeyData& key) |
201 | { |
202 | LOG(IndexedDB, "MemoryObjectStore::deleteRecord" ); |
203 | |
204 | ASSERT(m_writeTransaction); |
205 | |
206 | if (!m_keyValueStore) { |
207 | m_writeTransaction->recordValueChanged(*this, key, nullptr); |
208 | return; |
209 | } |
210 | |
211 | ASSERT(m_orderedKeys); |
212 | |
213 | auto iterator = m_keyValueStore->find(key); |
214 | if (iterator == m_keyValueStore->end()) { |
215 | m_writeTransaction->recordValueChanged(*this, key, nullptr); |
216 | return; |
217 | } |
218 | |
219 | m_writeTransaction->recordValueChanged(*this, key, &iterator->value); |
220 | m_keyValueStore->remove(iterator); |
221 | m_orderedKeys->erase(key); |
222 | |
223 | updateIndexesForDeleteRecord(key); |
224 | updateCursorsForDeleteRecord(key); |
225 | } |
226 | |
227 | void MemoryObjectStore::deleteRange(const IDBKeyRangeData& inputRange) |
228 | { |
229 | LOG(IndexedDB, "MemoryObjectStore::deleteRange" ); |
230 | |
231 | ASSERT(m_writeTransaction); |
232 | |
233 | if (inputRange.isExactlyOneKey()) { |
234 | deleteRecord(inputRange.lowerKey); |
235 | return; |
236 | } |
237 | |
238 | IDBKeyRangeData range = inputRange; |
239 | while (true) { |
240 | auto key = lowestKeyWithRecordInRange(range); |
241 | if (key.isNull()) |
242 | break; |
243 | |
244 | deleteRecord(key); |
245 | |
246 | range.lowerKey = key; |
247 | range.lowerOpen = true; |
248 | } |
249 | } |
250 | |
251 | IDBError MemoryObjectStore::addRecord(MemoryBackingStoreTransaction& transaction, const IDBKeyData& keyData, const IDBValue& value) |
252 | { |
253 | LOG(IndexedDB, "MemoryObjectStore::addRecord" ); |
254 | |
255 | ASSERT(m_writeTransaction); |
256 | ASSERT_UNUSED(transaction, m_writeTransaction == &transaction); |
257 | ASSERT(!m_keyValueStore || !m_keyValueStore->contains(keyData)); |
258 | ASSERT(!m_orderedKeys || m_orderedKeys->find(keyData) == m_orderedKeys->end()); |
259 | |
260 | if (!m_keyValueStore) { |
261 | ASSERT(!m_orderedKeys); |
262 | m_keyValueStore = std::make_unique<KeyValueMap>(); |
263 | m_orderedKeys = std::make_unique<IDBKeyDataSet>(); |
264 | } |
265 | |
266 | auto mapResult = m_keyValueStore->set(keyData, value.data()); |
267 | ASSERT(mapResult.isNewEntry); |
268 | auto listResult = m_orderedKeys->insert(keyData); |
269 | ASSERT(listResult.second); |
270 | |
271 | // If there was an error indexing this addition, then revert it. |
272 | auto error = updateIndexesForPutRecord(keyData, value.data()); |
273 | if (!error.isNull()) { |
274 | m_keyValueStore->remove(mapResult.iterator); |
275 | m_orderedKeys->erase(listResult.first); |
276 | } else |
277 | updateCursorsForPutRecord(listResult.first); |
278 | |
279 | return error; |
280 | } |
281 | |
282 | void MemoryObjectStore::updateCursorsForPutRecord(IDBKeyDataSet::iterator iterator) |
283 | { |
284 | for (auto& cursor : m_cursors.values()) |
285 | cursor->keyAdded(iterator); |
286 | } |
287 | |
288 | void MemoryObjectStore::updateCursorsForDeleteRecord(const IDBKeyData& key) |
289 | { |
290 | for (auto& cursor : m_cursors.values()) |
291 | cursor->keyDeleted(key); |
292 | } |
293 | |
294 | void MemoryObjectStore::updateIndexesForDeleteRecord(const IDBKeyData& value) |
295 | { |
296 | for (auto& index : m_indexesByName.values()) |
297 | index->removeEntriesWithValueKey(value); |
298 | } |
299 | |
300 | IDBError MemoryObjectStore::updateIndexesForPutRecord(const IDBKeyData& key, const ThreadSafeDataBuffer& value) |
301 | { |
302 | JSLockHolder locker(UniqueIDBDatabase::databaseThreadVM()); |
303 | |
304 | auto jsValue = deserializeIDBValueToJSValue(UniqueIDBDatabase::databaseThreadExecState(), value); |
305 | if (jsValue.isUndefinedOrNull()) |
306 | return IDBError { }; |
307 | |
308 | IDBError error; |
309 | Vector<std::pair<MemoryIndex*, IndexKey>> changedIndexRecords; |
310 | |
311 | for (auto& index : m_indexesByName.values()) { |
312 | IndexKey indexKey; |
313 | generateIndexKeyForValue(UniqueIDBDatabase::databaseThreadExecState(), index->info(), jsValue, indexKey, m_info.keyPath(), key); |
314 | |
315 | if (indexKey.isNull()) |
316 | continue; |
317 | |
318 | error = index->putIndexKey(key, indexKey); |
319 | if (!error.isNull()) |
320 | break; |
321 | |
322 | changedIndexRecords.append(std::make_pair(index.get(), indexKey)); |
323 | } |
324 | |
325 | // If any of the index puts failed, revert all of the ones that went through. |
326 | if (!error.isNull()) { |
327 | for (auto& record : changedIndexRecords) |
328 | record.first->removeRecord(key, record.second); |
329 | } |
330 | |
331 | return error; |
332 | } |
333 | |
334 | IDBError MemoryObjectStore::populateIndexWithExistingRecords(MemoryIndex& index) |
335 | { |
336 | if (!m_keyValueStore) |
337 | return IDBError { }; |
338 | |
339 | JSLockHolder locker(UniqueIDBDatabase::databaseThreadVM()); |
340 | |
341 | for (const auto& iterator : *m_keyValueStore) { |
342 | auto jsValue = deserializeIDBValueToJSValue(UniqueIDBDatabase::databaseThreadExecState(), iterator.value); |
343 | if (jsValue.isUndefinedOrNull()) |
344 | return IDBError { }; |
345 | |
346 | IndexKey indexKey; |
347 | generateIndexKeyForValue(UniqueIDBDatabase::databaseThreadExecState(), index.info(), jsValue, indexKey, m_info.keyPath(), iterator.key); |
348 | |
349 | if (indexKey.isNull()) |
350 | continue; |
351 | |
352 | IDBError error = index.putIndexKey(iterator.key, indexKey); |
353 | if (!error.isNull()) |
354 | return error; |
355 | } |
356 | |
357 | return IDBError { }; |
358 | } |
359 | |
360 | uint64_t MemoryObjectStore::countForKeyRange(uint64_t indexIdentifier, const IDBKeyRangeData& inRange) const |
361 | { |
362 | LOG(IndexedDB, "MemoryObjectStore::countForKeyRange" ); |
363 | |
364 | if (indexIdentifier) { |
365 | auto* index = m_indexesByIdentifier.get(indexIdentifier); |
366 | ASSERT(index); |
367 | return index->countForKeyRange(inRange); |
368 | } |
369 | |
370 | if (!m_keyValueStore) |
371 | return 0; |
372 | |
373 | uint64_t count = 0; |
374 | IDBKeyRangeData range = inRange; |
375 | while (true) { |
376 | auto key = lowestKeyWithRecordInRange(range); |
377 | if (key.isNull()) |
378 | break; |
379 | |
380 | ++count; |
381 | range.lowerKey = key; |
382 | range.lowerOpen = true; |
383 | } |
384 | |
385 | return count; |
386 | } |
387 | |
388 | ThreadSafeDataBuffer MemoryObjectStore::valueForKey(const IDBKeyData& key) const |
389 | { |
390 | if (!m_keyValueStore) |
391 | return { }; |
392 | |
393 | return m_keyValueStore->get(key); |
394 | } |
395 | |
396 | ThreadSafeDataBuffer MemoryObjectStore::valueForKeyRange(const IDBKeyRangeData& keyRangeData) const |
397 | { |
398 | LOG(IndexedDB, "MemoryObjectStore::valueForKey" ); |
399 | |
400 | IDBKeyData key = lowestKeyWithRecordInRange(keyRangeData); |
401 | if (key.isNull()) |
402 | return ThreadSafeDataBuffer(); |
403 | |
404 | ASSERT(m_keyValueStore); |
405 | return m_keyValueStore->get(key); |
406 | } |
407 | |
408 | void MemoryObjectStore::getAllRecords(const IDBKeyRangeData& keyRangeData, Optional<uint32_t> count, IndexedDB::GetAllType type, IDBGetAllResult& result) const |
409 | { |
410 | result = { type, m_info.keyPath() }; |
411 | |
412 | uint32_t targetCount; |
413 | if (count && count.value()) |
414 | targetCount = count.value(); |
415 | else |
416 | targetCount = std::numeric_limits<uint32_t>::max(); |
417 | |
418 | IDBKeyRangeData range = keyRangeData; |
419 | uint32_t currentCount = 0; |
420 | while (currentCount < targetCount) { |
421 | IDBKeyData key = lowestKeyWithRecordInRange(range); |
422 | if (key.isNull()) |
423 | return; |
424 | |
425 | range.lowerKey = key; |
426 | range.lowerOpen = true; |
427 | if (type == IndexedDB::GetAllType::Values) |
428 | result.addValue(valueForKey(key)); |
429 | result.addKey(WTFMove(key)); |
430 | |
431 | ++currentCount; |
432 | } |
433 | } |
434 | |
435 | IDBGetResult MemoryObjectStore::indexValueForKeyRange(uint64_t indexIdentifier, IndexedDB::IndexRecordType recordType, const IDBKeyRangeData& range) const |
436 | { |
437 | LOG(IndexedDB, "MemoryObjectStore::indexValueForKeyRange" ); |
438 | |
439 | auto* index = m_indexesByIdentifier.get(indexIdentifier); |
440 | ASSERT(index); |
441 | return index->getResultForKeyRange(recordType, range); |
442 | } |
443 | |
444 | IDBKeyData MemoryObjectStore::lowestKeyWithRecordInRange(const IDBKeyRangeData& keyRangeData) const |
445 | { |
446 | if (!m_keyValueStore) |
447 | return { }; |
448 | |
449 | if (keyRangeData.isExactlyOneKey() && m_keyValueStore->contains(keyRangeData.lowerKey)) |
450 | return keyRangeData.lowerKey; |
451 | |
452 | ASSERT(m_orderedKeys); |
453 | |
454 | auto lowestInRange = m_orderedKeys->lower_bound(keyRangeData.lowerKey); |
455 | |
456 | if (lowestInRange == m_orderedKeys->end()) |
457 | return { }; |
458 | |
459 | if (keyRangeData.lowerOpen && *lowestInRange == keyRangeData.lowerKey) |
460 | ++lowestInRange; |
461 | |
462 | if (lowestInRange == m_orderedKeys->end()) |
463 | return { }; |
464 | |
465 | if (!keyRangeData.upperKey.isNull()) { |
466 | if (lowestInRange->compare(keyRangeData.upperKey) > 0) |
467 | return { }; |
468 | if (keyRangeData.upperOpen && *lowestInRange == keyRangeData.upperKey) |
469 | return { }; |
470 | } |
471 | |
472 | return *lowestInRange; |
473 | } |
474 | |
475 | void MemoryObjectStore::registerIndex(Ref<MemoryIndex>&& index) |
476 | { |
477 | ASSERT(!m_indexesByIdentifier.contains(index->info().identifier())); |
478 | ASSERT(!m_indexesByName.contains(index->info().name())); |
479 | |
480 | auto identifier = index->info().identifier(); |
481 | m_indexesByName.set(index->info().name(), &index.get()); |
482 | m_indexesByIdentifier.set(identifier, WTFMove(index)); |
483 | } |
484 | |
485 | void MemoryObjectStore::unregisterIndex(MemoryIndex& index) |
486 | { |
487 | ASSERT(m_indexesByIdentifier.contains(index.info().identifier())); |
488 | ASSERT(m_indexesByName.contains(index.info().name())); |
489 | |
490 | m_indexesByName.remove(index.info().name()); |
491 | m_indexesByIdentifier.remove(index.info().identifier()); |
492 | } |
493 | |
494 | MemoryObjectStoreCursor* MemoryObjectStore::maybeOpenCursor(const IDBCursorInfo& info) |
495 | { |
496 | auto result = m_cursors.add(info.identifier(), nullptr); |
497 | if (!result.isNewEntry) |
498 | return nullptr; |
499 | |
500 | result.iterator->value = std::make_unique<MemoryObjectStoreCursor>(*this, info); |
501 | return result.iterator->value.get(); |
502 | } |
503 | |
504 | void MemoryObjectStore::renameIndex(MemoryIndex& index, const String& newName) |
505 | { |
506 | ASSERT(m_indexesByName.get(index.info().name()) == &index); |
507 | ASSERT(!m_indexesByName.contains(newName)); |
508 | ASSERT(m_info.infoForExistingIndex(index.info().name())); |
509 | |
510 | m_info.infoForExistingIndex(index.info().name())->rename(newName); |
511 | m_indexesByName.set(newName, m_indexesByName.take(index.info().name())); |
512 | index.rename(newName); |
513 | } |
514 | |
515 | } // namespace IDBServer |
516 | } // namespace WebCore |
517 | |
518 | #endif // ENABLE(INDEXED_DATABASE) |
519 | |