chromium/sql/database.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "sql/database.h"

#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>

#include <cinttypes>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <tuple>
#include <utility>
#include <vector>

#include "base/check.h"
#include "base/check_op.h"
#include "base/dcheck_is_on.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/format_macros.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/no_destructor.h"
#include "base/notimplemented.h"
#include "base/notreached.h"
#include "base/ranges/algorithm.h"
#include "base/sequence_checker.h"
#include "base/strings/cstring_view.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/lock.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/time/time.h"
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/trace_event.h"
#include "base/tracing/protos/chrome_track_event.pbzero.h"  // IWYU pragma: keep
#include "base/types/pass_key.h"
#include "build/build_config.h"
#include "sql/database_memory_dump_provider.h"
#include "sql/initialization.h"
#include "sql/internal_api_token.h"
#include "sql/meta_table.h"
#include "sql/sqlite_result_code.h"
#include "sql/sqlite_result_code_values.h"
#include "sql/statement.h"
#include "sql/statement_id.h"
#include "sql/transaction.h"
#include "third_party/perfetto/include/perfetto/tracing/traced_proto.h"
#include "third_party/sqlite/sqlite3.h"

#if BUILDFLAG(IS_WIN)
#include "base/containers/contains.h"
#endif

namespace sql {

namespace {

bool enable_mmap_by_default_ =;

// The name of the main database associated with a sqlite3* connection.
//
// SQLite has the ability to ATTACH multiple databases to the same connection.
// As a consequence, some SQLite APIs require the connection-specific database
// name. This is the right name to be passed to such APIs.
static constexpr char kSqliteMainDatabaseName[] =;

// Magic path value telling sqlite3_open_v2() to open an in-memory database.
static constexpr char kSqliteOpenInMemoryPath[] =;

// Spin for up to a second waiting for the lock to clear when setting
// up the database.
// TODO(shess): Better story on this.  http://crbug.com/56559
const int kBusyTimeoutSeconds =;

// RAII-style wrapper that enables `writable_schema` until it goes out of scope.
// No error checking on the PRAGMA statements because it is reasonable to just
// forge ahead in case of an error. If turning it on fails, then most likely
// nothing will work, whereas if turning it off fails, it only matters if some
// code attempts to continue working with the database and tries to modify the
// sqlite_schema table (none of our code does this).
class ScopedWritableSchema {};

// Raze() helper that uses SQLite's online backup API.
//
// Returns the SQLite error code produced by sqlite3_backup_step(). SQLITE_DONE
// signals success. SQLITE_OK will never be returned.
//
// The implementation is tailored for the Raze() use case. In particular, the
// SQLite API use and and error handling is optimized for 1-page databases.
SqliteResultCode BackupDatabaseForRaze(sqlite3* source_db,
                                       sqlite3* destination_db) {}

bool ValidAttachmentPoint(std::string_view attachment_point) {}

std::string AsUTF8ForSQL(const base::FilePath& path) {}

}  // namespace

// static
Database::ScopedErrorExpecterCallback* Database::current_expecter_cb_ =;

// static
bool Database::IsExpectedSqliteError(int sqlite_error_code) {}

// static
void Database::SetScopedErrorExpecter(
    Database::ScopedErrorExpecterCallback* cb,
    base::PassKey<test::ScopedErrorExpecter>) {}

// static
void Database::ResetScopedErrorExpecter(
    base::PassKey<test::ScopedErrorExpecter>) {}

// static
base::FilePath Database::JournalPath(const base::FilePath& db_path) {}

// static
base::FilePath Database::WriteAheadLogPath(const base::FilePath& db_path) {}

// static
base::FilePath Database::SharedMemoryFilePath(const base::FilePath& db_path) {}

base::WeakPtr<Database> Database::GetWeakPtr(InternalApiToken) {}

Database::StatementRef::StatementRef(Database* database,
                                     sqlite3_stmt* stmt,
                                     bool was_valid)
    :{}

Database::StatementRef::~StatementRef() {}

void Database::StatementRef::Close(bool forced) {}

static_assert;

DatabaseDiagnostics::DatabaseDiagnostics() = default;
DatabaseDiagnostics::~DatabaseDiagnostics() = default;

void DatabaseDiagnostics::WriteIntoTrace(
    perfetto::TracedProto<TraceProto> context) const {}

Database::Database() :{}

Database::Database(DatabaseOptions options)
    :{}

Database::~Database() {}

// static
void Database::DisableMmapByDefault() {}

bool Database::Open(const base::FilePath& path) {}

bool Database::OpenInMemory() {}

void Database::DetachFromSequence() {}

void Database::CloseInternal(bool forced) {}

bool Database::is_open() const {}

void Database::Close() {}

void Database::Preload() {}

// SQLite keeps unused pages associated with a database in a cache.  It asks
// the cache for pages by an id, and if the page is present and the database is
// unchanged, it considers the content of the page valid and doesn't read it
// from disk.  When memory-mapped I/O is enabled, on read SQLite uses page
// structures created from the memory map data before consulting the cache.  On
// write SQLite creates a new in-memory page structure, copies the data from the
// memory map, and later writes it, releasing the updated page back to the
// cache.
//
// This means that in memory-mapped mode, the contents of the cached pages are
// not re-used for reads, but they are re-used for writes if the re-written page
// is still in the cache. The implementation of sqlite3_db_release_memory() as
// of SQLite 3.8.7.4 frees all pages from pcaches associated with the
// database, so it should free these pages.
//
// Unfortunately, the zero page is also freed.  That page is never accessed
// using memory-mapped I/O, and the cached copy can be re-used after verifying
// the file change counter on disk.  Also, fresh pages from cache receive some
// pager-level initialization before they can be used.  Since the information
// involved will immediately be accessed in various ways, it is unclear if the
// additional overhead is material, or just moving processor cache effects
// around.
//
// TODO(shess): It would be better to release the pages immediately when they
// are no longer needed.  This would basically happen after SQLite commits a
// transaction.  I had implemented a pcache wrapper to do this, but it involved
// layering violations, and it had to be setup before any other sqlite call,
// which was brittle.  Also, for large files it would actually make sense to
// maintain the existing pcache behavior for blocks past the memory-mapped
// segment.  I think drh would accept a reasonable implementation of the overall
// concept for upstreaming to SQLite core.
//
// TODO(shess): Another possibility would be to set the cache size small, which
// would keep the zero page around, plus some pre-initialized pages, and SQLite
// can manage things.  The downside is that updates larger than the cache would
// spill to the journal.  That could be compensated by setting cache_spill to
// false.  The downside then is that it allows open-ended use of memory for
// large transactions.
void Database::ReleaseCacheMemoryIfNeeded(bool implicit_change_performed) {}

base::FilePath Database::DbPath() const {}

std::string Database::CollectErrorInfo(int sqlite_error_code,
                                       Statement* stmt,
                                       DatabaseDiagnostics* diagnostics) const {}

// TODO(shess): Since this is only called in an error situation, it might be
// prudent to rewrite in terms of SQLite API calls, and mark the function const.
std::string Database::CollectCorruptionInfo() {}

bool Database::GetMmapAltStatus(int64_t* status) {}

bool Database::SetMmapAltStatus(int64_t status) {}

size_t Database::ComputeMmapSizeForOpen() {}

int Database::SqlitePrepareFlags() const {}

sqlite3_file* Database::GetSqliteVfsFile() {}

void Database::TrimMemory() {}

// Create an in-memory database with the existing database's page
// size, then backup that database over the existing database.
bool Database::Raze() {}

bool Database::RazeAndPoison() {}

void Database::Poison() {}

// TODO(shess): To the extent possible, figure out the optimal
// ordering for these deletes which will prevent other Database connections
// from seeing odd behavior.  For instance, it may be necessary to
// manually lock the main database file in a SQLite-compatible fashion
// (to prevent other processes from opening it), then delete the
// journal files, then delete the main database file.  Another option
// might be to lock the main database file and poison the header with
// junk to prevent other processes from opening it successfully (like
// Gears "SQLite poison 3" trick).
//
// static
bool Database::Delete(const base::FilePath& path) {}

bool Database::BeginTransaction(InternalApiToken) {}

void Database::RollbackTransaction(InternalApiToken) {}

bool Database::CommitTransaction(InternalApiToken) {}

bool Database::BeginTransactionDeprecated() {}

bool Database::CommitTransactionDeprecated() {}

void Database::RollbackTransactionDeprecated() {}

void Database::RollbackAllTransactions() {}

bool Database::AttachDatabase(const base::FilePath& other_db_path,
                              std::string_view attachment_point) {}

bool Database::DetachDatabase(std::string_view attachment_point) {}

// TODO(crbug.com/40779018): Change this to execute exactly one statement.
SqliteResultCode Database::ExecuteAndReturnResultCode(
    base::cstring_view initial_sql) {}

bool Database::Execute(base::cstring_view sql) {}

bool Database::ExecuteWithTimeout(base::cstring_view sql,
                                  base::TimeDelta timeout) {}

bool Database::ExecuteScriptForTesting(base::cstring_view sql_script) {}

scoped_refptr<Database::StatementRef> Database::GetCachedStatement(
    StatementID id,
    base::cstring_view sql) {}

scoped_refptr<Database::StatementRef> Database::GetUniqueStatement(
    base::cstring_view sql) {}

scoped_refptr<Database::StatementRef> Database::GetReadonlyStatement(
    base::cstring_view sql) {}

scoped_refptr<Database::StatementRef> Database::GetStatementImpl(
    base::cstring_view sql,
    bool is_readonly) {}

std::string Database::GetSchema() {}

bool Database::IsSQLValid(base::cstring_view sql) {}

bool Database::DoesIndexExist(std::string_view index_name) {}

bool Database::DoesTableExist(std::string_view table_name) {}

bool Database::DoesViewExist(std::string_view view_name) {}

bool Database::DoesSchemaItemExist(std::string_view name,
                                   std::string_view type) {}

bool Database::DoesColumnExist(base::cstring_view table_name,
                               base::cstring_view column_name) {}

int64_t Database::GetLastInsertRowId() const {}

int64_t Database::GetLastChangeCount() {}

int Database::GetMemoryUsage() {}

int Database::GetErrorCode() const {}

int Database::GetLastErrno() const {}

const char* Database::GetErrorMessage() const {}

bool Database::OpenInternal(const std::string& db_file_path) {}

void Database::ConfigureSqliteDatabaseObject() {}

void Database::DoRollback() {}

void Database::StatementRefCreated(StatementRef* ref) {}

void Database::StatementRefDeleted(StatementRef* ref) {}

void Database::OnSqliteError(SqliteErrorCode sqlite_error_code,
                             sql::Statement* statement,
                             const char* sql_statement) {}

std::string Database::GetDiagnosticInfo(int sqlite_error_code,
                                        Statement* statement,
                                        DatabaseDiagnostics* diagnostics) {}

bool Database::FullIntegrityCheck(std::vector<std::string>* messages) {}

bool Database::ReportMemoryUsage(base::trace_event::ProcessMemoryDump* pmd,
                                 const std::string& dump_name) {}

bool Database::UseWALMode() const {}

bool Database::CheckpointDatabase() {}

}  // namespace sql