
Done entirely with `git grep` and `sed` + `git cl format`, no hand-editing. Bug: 386918226 Change-Id: Ic4bae3c955936455ce0112a7e5d4efe806f5b9b9 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6196286 Commit-Queue: Etienne Bergeron <etienneb@chromium.org> Code-Coverage: findit-for-me@appspot.gserviceaccount.com <findit-for-me@appspot.gserviceaccount.com> Reviewed-by: Etienne Bergeron <etienneb@chromium.org> Auto-Submit: Peter Kasting <pkasting@chromium.org> Commit-Queue: Peter Kasting <pkasting@chromium.org> Cr-Commit-Position: refs/heads/main@{#1411795}
469 lines
19 KiB
C++
469 lines
19 KiB
C++
// Copyright 2022 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "sql/sqlite_result_code.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstddef>
|
|
#include <ostream> // Needed to compile CHECK() with operator <<.
|
|
#include <ranges>
|
|
#include <set>
|
|
#include <string>
|
|
#include <string_view>
|
|
|
|
#include "base/check.h"
|
|
#include "base/check_op.h"
|
|
#include "base/dcheck_is_on.h"
|
|
#include "base/logging.h"
|
|
#include "base/metrics/histogram_functions.h"
|
|
#include "sql/sqlite_result_code_values.h"
|
|
#include "third_party/sqlite/sqlite3.h"
|
|
|
|
namespace sql {
|
|
|
|
namespace {
|
|
|
|
// The highly packed representation minimizes binary size and memory usage.
|
|
struct SqliteResultCodeMappingEntry {
|
|
unsigned result_code : 16;
|
|
unsigned logged_code : 8;
|
|
|
|
// The remaining bits will be used to encode the result values of helpers that
|
|
// indicate corruption handling.
|
|
};
|
|
|
|
constexpr SqliteResultCodeMappingEntry kResultCodeMapping[] = {
|
|
// Entries are ordered by SQLite result code value. This should match the
|
|
// ordering in https://www.sqlite.org/rescode.html.
|
|
|
|
{SQLITE_OK, static_cast<int>(SqliteLoggedResultCode::kNoError)},
|
|
{SQLITE_ERROR, static_cast<int>(SqliteLoggedResultCode::kGeneric)},
|
|
{SQLITE_INTERNAL, static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
|
|
{SQLITE_PERM, static_cast<int>(SqliteLoggedResultCode::kPermission)},
|
|
{SQLITE_ABORT, static_cast<int>(SqliteLoggedResultCode::kAbort)},
|
|
{SQLITE_BUSY, static_cast<int>(SqliteLoggedResultCode::kBusy)},
|
|
|
|
// Chrome features shouldn't execute conflicting statements concurrently.
|
|
{SQLITE_LOCKED, static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
// Chrome should crash on OOM.
|
|
{SQLITE_NOMEM, static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_READONLY, static_cast<int>(SqliteLoggedResultCode::kReadOnly)},
|
|
|
|
// Chrome doesn't use sqlite3_interrupt().
|
|
{SQLITE_INTERRUPT, static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_IOERR, static_cast<int>(SqliteLoggedResultCode::kIo)},
|
|
{SQLITE_CORRUPT, static_cast<int>(SqliteLoggedResultCode::kCorrupt)},
|
|
|
|
// Chrome only passes a few known-good opcodes to sqlite3_file_control().
|
|
{SQLITE_NOTFOUND, static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_FULL, static_cast<int>(SqliteLoggedResultCode::kFullDisk)},
|
|
{SQLITE_CANTOPEN, static_cast<int>(SqliteLoggedResultCode::kCantOpen)},
|
|
{SQLITE_PROTOCOL,
|
|
static_cast<int>(SqliteLoggedResultCode::kLockingProtocol)},
|
|
{SQLITE_EMPTY, static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
|
|
{SQLITE_SCHEMA, static_cast<int>(SqliteLoggedResultCode::kSchemaChanged)},
|
|
{SQLITE_TOOBIG, static_cast<int>(SqliteLoggedResultCode::kTooBig)},
|
|
{SQLITE_CONSTRAINT, static_cast<int>(SqliteLoggedResultCode::kConstraint)},
|
|
{SQLITE_MISMATCH, static_cast<int>(SqliteLoggedResultCode::kTypeMismatch)},
|
|
|
|
// Chrome should not misuse SQLite's API.
|
|
{SQLITE_MISUSE, static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_NOLFS,
|
|
static_cast<int>(SqliteLoggedResultCode::kNoLargeFileSupport)},
|
|
|
|
// Chrome does not set an authorizer callback.
|
|
{SQLITE_AUTH, static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_FORMAT, static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
|
|
|
|
// Chrome should not use invalid column indexes in sqlite3_{bind,column}*().
|
|
{SQLITE_RANGE, static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_NOTADB, static_cast<int>(SqliteLoggedResultCode::kNotADatabase)},
|
|
{SQLITE_NOTICE, static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
|
|
{SQLITE_WARNING, static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
|
|
{SQLITE_ROW, static_cast<int>(SqliteLoggedResultCode::kNoError)},
|
|
{SQLITE_DONE, static_cast<int>(SqliteLoggedResultCode::kNoError)},
|
|
{SQLITE_OK_LOAD_PERMANENTLY,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
|
|
|
|
// Chrome should not use collating sequence names in SQL statements.
|
|
{SQLITE_ERROR_MISSING_COLLSEQ,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_BUSY_RECOVERY,
|
|
static_cast<int>(SqliteLoggedResultCode::kBusyRecovery)},
|
|
|
|
// Chrome does not use a shared page cache.
|
|
{SQLITE_LOCKED_SHAREDCACHE,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_READONLY_RECOVERY,
|
|
static_cast<int>(SqliteLoggedResultCode::kReadOnlyRecovery)},
|
|
{SQLITE_IOERR_READ, static_cast<int>(SqliteLoggedResultCode::kIoRead)},
|
|
|
|
// Chrome does not use a virtual table that signals corruption. We only use
|
|
// a
|
|
// virtual table code for recovery. That code does not use this error.
|
|
{SQLITE_CORRUPT_VTAB,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_CANTOPEN_NOTEMPDIR,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
|
|
{SQLITE_CONSTRAINT_CHECK,
|
|
static_cast<int>(SqliteLoggedResultCode::kConstraintCheck)},
|
|
|
|
// Chrome does not set an authorizer callback.
|
|
{SQLITE_AUTH_USER, static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_NOTICE_RECOVER_WAL,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
|
|
{SQLITE_WARNING_AUTOINDEX,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
|
|
{SQLITE_ERROR_RETRY,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
|
|
{SQLITE_ABORT_ROLLBACK,
|
|
static_cast<int>(SqliteLoggedResultCode::kAbortRollback)},
|
|
{SQLITE_BUSY_SNAPSHOT,
|
|
static_cast<int>(SqliteLoggedResultCode::kBusySnapshot)},
|
|
|
|
// Chrome does not use a virtual table that signals conflicts. We only use a
|
|
// virtual table code for recovery. That code does not use this error.
|
|
{SQLITE_LOCKED_VTAB,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_READONLY_CANTLOCK,
|
|
static_cast<int>(SqliteLoggedResultCode::kReadOnlyCantLock)},
|
|
{SQLITE_IOERR_SHORT_READ,
|
|
static_cast<int>(SqliteLoggedResultCode::kIoShortRead)},
|
|
{SQLITE_CORRUPT_SEQUENCE,
|
|
static_cast<int>(SqliteLoggedResultCode::kCorruptSequence)},
|
|
{SQLITE_CANTOPEN_ISDIR,
|
|
static_cast<int>(SqliteLoggedResultCode::kCantOpenIsDir)},
|
|
|
|
// Chrome does not use commit hook callbacks.
|
|
{SQLITE_CONSTRAINT_COMMITHOOK,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_NOTICE_RECOVER_ROLLBACK,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
|
|
|
|
// Chrome does not use sqlite3_snapshot_open().
|
|
{SQLITE_ERROR_SNAPSHOT,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
#ifdef SQLITE_ENABLE_SNAPSHOT
|
|
#error "This code assumes that Chrome does not use sqlite3_snapshot_open()"
|
|
#endif
|
|
|
|
// Chrome does not use blocking Posix advisory file lock requests.
|
|
{SQLITE_BUSY_TIMEOUT,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
|
|
#error \
|
|
"This code assumes that Chrome does not use blocking Posix advisory \
|
|
file lock requests"
|
|
#endif
|
|
|
|
{SQLITE_READONLY_ROLLBACK,
|
|
static_cast<int>(SqliteLoggedResultCode::kReadOnlyRollback)},
|
|
{SQLITE_IOERR_WRITE, static_cast<int>(SqliteLoggedResultCode::kIoWrite)},
|
|
{SQLITE_CORRUPT_INDEX,
|
|
static_cast<int>(SqliteLoggedResultCode::kCorruptIndex)},
|
|
|
|
// Chrome should always pass full paths to SQLite.
|
|
{SQLITE_CANTOPEN_FULLPATH,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_CONSTRAINT_FOREIGNKEY,
|
|
static_cast<int>(SqliteLoggedResultCode::kConstraintForeignKey)},
|
|
{SQLITE_READONLY_DBMOVED,
|
|
static_cast<int>(SqliteLoggedResultCode::kReadOnlyDbMoved)},
|
|
{SQLITE_IOERR_FSYNC, static_cast<int>(SqliteLoggedResultCode::kIoFsync)},
|
|
|
|
// Chrome does not support Cygwin and does not use its VFS.
|
|
{SQLITE_CANTOPEN_CONVPATH,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
// Chrome does not use extension functions.
|
|
{SQLITE_CONSTRAINT_FUNCTION,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_READONLY_CANTINIT,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
|
|
{SQLITE_IOERR_DIR_FSYNC,
|
|
static_cast<int>(SqliteLoggedResultCode::kIoDirFsync)},
|
|
{SQLITE_CANTOPEN_DIRTYWAL,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
|
|
{SQLITE_CONSTRAINT_NOTNULL,
|
|
static_cast<int>(SqliteLoggedResultCode::kConstraintNotNull)},
|
|
{SQLITE_READONLY_DIRECTORY,
|
|
static_cast<int>(SqliteLoggedResultCode::kReadOnlyDirectory)},
|
|
{SQLITE_IOERR_TRUNCATE,
|
|
static_cast<int>(SqliteLoggedResultCode::kIoTruncate)},
|
|
|
|
// Chrome does not use the SQLITE_OPEN_NOFOLLOW flag.
|
|
{SQLITE_CANTOPEN_SYMLINK,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_CONSTRAINT_PRIMARYKEY,
|
|
static_cast<int>(SqliteLoggedResultCode::kConstraintPrimaryKey)},
|
|
{SQLITE_IOERR_FSTAT, static_cast<int>(SqliteLoggedResultCode::kIoFstat)},
|
|
|
|
// Chrome unconditionally disables database triggers via
|
|
// sqlite3_db_config(SQLITE_DBCONFIG_ENABLE_TRIGGER).
|
|
{SQLITE_CONSTRAINT_TRIGGER,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_IOERR_UNLOCK, static_cast<int>(SqliteLoggedResultCode::kIoUnlock)},
|
|
{SQLITE_CONSTRAINT_UNIQUE,
|
|
static_cast<int>(SqliteLoggedResultCode::kConstraintUnique)},
|
|
{SQLITE_IOERR_RDLOCK,
|
|
static_cast<int>(SqliteLoggedResultCode::kIoReadLock)},
|
|
|
|
// Chrome does not use a virtual table that signals constraints. We only use
|
|
// a virtual table code for recovery. That code does not use this error.
|
|
{SQLITE_CONSTRAINT_VTAB,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_IOERR_DELETE, static_cast<int>(SqliteLoggedResultCode::kIoDelete)},
|
|
{SQLITE_CONSTRAINT_ROWID,
|
|
static_cast<int>(SqliteLoggedResultCode::kConstraintRowId)},
|
|
{SQLITE_IOERR_BLOCKED,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
|
|
|
|
// Chrome unconditionally disables database triggers via
|
|
// sqlite3_db_config(SQLITE_DBCONFIG_ENABLE_TRIGGER).
|
|
{SQLITE_CONSTRAINT_PINNED,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
// The SQLite docus claim that this error code is "normally" converted to
|
|
// SQLITE_NOMEM. This doesn't seem 100% categorical, so we're flagging this
|
|
// as "unused in Chrome" per the same rationale as SQLITE_NOMEM.
|
|
{SQLITE_IOERR_NOMEM,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_CONSTRAINT_DATATYPE,
|
|
static_cast<int>(SqliteLoggedResultCode::kConstraintDataType)},
|
|
{SQLITE_IOERR_ACCESS, static_cast<int>(SqliteLoggedResultCode::kIoAccess)},
|
|
{SQLITE_IOERR_CHECKRESERVEDLOCK,
|
|
static_cast<int>(SqliteLoggedResultCode::kIoCheckReservedLock)},
|
|
{SQLITE_IOERR_LOCK, static_cast<int>(SqliteLoggedResultCode::kIoLock)},
|
|
{SQLITE_IOERR_CLOSE, static_cast<int>(SqliteLoggedResultCode::kIoClose)},
|
|
{SQLITE_IOERR_DIR_CLOSE,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
|
|
|
|
// Chrome will only allow enabling WAL on databases with exclusive locking.
|
|
{SQLITE_IOERR_SHMOPEN,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
// Chrome will only allow enabling WAL on databases with exclusive locking.
|
|
{SQLITE_IOERR_SHMSIZE,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_IOERR_SHMLOCK,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
|
|
|
|
// Chrome will only allow enabling WAL on databases with exclusive locking.
|
|
{SQLITE_IOERR_SHMMAP,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_IOERR_SEEK, static_cast<int>(SqliteLoggedResultCode::kIoSeek)},
|
|
{SQLITE_IOERR_DELETE_NOENT,
|
|
static_cast<int>(SqliteLoggedResultCode::kIoDeleteNoEntry)},
|
|
{SQLITE_IOERR_MMAP,
|
|
static_cast<int>(SqliteLoggedResultCode::kIoMemoryMapping)},
|
|
{SQLITE_IOERR_GETTEMPPATH,
|
|
static_cast<int>(SqliteLoggedResultCode::kIoGetTemporaryPath)},
|
|
|
|
// Chrome does not support Cygwin and does not use its VFS.
|
|
{SQLITE_IOERR_CONVPATH,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
// Chrome does not use SQLite extensions.
|
|
{SQLITE_IOERR_VNODE,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
// Chrome does not use SQLite extensions.
|
|
{SQLITE_IOERR_AUTH,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_IOERR_BEGIN_ATOMIC,
|
|
static_cast<int>(SqliteLoggedResultCode::kIoBeginAtomic)},
|
|
{SQLITE_IOERR_COMMIT_ATOMIC,
|
|
static_cast<int>(SqliteLoggedResultCode::kIoCommitAtomic)},
|
|
{SQLITE_IOERR_ROLLBACK_ATOMIC,
|
|
static_cast<int>(SqliteLoggedResultCode::kIoRollbackAtomic)},
|
|
|
|
// Chrome does not use the checksum VFS shim.
|
|
{SQLITE_IOERR_DATA,
|
|
static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
|
|
|
|
{SQLITE_IOERR_CORRUPTFS,
|
|
static_cast<int>(SqliteLoggedResultCode::kIoCorruptFileSystem)},
|
|
};
|
|
|
|
// Looks up a `sqlite_result_code` in the mapping tables.
|
|
//
|
|
// Returns an entry in kResultCodeMapping or kUnknownResultCodeMappingEntry.
|
|
// CHECKs if the `sqlite_result_code` is not in the mapping table.
|
|
SqliteResultCodeMappingEntry FindResultCode(int sqlite_result_code) {
|
|
const auto* mapping_it = std::ranges::find_if(
|
|
kResultCodeMapping,
|
|
[&sqlite_result_code](SqliteResultCodeMappingEntry rhs) {
|
|
return sqlite_result_code == rhs.result_code;
|
|
});
|
|
|
|
CHECK(mapping_it != std::ranges::end(kResultCodeMapping))
|
|
<< "Unsupported SQLite result code: " << sqlite_result_code;
|
|
return *mapping_it;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
#if DCHECK_IS_ON()
|
|
|
|
SqliteResultCode ToSqliteResultCode(int sqlite_result_code) {
|
|
SqliteLoggedResultCode logged_code = static_cast<SqliteLoggedResultCode>(
|
|
FindResultCode(sqlite_result_code).logged_code);
|
|
|
|
DCHECK_NE(logged_code, SqliteLoggedResultCode::kUnusedSqlite)
|
|
<< "SQLite reported code marked for internal use: " << sqlite_result_code;
|
|
DVLOG_IF(1, logged_code == SqliteLoggedResultCode::kUnusedChrome)
|
|
<< "SQLite reported code that should never show up in Chrome unless a "
|
|
"sql database has been corrupted: "
|
|
<< sqlite_result_code;
|
|
|
|
return static_cast<SqliteResultCode>(sqlite_result_code);
|
|
}
|
|
|
|
SqliteErrorCode ToSqliteErrorCode(SqliteResultCode sqlite_error_code) {
|
|
SqliteLoggedResultCode logged_code = static_cast<SqliteLoggedResultCode>(
|
|
FindResultCode(static_cast<int>(sqlite_error_code)).logged_code);
|
|
|
|
DCHECK_NE(logged_code, SqliteLoggedResultCode::kUnusedSqlite)
|
|
<< "SQLite reported code marked for internal use: " << sqlite_error_code;
|
|
DVLOG_IF(1, logged_code == SqliteLoggedResultCode::kUnusedChrome)
|
|
<< "SQLite reported code that should never show up in Chrome unless a "
|
|
"sql database has been corrupted: "
|
|
<< sqlite_error_code;
|
|
DCHECK_NE(logged_code, SqliteLoggedResultCode::kNoError)
|
|
<< __func__
|
|
<< " called with non-error result code: " << sqlite_error_code;
|
|
|
|
return static_cast<SqliteErrorCode>(sqlite_error_code);
|
|
}
|
|
|
|
#endif // DCHECK_IS_ON()
|
|
|
|
bool IsSqliteSuccessCode(SqliteResultCode sqlite_result_code) {
|
|
// https://www.sqlite.org/rescode.html lists the result codes that are not
|
|
// errors.
|
|
bool is_success = (sqlite_result_code == SqliteResultCode::kOk) ||
|
|
(sqlite_result_code == SqliteResultCode::kRow) ||
|
|
(sqlite_result_code == SqliteResultCode::kDone);
|
|
|
|
#if DCHECK_IS_ON()
|
|
SqliteLoggedResultCode logged_code = static_cast<SqliteLoggedResultCode>(
|
|
FindResultCode(static_cast<int>(sqlite_result_code)).logged_code);
|
|
|
|
DCHECK_EQ(is_success, logged_code == SqliteLoggedResultCode::kNoError)
|
|
<< __func__ << " logic disagrees with the code mapping for "
|
|
<< sqlite_result_code;
|
|
|
|
DCHECK_NE(logged_code, SqliteLoggedResultCode::kUnusedSqlite)
|
|
<< "SQLite reported code marked for internal use: " << sqlite_result_code;
|
|
DCHECK_NE(logged_code, SqliteLoggedResultCode::kUnusedChrome)
|
|
<< "SQLite reported code that should never show up in Chrome: "
|
|
<< sqlite_result_code;
|
|
#endif // DCHECK_IS_ON()
|
|
|
|
return is_success;
|
|
}
|
|
|
|
SqliteLoggedResultCode ToSqliteLoggedResultCode(int sqlite_result_code) {
|
|
SqliteLoggedResultCode logged_code = static_cast<SqliteLoggedResultCode>(
|
|
FindResultCode(sqlite_result_code).logged_code);
|
|
|
|
DCHECK_NE(logged_code, SqliteLoggedResultCode::kUnusedSqlite)
|
|
<< "SQLite reported code marked for internal use: " << sqlite_result_code;
|
|
DCHECK_NE(logged_code, SqliteLoggedResultCode::kUnusedChrome)
|
|
<< "SQLite reported code that should never show up in Chrome: "
|
|
<< sqlite_result_code;
|
|
return logged_code;
|
|
}
|
|
|
|
void UmaHistogramSqliteResult(const std::string& histogram_name,
|
|
int sqlite_result_code) {
|
|
auto logged_code = ToSqliteLoggedResultCode(sqlite_result_code);
|
|
base::UmaHistogramEnumeration(histogram_name, logged_code);
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os,
|
|
SqliteResultCode sqlite_result_code) {
|
|
return os << static_cast<int>(sqlite_result_code);
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os, SqliteErrorCode sqlite_error_code) {
|
|
return os << static_cast<SqliteResultCode>(sqlite_error_code);
|
|
}
|
|
|
|
void CheckSqliteLoggedResultCodeForTesting() {
|
|
// Ensure that error codes are alphabetical.
|
|
const auto* unordered_it = std::ranges::adjacent_find(
|
|
kResultCodeMapping,
|
|
[](SqliteResultCodeMappingEntry lhs, SqliteResultCodeMappingEntry rhs) {
|
|
return lhs.result_code >= rhs.result_code;
|
|
});
|
|
DCHECK_EQ(unordered_it, std::ranges::end(kResultCodeMapping))
|
|
<< "Mapping ordering broken at {" << unordered_it->result_code << ", "
|
|
<< static_cast<int>(unordered_it->logged_code) << "}";
|
|
|
|
std::set<int> sqlite_result_codes;
|
|
for (auto& mapping_entry : kResultCodeMapping) {
|
|
sqlite_result_codes.insert(mapping_entry.result_code);
|
|
}
|
|
|
|
// SQLite doesn't have special messages for extended errors.
|
|
// At the time of this writing, sqlite3_errstr() has a string table for
|
|
// primary result codes, and uses it for extended error codes as well.
|
|
//
|
|
// So, we can only use sqlite3_errstr() to check for holes in the primary
|
|
// message table.
|
|
for (int result_code = 0; result_code <= 256; ++result_code) {
|
|
if (sqlite_result_codes.count(result_code) != 0) {
|
|
continue;
|
|
}
|
|
|
|
const char* error_message = sqlite3_errstr(result_code);
|
|
|
|
static constexpr std::string_view kUnknownErrorMessage("unknown error");
|
|
DCHECK_EQ(kUnknownErrorMessage.compare(error_message), 0)
|
|
<< "Unmapped SQLite result code: " << result_code
|
|
<< " SQLite message: " << error_message;
|
|
}
|
|
|
|
// Number of #defines in https://www.sqlite.org/c3ref/c_abort.html
|
|
//
|
|
// This number is also stated at
|
|
// https://www.sqlite.org/rescode.html#primary_result_code_list
|
|
static constexpr int kPrimaryResultCodes = 31;
|
|
|
|
// Number of #defines in https://www.sqlite.org/c3ref/c_abort_rollback.html
|
|
//
|
|
// This number is also stated at
|
|
// https://www.sqlite.org/rescode.html#extended_result_code_list
|
|
static constexpr int kExtendedResultCodes = 74;
|
|
|
|
DCHECK_EQ(std::size(kResultCodeMapping),
|
|
size_t{kPrimaryResultCodes + kExtendedResultCodes})
|
|
<< "Mapping table has incorrect number of entries";
|
|
}
|
|
|
|
} // namespace sql
|