void IDBObjectStoreBackendImpl::put(PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> prpKey, PutMode putMode, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec) { if (transactionPtr->mode() == IDBTransaction::READ_ONLY) { ec = IDBDatabaseException::READ_ONLY_ERR; return; } RefPtr<IDBObjectStoreBackendImpl> objectStore = this; RefPtr<SerializedScriptValue> value = prpValue; RefPtr<IDBKey> key = prpKey; RefPtr<IDBCallbacks> callbacks = prpCallbacks; RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr; if (putMode != CursorUpdate) { if (key && !key->valid()) { ec = IDBDatabaseException::DATA_ERR; return; } const bool autoIncrement = objectStore->autoIncrement(); const bool hasKeyPath = !objectStore->m_keyPath.isNull(); if (!key && !autoIncrement && !hasKeyPath) { ec = IDBDatabaseException::DATA_ERR; return; } if (hasKeyPath) { if (key) { ec = IDBDatabaseException::DATA_ERR; return; } RefPtr<IDBKey> keyPathKey = fetchKeyFromKeyPath(value.get(), objectStore->m_keyPath); if (!autoIncrement) { if (!keyPathKey || !keyPathKey->valid()) { ec = IDBDatabaseException::DATA_ERR; return; } } else if (keyPathKey && !keyPathKey->valid()) { ec = IDBDatabaseException::DATA_ERR; return; } } for (IndexMap::iterator it = m_indexes.begin(); it != m_indexes.end(); ++it) { const RefPtr<IDBIndexBackendImpl>& index = it->second; RefPtr<IDBKey> indexKey = fetchKeyFromKeyPath(value.get(), index->keyPath()); if (indexKey && !indexKey->valid()) { ec = IDBDatabaseException::DATA_ERR; return; } } } if (!transaction->scheduleTask(createCallbackTask(&IDBObjectStoreBackendImpl::putInternal, objectStore, value, key, putMode, callbacks, transaction))) ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR; }
PassRefPtr<IDBKey> IDBObjectStoreBackendImpl::selectKeyForPut(IDBObjectStoreBackendImpl* objectStore, IDBKey* key, PutMode putMode, IDBCallbacks* callbacks, RefPtr<SerializedScriptValue>& value) { if (putMode == CursorUpdate) ASSERT(key); const bool autoIncrement = objectStore->autoIncrement(); const bool hasKeyPath = !objectStore->m_keyPath.isNull(); if (autoIncrement && key) { objectStore->resetAutoIncrementKeyCache(); return key; } if (autoIncrement) { ASSERT(!key); if (!hasKeyPath) return objectStore->genAutoIncrementKey(); RefPtr<IDBKey> keyPathKey = fetchKeyFromKeyPath(value.get(), objectStore->m_keyPath); if (keyPathKey) { objectStore->resetAutoIncrementKeyCache(); return keyPathKey; } RefPtr<IDBKey> autoIncKey = objectStore->genAutoIncrementKey(); RefPtr<SerializedScriptValue> valueAfterInjection = injectKeyIntoKeyPath(autoIncKey, value, objectStore->m_keyPath); if (!valueAfterInjection) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "The generated key could not be inserted into the object using the keyPath.")); return 0; } value = valueAfterInjection; return autoIncKey.release(); } if (hasKeyPath) { RefPtr<IDBKey> keyPathKey = fetchKeyFromKeyPath(value.get(), objectStore->m_keyPath); // FIXME: This check should be moved to put() and raise an exception. WK76952 if (putMode == CursorUpdate && !keyPathKey->isEqual(key)) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "The key fetched from the keyPath does not match the key of the cursor.")); return 0; } return keyPathKey.release(); } if (!key) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "No key supplied")); return 0; } return key; }
void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> prpKey, PutMode putMode, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction) { RefPtr<SerializedScriptValue> value = prpValue; RefPtr<IDBKey> key = selectKeyForPut(objectStore.get(), prpKey.get(), putMode, callbacks.get(), value); if (!key) return; ASSERT(key->valid()); Vector<RefPtr<IDBKey> > indexKeys; for (IndexMap::iterator it = objectStore->m_indexes.begin(); it != objectStore->m_indexes.end(); ++it) { const RefPtr<IDBIndexBackendImpl>& index = it->second; RefPtr<IDBKey> indexKey = fetchKeyFromKeyPath(value.get(), index->keyPath()); if (!indexKey) { indexKeys.append(indexKey.release()); continue; } ASSERT(indexKey->valid()); if ((!index->multiEntry() || indexKey->type() != IDBKey::ArrayType) && !index->addingKeyAllowed(indexKey.get(), key.get())) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::CONSTRAINT_ERR, "One of the derived (from a keyPath) keys for an index does not satisfy its uniqueness requirements.")); return; } if (index->multiEntry() && indexKey->type() == IDBKey::ArrayType) { for (size_t j = 0; j < indexKey->array().size(); ++j) { if (!index->addingKeyAllowed(indexKey->array()[j].get(), key.get())) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::CONSTRAINT_ERR, "One of the derived (from a keyPath) keys for an index does not satisfy its uniqueness requirements.")); return; } } } indexKeys.append(indexKey.release()); } RefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> recordIdentifier = objectStore->m_backingStore->createInvalidRecordIdentifier(); bool isExistingValue = objectStore->m_backingStore->keyExistsInObjectStore(objectStore->m_databaseId, objectStore->id(), *key, recordIdentifier.get()); if (putMode == AddOnly && isExistingValue) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::CONSTRAINT_ERR, "Key already exists in the object store.")); return; } // Before this point, don't do any mutation. After this point, rollback the transaction in case of error. if (!objectStore->m_backingStore->putObjectStoreRecord(objectStore->m_databaseId, objectStore->id(), *key, value->toWireString(), recordIdentifier.get())) { // FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors. callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage.")); transaction->abort(); return; } int i = 0; for (IndexMap::iterator it = objectStore->m_indexes.begin(); it != objectStore->m_indexes.end(); ++it, ++i) { RefPtr<IDBIndexBackendImpl>& index = it->second; if (!index->hasValidId()) continue; // The index object has been created, but does not exist in the database yet. if (!objectStore->m_backingStore->deleteIndexDataForRecord(objectStore->m_databaseId, objectStore->id(), index->id(), recordIdentifier.get())) { // FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors. callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage.")); transaction->abort(); return; } if (!indexKeys[i]) continue; RefPtr<IDBKey> indexKey = indexKeys[i]; if (!index->multiEntry() || indexKey->type() != IDBKey::ArrayType) { if (!objectStore->m_backingStore->putIndexDataForRecord(objectStore->m_databaseId, objectStore->id(), index->id(), *indexKey, recordIdentifier.get())) { // FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors. callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage.")); transaction->abort(); return; } } else { ASSERT(index->multiEntry()); ASSERT(indexKey->type() == IDBKey::ArrayType); for (size_t j = 0; j < indexKey->array().size(); ++j) { if (!objectStore->m_backingStore->putIndexDataForRecord(objectStore->m_databaseId, objectStore->id(), index->id(), *indexKey->array()[j], recordIdentifier.get())) { // FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors. callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage.")); transaction->abort(); return; } } } } callbacks->onSuccess(key.get()); }
void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> prpKey, bool addOnly, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction) { RefPtr<SerializedScriptValue> value = prpValue; RefPtr<IDBKey> key = prpKey; // FIXME: Support auto-increment. if (!objectStore->m_keyPath.isNull()) { if (key) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "A key was supplied for an objectStore that has a keyPath.")); return; } key = fetchKeyFromKeyPath(value.get(), objectStore->m_keyPath); if (!key) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "The key could not be fetched from the keyPath.")); return; } } else if (!key) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "No key supplied.")); return; } if (key->type() == IDBKey::NullType) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "NULL key is not allowed.")); return; } Vector<RefPtr<IDBKey> > indexKeys; for (IndexMap::iterator it = objectStore->m_indexes.begin(); it != objectStore->m_indexes.end(); ++it) { RefPtr<IDBKey> key = fetchKeyFromKeyPath(value.get(), it->second->keyPath()); if (!key) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "The key could not be fetched from an index's keyPath.")); return; } if (key->type() == IDBKey::NullType) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "One of the derived (from a keyPath) keys for an index is NULL.")); return; } if (!it->second->addingKeyAllowed(key.get())) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "One of the derived (from a keyPath) keys for an index does not satisfy its uniqueness requirements.")); return; } indexKeys.append(key.release()); } SQLiteStatement getQuery(objectStore->sqliteDatabase(), "SELECT id FROM ObjectStoreData " + whereClause(key.get())); bool ok = getQuery.prepare() == SQLResultOk; ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? bindWhereClause(getQuery, objectStore->id(), key.get()); bool isExistingValue = getQuery.step() == SQLResultRow; if (addOnly && isExistingValue) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::CONSTRAINT_ERR, "Key already exists in the object store.")); return; } // Before this point, don't do any mutation. After this point, rollback the transaction in case of error. int64_t dataRowId = isExistingValue ? getQuery.getColumnInt(0) : InvalidId; if (!putObjectStoreData(objectStore->sqliteDatabase(), key.get(), value.get(), objectStore->id(), dataRowId)) { // FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors. callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage.")); transaction->abort(); return; } if (!deleteIndexData(objectStore->sqliteDatabase(), dataRowId)) { // FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors. callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage.")); transaction->abort(); return; } int i = 0; for (IndexMap::iterator it = objectStore->m_indexes.begin(); it != objectStore->m_indexes.end(); ++it, ++i) { if (!putIndexData(objectStore->sqliteDatabase(), indexKeys[i].get(), it->second->id(), dataRowId)) { // FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors. callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage.")); transaction->abort(); return; } } callbacks->onSuccess(key.get()); }