This source file includes following definitions.
- SetTimeout
- BackupDatabase
- ValidAttachmentPoint
- InitializeSqlite
- GetSqlite3File
- ShouldIgnoreSqliteError
- SetErrorIgnorer
- ResetErrorIgnorer
- was_valid_
- Close
- poisoned_
- Open
- OpenInMemory
- OpenTemporary
- CloseInternal
- Close
- Preload
- TrimMemory
- Raze
- RazeWithTimout
- RazeAndClose
- Poison
- Delete
- BeginTransaction
- RollbackTransaction
- CommitTransaction
- RollbackAllTransactions
- AttachDatabase
- DetachDatabase
- ExecuteAndReturnErrorCode
- Execute
- ExecuteWithTimeout
- HasCachedStatement
- GetCachedStatement
- GetUniqueStatement
- GetUntrackedStatement
- GetSchema
- IsSQLValid
- DoesTableExist
- DoesIndexExist
- DoesTableOrIndexExist
- DoesColumnExist
- GetLastInsertRowId
- GetLastChangeCount
- GetErrorCode
- GetLastErrno
- GetErrorMessage
- OpenInternal
- DoRollback
- StatementRefCreated
- StatementRefDeleted
- AddTaggedHistogram
- OnSqliteError
- FullIntegrityCheck
- QuickIntegrityCheck
- IntegrityCheckHelper
#include "sql/connection.h"
#include <string.h>
#include "base/files/file_path.h"
#include "base/file_util.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/metrics/sparse_histogram.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
#include "sql/statement.h"
#include "third_party/sqlite/sqlite3.h"
#if defined(OS_IOS) && defined(USE_SYSTEM_SQLITE)
#include "third_party/sqlite/src/ext/icu/sqliteicu.h"
#endif
namespace {
const int kBusyTimeoutSeconds = 1;
class ScopedBusyTimeout {
public:
explicit ScopedBusyTimeout(sqlite3* db)
: db_(db) {
}
~ScopedBusyTimeout() {
sqlite3_busy_timeout(db_, 0);
}
int SetTimeout(base::TimeDelta timeout) {
DCHECK_LT(timeout.InMilliseconds(), INT_MAX);
return sqlite3_busy_timeout(db_,
static_cast<int>(timeout.InMilliseconds()));
}
private:
sqlite3* db_;
};
class ScopedWritableSchema {
public:
explicit ScopedWritableSchema(sqlite3* db)
: db_(db) {
sqlite3_exec(db_, "PRAGMA writable_schema=1", NULL, NULL, NULL);
}
~ScopedWritableSchema() {
sqlite3_exec(db_, "PRAGMA writable_schema=0", NULL, NULL, NULL);
}
private:
sqlite3* db_;
};
int BackupDatabase(sqlite3* src, sqlite3* dst, const char* db_name) {
DCHECK_NE(src, dst);
sqlite3_backup* backup = sqlite3_backup_init(dst, db_name, src, db_name);
if (!backup) {
DLOG(FATAL) << "Unable to start sqlite3_backup(): " << sqlite3_errmsg(dst);
return sqlite3_errcode(dst);
}
int rc = sqlite3_backup_step(backup, -1);
int pages = sqlite3_backup_pagecount(backup);
sqlite3_backup_finish(backup);
if (rc == SQLITE_DONE)
DCHECK_EQ(pages, 1);
return rc;
}
bool ValidAttachmentPoint(const char* attachment_point) {
for (size_t i = 0; attachment_point[i]; ++i) {
if (!((attachment_point[i] >= '0' && attachment_point[i] <= '9') ||
(attachment_point[i] >= 'a' && attachment_point[i] <= 'z') ||
(attachment_point[i] >= 'A' && attachment_point[i] <= 'Z') ||
attachment_point[i] == '_')) {
return false;
}
}
return true;
}
base::LazyInstance<base::Lock>::Leaky
g_sqlite_init_lock = LAZY_INSTANCE_INITIALIZER;
void InitializeSqlite() {
base::AutoLock lock(g_sqlite_init_lock.Get());
sqlite3_initialize();
}
int GetSqlite3File(sqlite3* db, sqlite3_file** file) {
*file = NULL;
int rc = sqlite3_file_control(db, NULL, SQLITE_FCNTL_FILE_POINTER, file);
if (rc != SQLITE_OK)
return rc;
if (!*file || !(*file)->pMethods)
return SQLITE_ERROR;
return rc;
}
}
namespace sql {
Connection::ErrorIgnorerCallback* Connection::current_ignorer_cb_ = NULL;
bool Connection::ShouldIgnoreSqliteError(int error) {
if (!current_ignorer_cb_)
return false;
return current_ignorer_cb_->Run(error);
}
void Connection::SetErrorIgnorer(Connection::ErrorIgnorerCallback* cb) {
CHECK(current_ignorer_cb_ == NULL);
current_ignorer_cb_ = cb;
}
void Connection::ResetErrorIgnorer() {
CHECK(current_ignorer_cb_);
current_ignorer_cb_ = NULL;
}
bool StatementID::operator<(const StatementID& other) const {
if (number_ != other.number_)
return number_ < other.number_;
return strcmp(str_, other.str_) < 0;
}
Connection::StatementRef::StatementRef(Connection* connection,
sqlite3_stmt* stmt,
bool was_valid)
: connection_(connection),
stmt_(stmt),
was_valid_(was_valid) {
if (connection)
connection_->StatementRefCreated(this);
}
Connection::StatementRef::~StatementRef() {
if (connection_)
connection_->StatementRefDeleted(this);
Close(false);
}
void Connection::StatementRef::Close(bool forced) {
if (stmt_) {
AssertIOAllowed();
sqlite3_finalize(stmt_);
stmt_ = NULL;
}
connection_ = NULL;
was_valid_ = was_valid_ && forced;
}
Connection::Connection()
: db_(NULL),
page_size_(0),
cache_size_(0),
exclusive_locking_(false),
restrict_to_user_(false),
transaction_nesting_(0),
needs_rollback_(false),
in_memory_(false),
poisoned_(false) {
}
Connection::~Connection() {
Close();
}
bool Connection::Open(const base::FilePath& path) {
if (!histogram_tag_.empty()) {
int64 size_64 = 0;
if (base::GetFileSize(path, &size_64)) {
size_t sample = static_cast<size_t>(size_64 / 1024);
std::string full_histogram_name = "Sqlite.SizeKB." + histogram_tag_;
base::HistogramBase* histogram =
base::Histogram::FactoryGet(
full_histogram_name, 1, 1000000, 50,
base::HistogramBase::kUmaTargetedHistogramFlag);
if (histogram)
histogram->Add(sample);
}
}
#if defined(OS_WIN)
return OpenInternal(base::WideToUTF8(path.value()), RETRY_ON_POISON);
#elif defined(OS_POSIX)
return OpenInternal(path.value(), RETRY_ON_POISON);
#endif
}
bool Connection::OpenInMemory() {
in_memory_ = true;
return OpenInternal(":memory:", NO_RETRY);
}
bool Connection::OpenTemporary() {
return OpenInternal("", NO_RETRY);
}
void Connection::CloseInternal(bool forced) {
statement_cache_.clear();
DCHECK(forced || open_statements_.empty());
for (StatementRefSet::iterator i = open_statements_.begin();
i != open_statements_.end(); ++i)
(*i)->Close(forced);
open_statements_.clear();
if (db_) {
AssertIOAllowed();
int rc = sqlite3_close(db_);
if (rc != SQLITE_OK) {
UMA_HISTOGRAM_SPARSE_SLOWLY("Sqlite.CloseFailure", rc);
DLOG(FATAL) << "sqlite3_close failed: " << GetErrorMessage();
}
}
db_ = NULL;
}
void Connection::Close() {
if (poisoned_) {
poisoned_ = false;
return;
}
CloseInternal(false);
}
void Connection::Preload() {
AssertIOAllowed();
if (!db_) {
DLOG_IF(FATAL, !poisoned_) << "Cannot preload null db";
return;
}
const int page_size = page_size_ ? page_size_ : 1024;
sqlite3_int64 preload_size = page_size * (cache_size_ ? cache_size_ : 2000);
if (preload_size < 1)
return;
sqlite3_file* file = NULL;
int rc = GetSqlite3File(db_, &file);
if (rc != SQLITE_OK)
return;
sqlite3_int64 file_size = 0;
rc = file->pMethods->xFileSize(file, &file_size);
if (rc != SQLITE_OK)
return;
if (preload_size > file_size)
preload_size = file_size;
scoped_ptr<char[]> buf(new char[page_size]);
for (sqlite3_int64 pos = 0; pos < file_size; pos += page_size) {
rc = file->pMethods->xRead(file, buf.get(), page_size, pos);
if (rc != SQLITE_OK)
return;
}
}
void Connection::TrimMemory(bool aggressively) {
if (!db_)
return;
int original_cache_size;
{
Statement sql_get_original(GetUniqueStatement("PRAGMA cache_size"));
if (!sql_get_original.Step()) {
DLOG(WARNING) << "Could not get cache size " << GetErrorMessage();
return;
}
original_cache_size = sql_get_original.ColumnInt(0);
}
int shrink_cache_size = aggressively ? 1 : (original_cache_size / 2);
const std::string sql_shrink =
base::StringPrintf("PRAGMA cache_size=%d", shrink_cache_size);
if (!Execute(sql_shrink.c_str()))
DLOG(WARNING) << "Could not shrink cache size: " << GetErrorMessage();
const std::string sql_restore =
base::StringPrintf("PRAGMA cache_size=%d", original_cache_size);
if (!Execute(sql_restore.c_str()))
DLOG(WARNING) << "Could not restore cache size: " << GetErrorMessage();
}
bool Connection::Raze() {
AssertIOAllowed();
if (!db_) {
DLOG_IF(FATAL, !poisoned_) << "Cannot raze null db";
return false;
}
if (transaction_nesting_ > 0) {
DLOG(FATAL) << "Cannot raze within a transaction";
return false;
}
sql::Connection null_db;
if (!null_db.OpenInMemory()) {
DLOG(FATAL) << "Unable to open in-memory database.";
return false;
}
if (page_size_) {
DCHECK(!(page_size_ & (page_size_ - 1)))
<< " page_size_ " << page_size_ << " is not a power of two.";
const int kSqliteMaxPageSize = 32768;
DCHECK_LE(page_size_, kSqliteMaxPageSize);
const std::string sql =
base::StringPrintf("PRAGMA page_size=%d", page_size_);
if (!null_db.Execute(sql.c_str()))
return false;
}
#if defined(OS_ANDROID)
if (!null_db.Execute("PRAGMA auto_vacuum = 1"))
return false;
#endif
if (!null_db.Execute("PRAGMA schema_version = 1"))
return false;
ScopedWritableSchema writable_schema(db_);
const char* kMain = "main";
int rc = BackupDatabase(null_db.db_, db_, kMain);
UMA_HISTOGRAM_SPARSE_SLOWLY("Sqlite.RazeDatabase",rc);
if (rc == SQLITE_BUSY) {
return false;
}
if (rc == SQLITE_NOTADB || rc == SQLITE_IOERR_SHORT_READ) {
sqlite3_file* file = NULL;
rc = GetSqlite3File(db_, &file);
if (rc != SQLITE_OK) {
DLOG(FATAL) << "Failure getting file handle.";
return false;
}
rc = file->pMethods->xTruncate(file, 0);
if (rc != SQLITE_OK) {
UMA_HISTOGRAM_SPARSE_SLOWLY("Sqlite.RazeDatabaseTruncate",rc);
DLOG(FATAL) << "Failed to truncate file.";
return false;
}
rc = BackupDatabase(null_db.db_, db_, kMain);
UMA_HISTOGRAM_SPARSE_SLOWLY("Sqlite.RazeDatabase2",rc);
if (rc != SQLITE_DONE) {
DLOG(FATAL) << "Failed retrying Raze().";
}
}
if (rc != SQLITE_DONE) {
DLOG(FATAL) << "Unable to copy entire null database.";
return false;
}
return true;
}
bool Connection::RazeWithTimout(base::TimeDelta timeout) {
if (!db_) {
DLOG_IF(FATAL, !poisoned_) << "Cannot raze null db";
return false;
}
ScopedBusyTimeout busy_timeout(db_);
busy_timeout.SetTimeout(timeout);
return Raze();
}
bool Connection::RazeAndClose() {
if (!db_) {
DLOG_IF(FATAL, !poisoned_) << "Cannot raze null db";
return false;
}
RollbackAllTransactions();
bool result = Raze();
CloseInternal(true);
poisoned_ = true;
return result;
}
void Connection::Poison() {
if (!db_) {
DLOG_IF(FATAL, !poisoned_) << "Cannot poison null db";
return;
}
RollbackAllTransactions();
CloseInternal(true);
poisoned_ = true;
}
bool Connection::Delete(const base::FilePath& path) {
base::ThreadRestrictions::AssertIOAllowed();
base::FilePath journal_path(path.value() + FILE_PATH_LITERAL("-journal"));
base::FilePath wal_path(path.value() + FILE_PATH_LITERAL("-wal"));
base::DeleteFile(journal_path, false);
base::DeleteFile(wal_path, false);
base::DeleteFile(path, false);
return !base::PathExists(journal_path) &&
!base::PathExists(wal_path) &&
!base::PathExists(path);
}
bool Connection::BeginTransaction() {
if (needs_rollback_) {
DCHECK_GT(transaction_nesting_, 0);
return false;
}
bool success = true;
if (!transaction_nesting_) {
needs_rollback_ = false;
Statement begin(GetCachedStatement(SQL_FROM_HERE, "BEGIN TRANSACTION"));
if (!begin.Run())
return false;
}
transaction_nesting_++;
return success;
}
void Connection::RollbackTransaction() {
if (!transaction_nesting_) {
DLOG_IF(FATAL, !poisoned_) << "Rolling back a nonexistent transaction";
return;
}
transaction_nesting_--;
if (transaction_nesting_ > 0) {
needs_rollback_ = true;
return;
}
DoRollback();
}
bool Connection::CommitTransaction() {
if (!transaction_nesting_) {
DLOG_IF(FATAL, !poisoned_) << "Rolling back a nonexistent transaction";
return false;
}
transaction_nesting_--;
if (transaction_nesting_ > 0) {
return !needs_rollback_;
}
if (needs_rollback_) {
DoRollback();
return false;
}
Statement commit(GetCachedStatement(SQL_FROM_HERE, "COMMIT"));
return commit.Run();
}
void Connection::RollbackAllTransactions() {
if (transaction_nesting_ > 0) {
transaction_nesting_ = 0;
DoRollback();
}
}
bool Connection::AttachDatabase(const base::FilePath& other_db_path,
const char* attachment_point) {
DCHECK(ValidAttachmentPoint(attachment_point));
Statement s(GetUniqueStatement("ATTACH DATABASE ? AS ?"));
#if OS_WIN
s.BindString16(0, other_db_path.value());
#else
s.BindString(0, other_db_path.value());
#endif
s.BindString(1, attachment_point);
return s.Run();
}
bool Connection::DetachDatabase(const char* attachment_point) {
DCHECK(ValidAttachmentPoint(attachment_point));
Statement s(GetUniqueStatement("DETACH DATABASE ?"));
s.BindString(0, attachment_point);
return s.Run();
}
int Connection::ExecuteAndReturnErrorCode(const char* sql) {
AssertIOAllowed();
if (!db_) {
DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db";
return SQLITE_ERROR;
}
return sqlite3_exec(db_, sql, NULL, NULL, NULL);
}
bool Connection::Execute(const char* sql) {
if (!db_) {
DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db";
return false;
}
int error = ExecuteAndReturnErrorCode(sql);
if (error != SQLITE_OK)
error = OnSqliteError(error, NULL, sql);
if (error == SQLITE_ERROR)
DLOG(FATAL) << "SQL Error in " << sql << ", " << GetErrorMessage();
return error == SQLITE_OK;
}
bool Connection::ExecuteWithTimeout(const char* sql, base::TimeDelta timeout) {
if (!db_) {
DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db";
return false;
}
ScopedBusyTimeout busy_timeout(db_);
busy_timeout.SetTimeout(timeout);
return Execute(sql);
}
bool Connection::HasCachedStatement(const StatementID& id) const {
return statement_cache_.find(id) != statement_cache_.end();
}
scoped_refptr<Connection::StatementRef> Connection::GetCachedStatement(
const StatementID& id,
const char* sql) {
CachedStatementMap::iterator i = statement_cache_.find(id);
if (i != statement_cache_.end()) {
DCHECK(i->second->is_valid());
sqlite3_reset(i->second->stmt());
return i->second;
}
scoped_refptr<StatementRef> statement = GetUniqueStatement(sql);
if (statement->is_valid())
statement_cache_[id] = statement;
return statement;
}
scoped_refptr<Connection::StatementRef> Connection::GetUniqueStatement(
const char* sql) {
AssertIOAllowed();
if (!db_)
return new StatementRef(NULL, NULL, poisoned_);
sqlite3_stmt* stmt = NULL;
int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
DLOG(FATAL) << "SQL compile error " << GetErrorMessage();
OnSqliteError(rc, NULL, sql);
return new StatementRef(NULL, NULL, false);
}
return new StatementRef(this, stmt, true);
}
scoped_refptr<Connection::StatementRef> Connection::GetUntrackedStatement(
const char* sql) const {
if (!db_)
return new StatementRef(NULL, NULL, poisoned_);
sqlite3_stmt* stmt = NULL;
int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
DLOG(FATAL) << "SQL compile error " << GetErrorMessage();
return new StatementRef(NULL, NULL, false);
}
return new StatementRef(NULL, stmt, true);
}
std::string Connection::GetSchema() const {
const char* kSql =
"SELECT type, name, tbl_name, sql "
"FROM sqlite_master ORDER BY 1, 2, 3, 4";
Statement statement(GetUntrackedStatement(kSql));
std::string schema;
while (statement.Step()) {
schema += statement.ColumnString(0);
schema += '|';
schema += statement.ColumnString(1);
schema += '|';
schema += statement.ColumnString(2);
schema += '|';
schema += statement.ColumnString(3);
schema += '\n';
}
return schema;
}
bool Connection::IsSQLValid(const char* sql) {
AssertIOAllowed();
if (!db_) {
DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db";
return false;
}
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(db_, sql, -1, &stmt, NULL) != SQLITE_OK)
return false;
sqlite3_finalize(stmt);
return true;
}
bool Connection::DoesTableExist(const char* table_name) const {
return DoesTableOrIndexExist(table_name, "table");
}
bool Connection::DoesIndexExist(const char* index_name) const {
return DoesTableOrIndexExist(index_name, "index");
}
bool Connection::DoesTableOrIndexExist(
const char* name, const char* type) const {
const char* kSql = "SELECT name FROM sqlite_master WHERE type=? AND name=?";
Statement statement(GetUntrackedStatement(kSql));
statement.BindString(0, type);
statement.BindString(1, name);
return statement.Step();
}
bool Connection::DoesColumnExist(const char* table_name,
const char* column_name) const {
std::string sql("PRAGMA TABLE_INFO(");
sql.append(table_name);
sql.append(")");
Statement statement(GetUntrackedStatement(sql.c_str()));
while (statement.Step()) {
if (!statement.ColumnString(1).compare(column_name))
return true;
}
return false;
}
int64 Connection::GetLastInsertRowId() const {
if (!db_) {
DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db";
return 0;
}
return sqlite3_last_insert_rowid(db_);
}
int Connection::GetLastChangeCount() const {
if (!db_) {
DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db";
return 0;
}
return sqlite3_changes(db_);
}
int Connection::GetErrorCode() const {
if (!db_)
return SQLITE_ERROR;
return sqlite3_errcode(db_);
}
int Connection::GetLastErrno() const {
if (!db_)
return -1;
int err = 0;
if (SQLITE_OK != sqlite3_file_control(db_, NULL, SQLITE_LAST_ERRNO, &err))
return -2;
return err;
}
const char* Connection::GetErrorMessage() const {
if (!db_)
return "sql::Connection has no connection.";
return sqlite3_errmsg(db_);
}
bool Connection::OpenInternal(const std::string& file_name,
Connection::Retry retry_flag) {
AssertIOAllowed();
if (db_) {
DLOG(FATAL) << "sql::Connection is already open.";
return false;
}
InitializeSqlite();
DLOG_IF(FATAL, poisoned_) << "sql::Connection is already open.";
poisoned_ = false;
int err = sqlite3_open(file_name.c_str(), &db_);
if (err != SQLITE_OK) {
err = sqlite3_extended_errcode(db_);
UMA_HISTOGRAM_SPARSE_SLOWLY("Sqlite.OpenFailure", err);
OnSqliteError(err, NULL, "-- sqlite3_open()");
bool was_poisoned = poisoned_;
Close();
if (was_poisoned && retry_flag == RETRY_ON_POISON)
return OpenInternal(file_name, NO_RETRY);
return false;
}
#if defined(OS_POSIX)
if (restrict_to_user_) {
DCHECK_NE(file_name, std::string(":memory"));
base::FilePath file_path(file_name);
int mode = 0;
if (base::GetPosixFilePermissions(file_path, &mode)) {
mode &= base::FILE_PERMISSION_USER_MASK;
base::SetPosixFilePermissions(file_path, mode);
base::FilePath journal_path(file_name + FILE_PATH_LITERAL("-journal"));
base::FilePath wal_path(file_name + FILE_PATH_LITERAL("-wal"));
base::SetPosixFilePermissions(journal_path, mode);
base::SetPosixFilePermissions(wal_path, mode);
}
}
#endif
sqlite3_db_config(db_, SQLITE_DBCONFIG_LOOKASIDE, NULL, 0, 0);
err = sqlite3_extended_result_codes(db_, 1);
DCHECK_EQ(err, SQLITE_OK) << "Could not enable extended result codes";
err = ExecuteAndReturnErrorCode("PRAGMA auto_vacuum");
if (err != SQLITE_OK)
UMA_HISTOGRAM_SPARSE_SLOWLY("Sqlite.OpenProbeFailure", err);
#if defined(OS_IOS) && defined(USE_SYSTEM_SQLITE)
err = sqlite3IcuInit(db_);
DCHECK_EQ(err, SQLITE_OK) << "Could not enable ICU support";
#endif
if (exclusive_locking_) {
ignore_result(Execute("PRAGMA locking_mode=EXCLUSIVE"));
}
ignore_result(Execute("PRAGMA journal_mode = PERSIST"));
ignore_result(Execute("PRAGMA journal_size_limit = 16384"));
const base::TimeDelta kBusyTimeout =
base::TimeDelta::FromSeconds(kBusyTimeoutSeconds);
if (page_size_ != 0) {
DCHECK(!(page_size_ & (page_size_ - 1)))
<< " page_size_ " << page_size_ << " is not a power of two.";
const int kSqliteMaxPageSize = 32768;
DCHECK_LE(page_size_, kSqliteMaxPageSize);
const std::string sql =
base::StringPrintf("PRAGMA page_size=%d", page_size_);
ignore_result(ExecuteWithTimeout(sql.c_str(), kBusyTimeout));
}
if (cache_size_ != 0) {
const std::string sql =
base::StringPrintf("PRAGMA cache_size=%d", cache_size_);
ignore_result(ExecuteWithTimeout(sql.c_str(), kBusyTimeout));
}
if (!ExecuteWithTimeout("PRAGMA secure_delete=ON", kBusyTimeout)) {
bool was_poisoned = poisoned_;
Close();
if (was_poisoned && retry_flag == RETRY_ON_POISON)
return OpenInternal(file_name, NO_RETRY);
return false;
}
return true;
}
void Connection::DoRollback() {
Statement rollback(GetCachedStatement(SQL_FROM_HERE, "ROLLBACK"));
rollback.Run();
needs_rollback_ = false;
}
void Connection::StatementRefCreated(StatementRef* ref) {
DCHECK(open_statements_.find(ref) == open_statements_.end());
open_statements_.insert(ref);
}
void Connection::StatementRefDeleted(StatementRef* ref) {
StatementRefSet::iterator i = open_statements_.find(ref);
if (i == open_statements_.end())
DLOG(FATAL) << "Could not find statement";
else
open_statements_.erase(i);
}
void Connection::AddTaggedHistogram(const std::string& name,
size_t sample) const {
if (histogram_tag_.empty())
return;
std::string full_histogram_name = name + "." + histogram_tag_;
base::HistogramBase* histogram =
base::SparseHistogram::FactoryGet(
full_histogram_name,
base::HistogramBase::kUmaTargetedHistogramFlag);
if (histogram)
histogram->Add(sample);
}
int Connection::OnSqliteError(int err, sql::Statement *stmt, const char* sql) {
UMA_HISTOGRAM_SPARSE_SLOWLY("Sqlite.Error", err);
AddTaggedHistogram("Sqlite.Error", err);
if (!sql && stmt)
sql = stmt->GetSQLStatement();
if (!sql)
sql = "-- unknown";
LOG(ERROR) << histogram_tag_ << " sqlite error " << err
<< ", errno " << GetLastErrno()
<< ": " << GetErrorMessage()
<< ", sql: " << sql;
if (!error_callback_.is_null()) {
ErrorCallback(error_callback_).Run(err, stmt);
return err;
}
if (!ShouldIgnoreSqliteError(err))
DLOG(FATAL) << GetErrorMessage();
return err;
}
bool Connection::FullIntegrityCheck(std::vector<std::string>* messages) {
return IntegrityCheckHelper("PRAGMA integrity_check", messages);
}
bool Connection::QuickIntegrityCheck() {
std::vector<std::string> messages;
if (!IntegrityCheckHelper("PRAGMA quick_check", &messages))
return false;
return messages.size() == 1 && messages[0] == "ok";
}
bool Connection::IntegrityCheckHelper(
const char* pragma_sql,
std::vector<std::string>* messages) {
messages->clear();
const char kWritableSchema[] = "PRAGMA writable_schema = ON";
if (!Execute(kWritableSchema))
return false;
bool ret = false;
{
sql::Statement stmt(GetUniqueStatement(pragma_sql));
while (stmt.Step()) {
std::string result(stmt.ColumnString(0));
base::SplitString(result, '\n', messages);
}
ret = stmt.Succeeded();
}
const char kNoWritableSchema[] = "PRAGMA writable_schema = OFF";
ignore_result(Execute(kNoWritableSchema));
return ret;
}
}