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;
        }
    }
}
Exemple #3
0
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);
}
Exemple #5
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);
}
Exemple #6
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;
    }
}