void MemoryObjectStoreCursor::iterate(const IDBKeyData& key, uint32_t count, IDBGetResult& outData) { LOG(IndexedDB, "MemoryObjectStoreCursor::iterate to key %s", key.loggingString().utf8().data()); if (!m_objectStore.orderedKeys()) { m_currentPositionKey = { }; outData = { }; return; } if (key.isValid() && !m_info.range().containsKey(key)) { m_currentPositionKey = { }; outData = { }; return; } auto* set = m_objectStore.orderedKeys(); if (set) { if (m_info.isDirectionForward()) incrementForwardIterator(*set, key, count); else incrementReverseIterator(*set, key, count); } m_currentPositionKey = { }; if (!hasValidPosition()) { outData = { }; return; } currentData(outData); }
void MemoryObjectStoreCursor::incrementReverseIterator(std::set<IDBKeyData>& set, const IDBKeyData& key, uint32_t count) { // We might need to re-grab the current iterator. // e.g. If the record it was pointed to had been deleted. bool didResetIterator = false; if (!m_iterator) { if (!m_currentPositionKey.isValid()) return; m_remainingRange.upperKey = m_currentPositionKey; m_remainingRange.upperOpen = false; setFirstInRemainingRange(set); didResetIterator = true; } if (*m_iterator == set.end()) return; if (key.isValid()) { // If iterating to a key, the count passed in must be zero, as there is no way to iterate by both a count and to a key. ASSERT(!count); if (!m_info.range().containsKey(key)) return; if ((*m_iterator)->compare(key) > 0) { m_remainingRange.upperKey = key; m_remainingRange.upperOpen = false; setFirstInRemainingRange(set); } return; } if (!count) count = 1; // If the reverseIterator was reset because it's previous record had been deleted, // we might have already advanced past the current position, eating one one of the iteration counts. if (didResetIterator && (*m_iterator)->compare(m_currentPositionKey) < 0) --count; while (count) { if (*m_iterator == set.begin()) { m_iterator = Nullopt; return; } --count; --*m_iterator; if (!m_info.range().containsKey(**m_iterator)) { m_iterator = Nullopt; return; } } }
void UniqueIDBDatabase::performPutOrAdd(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyData& keyData, const ThreadSafeDataBuffer& valueData, IndexedDB::ObjectStoreOverwriteMode overwriteMode) { ASSERT(!isMainThread()); LOG(IndexedDB, "(db) UniqueIDBDatabase::performPutOrAdd"); ASSERT(m_backingStore); ASSERT(objectStoreIdentifier); IDBKeyData usedKey; IDBError error; auto objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier); if (!objectStoreInfo) { error = IDBError(IDBExceptionCode::InvalidStateError, ASCIILiteral("Object store cannot be found in the backing store")); m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey)); return; } if (objectStoreInfo->autoIncrement() && !keyData.isValid()) { uint64_t keyNumber; error = m_backingStore->generateKeyNumber(transactionIdentifier, objectStoreIdentifier, keyNumber); if (!error.isNull()) { m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey)); return; } usedKey.setNumberValue(keyNumber); } else usedKey = keyData; if (overwriteMode == IndexedDB::ObjectStoreOverwriteMode::NoOverwrite) { bool keyExists; error = m_backingStore->keyExistsInObjectStore(transactionIdentifier, objectStoreIdentifier, usedKey, keyExists); if (error.isNull() && keyExists) error = IDBError(IDBExceptionCode::ConstraintError, ASCIILiteral("Key already exists in the object store")); if (!error.isNull()) { m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey)); return; } } // 3.4.1 Object Store Storage Operation // ...If a record already exists in store ... // then remove the record from store using the steps for deleting records from an object store... // This is important because formally deleting it from from the object store also removes it from the appropriate indexes. error = m_backingStore->deleteRange(transactionIdentifier, objectStoreIdentifier, usedKey); if (!error.isNull()) { m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey)); return; } error = m_backingStore->addRecord(transactionIdentifier, objectStoreIdentifier, usedKey, valueData); m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey)); }
void IDBCursor::continueFunction(const IDBKeyData& key, ExceptionCodeWithMessage& ec) { LOG(IndexedDB, "IDBCursor::continueFunction"); if (!m_request) { ec.code = IDBDatabaseException::InvalidStateError; return; } if (sourcesDeleted()) { ec.code = IDBDatabaseException::InvalidStateError; return; } if (!transaction().isActive()) { ec.code = IDBDatabaseException::TransactionInactiveError; ec.message = ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The transaction is inactive or finished."); return; } if (!m_gotValue) { ec.code = IDBDatabaseException::InvalidStateError; ec.message = ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The cursor is being iterated or has iterated past its end."); return; } if (!key.isNull() && !key.isValid()) { ec.code = IDBDatabaseException::DataError; ec.message = ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is not a valid key."); return; } if (m_info.isDirectionForward()) { if (!key.isNull() && key.compare(m_currentPrimaryKeyData) <= 0) { ec.code = IDBDatabaseException::DataError; ec.message = ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is less than or equal to this cursor's position."); return; } } else if (!key.isNull() && key.compare(m_currentPrimaryKeyData) >= 0) { ec.code = IDBDatabaseException::DataError; ec.message = ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is greater than or equal to this cursor's position."); return; } m_gotValue = false; uncheckedIteratorCursor(key, 0); }
void IDBCursor::continueFunction(const IDBKeyData& key, ExceptionCode& ec) { LOG(IndexedDB, "IDBCursor::continueFunction"); if (!m_request) { ec = IDBDatabaseException::InvalidStateError; return; } if (sourcesDeleted()) { ec = IDBDatabaseException::InvalidStateError; return; } if (!transaction().isActive()) { ec = IDBDatabaseException::TransactionInactiveError; return; } if (!m_gotValue) { ec = IDBDatabaseException::InvalidStateError; return; } if (!key.isNull() && !key.isValid()) { ec = IDBDatabaseException::DataError; return; } if (m_info.isDirectionForward()) { if (!key.isNull() && key.compare(m_currentPrimaryKeyData) <= 0) { ec = IDBDatabaseException::DataError; return; } } else if (!key.isNull() && key.compare(m_currentPrimaryKeyData) >= 0) { ec = IDBDatabaseException::DataError; return; } m_gotValue = false; uncheckedIteratorCursor(key, 0); }
static bool decodeKey(const uint8_t*& data, const uint8_t* end, IDBKeyData& result) { if (!data || data >= end) return false; SIDBKeyType type = static_cast<SIDBKeyType>(data++[0]); switch (type) { case SIDBKeyType::Min: result = IDBKeyData::minimum(); return true; case SIDBKeyType::Max: result = IDBKeyData::maximum(); return true; case SIDBKeyType::Number: { double d; if (!readDouble(data, end, d)) return false; result.setNumberValue(d); return true; } case SIDBKeyType::Date: { double d; if (!readDouble(data, end, d)) return false; result.setDateValue(d); return true; } case SIDBKeyType::String: { uint32_t length; if (!readLittleEndian(data, end, length)) return false; if (static_cast<uint64_t>(end - data) < length * 2) return false; Vector<UChar> buffer; buffer.reserveInitialCapacity(length); for (size_t i = 0; i < length; i++) { uint16_t ch; if (!readLittleEndian(data, end, ch)) return false; buffer.uncheckedAppend(ch); } result.setStringValue(String::adopt(WTFMove(buffer))); return true; } case SIDBKeyType::Binary: { uint64_t size64; if (!readLittleEndian(data, end, size64)) return false; if (static_cast<uint64_t>(end - data) < size64) return false; if (size64 > std::numeric_limits<size_t>::max()) return false; size_t size = static_cast<size_t>(size64); Vector<uint8_t> dataVector; dataVector.append(data, size); data += size; result.setBinaryValue(ThreadSafeDataBuffer::adoptVector(dataVector)); return true; } case SIDBKeyType::Array: { uint64_t size64; if (!readLittleEndian(data, end, size64)) return false; if (size64 > std::numeric_limits<size_t>::max()) return false; size_t size = static_cast<size_t>(size64); Vector<IDBKeyData> array; array.reserveInitialCapacity(size); for (size_t i = 0; i < size; ++i) { IDBKeyData keyData; if (!decodeKey(data, end, keyData)) return false; ASSERT(keyData.isValid()); array.uncheckedAppend(WTFMove(keyData)); } result.setArrayValue(array); return true; } default: LOG_ERROR("decodeKey encountered unexpected type: %i", (int)type); return false; } }