blob: a0a26b7acf8d96a97aa553e64f5c4a331810ae55 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_INDEXED_DB_INSTANCE_BACKING_STORE_H_
#define CONTENT_BROWSER_INDEXED_DB_INSTANCE_BACKING_STORE_H_
#include <list>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "base/types/expected.h"
#include "components/services/storage/indexed_db/locks/partitioned_lock.h"
#include "content/browser/indexed_db/indexed_db_external_object_storage.h"
#include "content/browser/indexed_db/status.h"
#include "third_party/blink/public/common/indexeddb/indexeddb_key_range.h"
#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
namespace base {
class WaitableEvent;
}
namespace content::indexed_db {
struct IndexedDBValue;
// NB: This interface is a WIP and is expected to experience heavy churn in the
// near future as additional interfaces are introduced to appropriately abstract
// the different database engines. See crbug.com/40273263.
//
// This interface abstracts the implementation of the data store for IDB data
// for a single bucket (which may contain many IDB databases). The current
// complete implementation uses LevelDB as its engine, along with a bespoke
// flat-file store for blobs, and is called level_db::BackingStore. In this
// system, each BackingStore corresponds to one LevelDB database.
//
// The SQLite version is a work in progress. There, each IDBDatabase correlates
// to a different database, so the BackingStore uses a collection of .sql
// files.
//
// Many of the methods here are likely to be moved to sibling interfaces that
// better encapsulate semantic objects. Eventually, all access to the
// BackingStore should be routed through this interface or its siblings rather
// than reaching directly into level_db::BackingStore.
class BackingStore {
public:
// Used to uniquely identify a record in the database. Can be treated as an
// opaque token by consumers of the `BackingStore`.
struct RecordIdentifier {
// The meaning of these fields is backend-specific. Consumer code should
// ignore them.
// SQLite: a row id. LevelDB: a version.
int64_t number;
// SQLite: unused. LevelDB: the *encoded* primary key bytes.
std::string data;
};
class Cursor;
class Transaction;
// Represents a database in the backing store. A single Database may be
// associated with many connections and transactions.
class Database {
public:
virtual ~Database() = default;
// Memory-cached metadata for this database.
virtual const blink::IndexedDBDatabaseMetadata& GetMetadata() = 0;
// Generates the lock ID key for the given object store. Not called on
// SQLite backing stores.
virtual std::string GetObjectStoreLockIdKey(
int64_t object_store_id) const = 0;
// Creates a transaction on this database.
virtual std::unique_ptr<Transaction> CreateTransaction(
blink::mojom::IDBTransactionDurability durability,
blink::mojom::IDBTransactionMode mode) = 0;
// Deletes the database from the backing store and resets metadata to a
// mostly uninitialized state. If the database does not exist, this should
// return Status::OK() and `on_complete` need not be called. (The LevelDB
// backing store does call it, which is harmless but unnecessary.)
[[nodiscard]] virtual Status DeleteDatabase(
std::vector<PartitionedLock> locks,
base::OnceClosure on_complete) = 0;
};
// This interface wraps state and actions executed on the backing store by the
// store-agnostic `Transaction`, and is to be implemented by backends such as
// LevelDB or SQLite.
// Each transaction is associated with a single `Database`.
class Transaction {
public:
virtual ~Transaction() = default;
// For now, refer to comments in level_db::BackingStore::Transaction for
// documentation.
virtual void Begin(std::vector<PartitionedLock> locks) = 0;
virtual Status CommitPhaseOne(
BlobWriteCallback blob_write_callback,
SerializeFsaCallback serialize_fsa_handle) = 0;
virtual Status CommitPhaseTwo() = 0;
virtual void Rollback() = 0;
// Changes the database version to |version|.
[[nodiscard]] virtual Status SetDatabaseVersion(int64_t version) = 0;
[[nodiscard]] virtual Status CreateObjectStore(
int64_t object_store_id,
const std::u16string& name,
blink::IndexedDBKeyPath key_path,
bool auto_increment) = 0;
[[nodiscard]] virtual Status DeleteObjectStore(int64_t object_store_id) = 0;
[[nodiscard]] virtual Status RenameObjectStore(
int64_t object_store_id,
const std::u16string& new_name) = 0;
// Removes all data contained in the given object store but keeps the object
// store.
[[nodiscard]] virtual Status ClearObjectStore(int64_t object_store_id) = 0;
// Creates a new index metadata and writes it to the transaction.
[[nodiscard]] virtual Status CreateIndex(
int64_t object_store_id,
blink::IndexedDBIndexMetadata index) = 0;
// Deletes the index metadata on the transaction (but not any index
// entries).
[[nodiscard]] virtual Status DeleteIndex(int64_t object_store_id,
int64_t index_id) = 0;
// Renames the given index and writes it to the transaction.
[[nodiscard]] virtual Status RenameIndex(
int64_t object_store_id,
int64_t index_id,
const std::u16string& new_name) = 0;
// When not found, the returned value is empty.
[[nodiscard]] virtual StatusOr<IndexedDBValue> GetRecord(
int64_t object_store_id,
const blink::IndexedDBKey& key) = 0;
// When successful, returns the identifier for the newly stored record.
[[nodiscard]] virtual StatusOr<RecordIdentifier> PutRecord(
int64_t object_store_id,
const blink::IndexedDBKey& key,
IndexedDBValue value) = 0;
[[nodiscard]] virtual Status DeleteRange(
int64_t object_store_id,
const blink::IndexedDBKeyRange&) = 0;
[[nodiscard]] virtual StatusOr<int64_t> GetKeyGeneratorCurrentNumber(
int64_t object_store_id) = 0;
// Sets the key generator current number for `object_store_id` to
// max(`new_number`, current number). `was_generated` is a hint that can be
// used by implementations to skip reading the current number.
[[nodiscard]] virtual Status MaybeUpdateKeyGeneratorCurrentNumber(
int64_t object_store_id,
int64_t new_number,
bool was_generated) = 0;
// Returns the `RecordIdentifier` for the record if the primary key exists
// in the given object store. Returns `Status` on error. Returns nullopt if
// no record exists with the given key.
[[nodiscard]] virtual StatusOr<std::optional<RecordIdentifier>>
KeyExistsInObjectStore(int64_t object_store_id,
const blink::IndexedDBKey& key) = 0;
[[nodiscard]] virtual Status PutIndexDataForRecord(
int64_t object_store_id,
int64_t index_id,
const blink::IndexedDBKey& key,
const RecordIdentifier& record) = 0;
// Returns the primary key of the first record (sorted by primary key) in
// the index with key value `key`, if found. Returns a "none" key
// (!IsValid()) if not found. Returns a `Status` on database error.
[[nodiscard]] virtual StatusOr<blink::IndexedDBKey>
GetFirstPrimaryKeyForIndexKey(int64_t object_store_id,
int64_t index_id,
const blink::IndexedDBKey& key) = 0;
[[nodiscard]] virtual StatusOr<uint32_t> GetObjectStoreKeyCount(
int64_t object_store_id,
blink::IndexedDBKeyRange key_range) = 0;
[[nodiscard]] virtual StatusOr<uint32_t> GetIndexKeyCount(
int64_t object_store_id,
int64_t index_id,
blink::IndexedDBKeyRange key_range) = 0;
virtual StatusOr<std::unique_ptr<Cursor>> OpenObjectStoreKeyCursor(
int64_t object_store_id,
const blink::IndexedDBKeyRange& key_range,
blink::mojom::IDBCursorDirection) = 0;
virtual StatusOr<std::unique_ptr<Cursor>> OpenObjectStoreCursor(
int64_t object_store_id,
const blink::IndexedDBKeyRange& key_range,
blink::mojom::IDBCursorDirection) = 0;
virtual StatusOr<std::unique_ptr<Cursor>> OpenIndexKeyCursor(
int64_t object_store_id,
int64_t index_id,
const blink::IndexedDBKeyRange& key_range,
blink::mojom::IDBCursorDirection) = 0;
virtual StatusOr<std::unique_ptr<Cursor>> OpenIndexCursor(
int64_t object_store_id,
int64_t index_id,
const blink::IndexedDBKeyRange& key_range,
blink::mojom::IDBCursorDirection) = 0;
// Builds a complete value to be passed to the renderer by creating external
// objects for `value`. `deserialize_handle` can be used to help create FSA
// handle external objects out of their serialized representations.
virtual blink::mojom::IDBValuePtr BuildMojoValue(
IndexedDBValue value,
DeserializeFsaCallback deserialize_handle) = 0;
};
// Another interface to be implemented by a backend implementation.
class Cursor {
public:
virtual ~Cursor() = default;
virtual const blink::IndexedDBKey& GetKey() const = 0;
virtual const blink::IndexedDBKey& GetPrimaryKey() const = 0;
virtual blink::IndexedDBKey TakeKey() && = 0;
virtual IndexedDBValue& GetValue() = 0;
// Advances the cursor to a new row and loads the row data. If the input
// keys are valid, advances the cursor to the row for `key` or `key` and
// `primary_key`. Returns true on success, or false if no eligible row was
// found. Returns an error if there was a DB error.
virtual StatusOr<bool> Continue() = 0;
virtual StatusOr<bool> Continue(const blink::IndexedDBKey& key,
const blink::IndexedDBKey& primary_key) = 0;
virtual StatusOr<bool> Advance(uint32_t count) = 0;
// Saves the current position of the cursor.
virtual void SavePosition() = 0;
// Attempts to reset the cursor to the last saved position. The cursor
// may not be in a valid state if this returns false.
virtual bool TryResetToLastSavedPosition() = 0;
};
virtual ~BackingStore() = default;
// The BucketContext deletes itself and the BackingStore when it has no
// database or blob connections active (after a short timeout). This method
// should return true if there are no connections and no blobs. Note that the
// LevelDB store just returns true because the BucketContext implements the
// logic for it. SQLite blobs are managed by the store itself, so this method
// is necessary.
// TODO(crbug.com/419203257): consider revisiting this logic since there's
// very little memory to be reclaimed by deleting the SQLite BackingStore.
virtual bool CanOpportunisticallyClose() const = 0;
virtual void TearDown(base::WaitableEvent* signal_on_destruction) = 0;
virtual void InvalidateBlobReferences() = 0;
// Get tasks to be run after a BackingStore no longer has any connections.
virtual void StartPreCloseTasks(base::OnceClosure on_done) = 0;
virtual void StopPreCloseTasks() = 0;
// Gets the total size of blobs and the database for in-memory backing
// stores.
virtual int64_t GetInMemorySize() const = 0;
// Returns true iff a database with the given name exists, whether or not it's
// currently open.
[[nodiscard]] virtual StatusOr<bool> DatabaseExists(
std::u16string_view name) = 0;
// Returns a list of names of existing databases and their version numbers
// (i.e. `IndexedDBDatabaseMetadata::version`), regardless of whether they're
// currently open.
[[nodiscard]]
virtual StatusOr<std::vector<blink::mojom::IDBNameAndVersionPtr>>
GetDatabaseNamesAndVersions() = 0;
// Creates a new database in the backing store, or opens an existing one. If
// pre-existing, the database's metadata will be populated from disk.
// Otherwise the version will be initialized to NO_VERSION.
[[nodiscard]] virtual StatusOr<std::unique_ptr<BackingStore::Database>>
CreateOrOpenDatabase(const std::u16string& name) = 0;
virtual uintptr_t GetIdentifierForMemoryDump() = 0;
// Writes backing store files to disk in their long-term format, e.g. converts
// a log to actual DB files.
virtual void FlushForTesting() = 0;
};
} // namespace content::indexed_db
#endif // CONTENT_BROWSER_INDEXED_DB_INSTANCE_BACKING_STORE_H_