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)); }
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; } }