// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/common/sqlite_utils.h"
#include <list>
#include "base/file_path.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/stl_util-inl.h"
#include "base/string16.h"
#include "base/synchronization/lock.h"
// The vanilla error handler implements the common fucntionality for all the
// error handlers. Specialized error handlers are expected to only override
// the Handler() function.
class VanillaSQLErrorHandler : public SQLErrorHandler {
public:
VanillaSQLErrorHandler() : error_(SQLITE_OK) {
}
virtual int GetLastError() const {
return error_;
}
protected:
int error_;
};
class DebugSQLErrorHandler: public VanillaSQLErrorHandler {
public:
virtual int HandleError(int error, sqlite3* db) {
error_ = error;
NOTREACHED() << "sqlite error " << error
<< " db " << static_cast<void*>(db);
return error;
}
};
class ReleaseSQLErrorHandler : public VanillaSQLErrorHandler {
public:
virtual int HandleError(int error, sqlite3* db) {
error_ = error;
// Used to have a CHECK here. Got lots of crashes.
return error;
}
};
// The default error handler factory is also in charge of managing the
// lifetime of the error objects. This object is multi-thread safe.
class DefaultSQLErrorHandlerFactory : public SQLErrorHandlerFactory {
public:
~DefaultSQLErrorHandlerFactory() {
STLDeleteContainerPointers(errors_.begin(), errors_.end());
}
virtual SQLErrorHandler* Make() {
SQLErrorHandler* handler;
#ifndef NDEBUG
handler = new DebugSQLErrorHandler;
#else
handler = new ReleaseSQLErrorHandler;
#endif // NDEBUG
AddHandler(handler);
return handler;
}
private:
void AddHandler(SQLErrorHandler* handler) {
base::AutoLock lock(lock_);
errors_.push_back(handler);
}
typedef std::list<SQLErrorHandler*> ErrorList;
ErrorList errors_;
base::Lock lock_;
};
static base::LazyInstance<DefaultSQLErrorHandlerFactory>
g_default_sql_error_handler_factory(base::LINKER_INITIALIZED);
SQLErrorHandlerFactory* GetErrorHandlerFactory() {
// TODO(cpu): Testing needs to override the error handler.
// Destruction of DefaultSQLErrorHandlerFactory handled by at_exit manager.
return g_default_sql_error_handler_factory.Pointer();
}
namespace sqlite_utils {
int OpenSqliteDb(const FilePath& filepath, sqlite3** database) {
#if defined(OS_WIN)
// We want the default encoding to always be UTF-8, so we use the
// 8-bit version of open().
return sqlite3_open(WideToUTF8(filepath.value()).c_str(), database);
#elif defined(OS_POSIX)
return sqlite3_open(filepath.value().c_str(), database);
#endif
}
bool DoesSqliteTableExist(sqlite3* db,
const char* db_name,
const char* table_name) {
// sqlite doesn't allow binding parameters as table names, so we have to
// manually construct the sql
std::string sql("SELECT name FROM ");
if (db_name && db_name[0]) {
sql.append(db_name);
sql.push_back('.');
}
sql.append("sqlite_master WHERE type='table' AND name=?");
SQLStatement statement;
if (statement.prepare(db, sql.c_str()) != SQLITE_OK)
return false;
if (statement.bind_text(0, table_name) != SQLITE_OK)
return false;
// we only care about if this matched a row, not the actual data
return sqlite3_step(statement.get()) == SQLITE_ROW;
}
bool DoesSqliteColumnExist(sqlite3* db,
const char* database_name,
const char* table_name,
const char* column_name,
const char* column_type) {
SQLStatement s;
std::string sql;
sql.append("PRAGMA ");
if (database_name && database_name[0]) {
// optional database name specified
sql.append(database_name);
sql.push_back('.');
}
sql.append("TABLE_INFO(");
sql.append(table_name);
sql.append(")");
if (s.prepare(db, sql.c_str()) != SQLITE_OK)
return false;
while (s.step() == SQLITE_ROW) {
if (!s.column_string(1).compare(column_name)) {
if (column_type && column_type[0])
return !s.column_string(2).compare(column_type);
return true;
}
}
return false;
}
bool DoesSqliteTableHaveRow(sqlite3* db, const char* table_name) {
SQLStatement s;
std::string b;
b.append("SELECT * FROM ");
b.append(table_name);
if (s.prepare(db, b.c_str()) != SQLITE_OK)
return false;
return s.step() == SQLITE_ROW;
}
} // namespace sqlite_utils
SQLTransaction::SQLTransaction(sqlite3* db) : db_(db), began_(false) {
}
SQLTransaction::~SQLTransaction() {
if (began_) {
Rollback();
}
}
int SQLTransaction::BeginCommand(const char* command) {
int rv = SQLITE_ERROR;
if (!began_ && db_) {
rv = sqlite3_exec(db_, command, NULL, NULL, NULL);
began_ = (rv == SQLITE_OK);
}
return rv;
}
int SQLTransaction::EndCommand(const char* command) {
int rv = SQLITE_ERROR;
if (began_ && db_) {
rv = sqlite3_exec(db_, command, NULL, NULL, NULL);
began_ = (rv != SQLITE_OK);
}
return rv;
}
SQLNestedTransactionSite::~SQLNestedTransactionSite() {
DCHECK(!top_transaction_);
}
void SQLNestedTransactionSite::SetTopTransaction(SQLNestedTransaction* top) {
DCHECK(!top || !top_transaction_);
top_transaction_ = top;
}
SQLNestedTransaction::SQLNestedTransaction(SQLNestedTransactionSite* site)
: SQLTransaction(site->GetSqlite3DB()),
needs_rollback_(false),
site_(site) {
DCHECK(site);
if (site->GetTopTransaction() == NULL) {
site->SetTopTransaction(this);
}
}
SQLNestedTransaction::~SQLNestedTransaction() {
if (began_) {
Rollback();
}
if (site_->GetTopTransaction() == this) {
site_->SetTopTransaction(NULL);
}
}
int SQLNestedTransaction::BeginCommand(const char* command) {
DCHECK(db_);
DCHECK(site_ && site_->GetTopTransaction());
if (!db_ || began_) {
return SQLITE_ERROR;
}
if (site_->GetTopTransaction() == this) {
int rv = sqlite3_exec(db_, command, NULL, NULL, NULL);
began_ = (rv == SQLITE_OK);
if (began_) {
site_->OnBegin();
}
return rv;
} else {
if (site_->GetTopTransaction()->needs_rollback_) {
return SQLITE_ERROR;
}
began_ = true;
return SQLITE_OK;
}
}
int SQLNestedTransaction::EndCommand(const char* command) {
DCHECK(db_);
DCHECK(site_ && site_->GetTopTransaction());
if (!db_ || !began_) {
return SQLITE_ERROR;
}
if (site_->GetTopTransaction() == this) {
if (needs_rollback_) {
sqlite3_exec(db_, "ROLLBACK", NULL, NULL, NULL);
began_ = false; // reset so we don't try to rollback or call
// OnRollback() again
site_->OnRollback();
return SQLITE_ERROR;
} else {
int rv = sqlite3_exec(db_, command, NULL, NULL, NULL);
began_ = (rv != SQLITE_OK);
if (strcmp(command, "ROLLBACK") == 0) {
began_ = false; // reset so we don't try to rollbck or call
// OnRollback() again
site_->OnRollback();
} else {
DCHECK(strcmp(command, "COMMIT") == 0);
if (rv == SQLITE_OK) {
site_->OnCommit();
}
}
return rv;
}
} else {
if (strcmp(command, "ROLLBACK") == 0) {
site_->GetTopTransaction()->needs_rollback_ = true;
}
began_ = false;
return SQLITE_OK;
}
}
int SQLStatement::prepare(sqlite3* db, const char* sql, int sql_len) {
DCHECK(!stmt_);
int rv = sqlite3_prepare_v2(db, sql, sql_len, &stmt_, NULL);
if (rv != SQLITE_OK) {
SQLErrorHandler* error_handler = GetErrorHandlerFactory()->Make();
return error_handler->HandleError(rv, db);
}
return rv;
}
int SQLStatement::step() {
DCHECK(stmt_);
int status = sqlite3_step(stmt_);
if ((status == SQLITE_ROW) || (status == SQLITE_DONE))
return status;
// We got a problem.
SQLErrorHandler* error_handler = GetErrorHandlerFactory()->Make();
return error_handler->HandleError(status, db_handle());
}
int SQLStatement::reset() {
DCHECK(stmt_);
return sqlite3_reset(stmt_);
}
sqlite_int64 SQLStatement::last_insert_rowid() {
DCHECK(stmt_);
return sqlite3_last_insert_rowid(db_handle());
}
int SQLStatement::changes() {
DCHECK(stmt_);
return sqlite3_changes(db_handle());
}
sqlite3* SQLStatement::db_handle() {
DCHECK(stmt_);
return sqlite3_db_handle(stmt_);
}
int SQLStatement::bind_parameter_count() {
DCHECK(stmt_);
return sqlite3_bind_parameter_count(stmt_);
}
int SQLStatement::bind_blob(int index, std::vector<unsigned char>* blob) {
if (blob) {
const void* value = blob->empty() ? NULL : &(*blob)[0];
int len = static_cast<int>(blob->size());
return bind_blob(index, value, len);
} else {
return bind_null(index);
}
}
int SQLStatement::bind_blob(int index, const void* value, int value_len) {
return bind_blob(index, value, value_len, SQLITE_TRANSIENT);
}
int SQLStatement::bind_blob(int index, const void* value, int value_len,
Function dtor) {
DCHECK(stmt_);
return sqlite3_bind_blob(stmt_, index + 1, value, value_len, dtor);
}
int SQLStatement::bind_double(int index, double value) {
DCHECK(stmt_);
return sqlite3_bind_double(stmt_, index + 1, value);
}
int SQLStatement::bind_bool(int index, bool value) {
DCHECK(stmt_);
return sqlite3_bind_int(stmt_, index + 1, value);
}
int SQLStatement::bind_int(int index, int value) {
DCHECK(stmt_);
return sqlite3_bind_int(stmt_, index + 1, value);
}
int SQLStatement::bind_int64(int index, sqlite_int64 value) {
DCHECK(stmt_);
return sqlite3_bind_int64(stmt_, index + 1, value);
}
int SQLStatement::bind_null(int index) {
DCHECK(stmt_);
return sqlite3_bind_null(stmt_, index + 1);
}
int SQLStatement::bind_text(int index, const char* value, int value_len,
Function dtor) {
DCHECK(stmt_);
return sqlite3_bind_text(stmt_, index + 1, value, value_len, dtor);
}
int SQLStatement::bind_text16(int index, const char16* value, int value_len,
Function dtor) {
DCHECK(stmt_);
value_len *= sizeof(char16);
return sqlite3_bind_text16(stmt_, index + 1, value, value_len, dtor);
}
int SQLStatement::bind_value(int index, const sqlite3_value* value) {
DCHECK(stmt_);
return sqlite3_bind_value(stmt_, index + 1, value);
}
int SQLStatement::column_count() {
DCHECK(stmt_);
return sqlite3_column_count(stmt_);
}
int SQLStatement::column_type(int index) {
DCHECK(stmt_);
return sqlite3_column_type(stmt_, index);
}
const void* SQLStatement::column_blob(int index) {
DCHECK(stmt_);
return sqlite3_column_blob(stmt_, index);
}
bool SQLStatement::column_blob_as_vector(int index,
std::vector<unsigned char>* blob) {
DCHECK(stmt_);
const void* p = column_blob(index);
size_t len = column_bytes(index);
blob->resize(len);
if (blob->size() != len) {
return false;
}
if (len > 0)
memcpy(&(blob->front()), p, len);
return true;
}
bool SQLStatement::column_blob_as_string(int index, std::string* blob) {
DCHECK(stmt_);
const void* p = column_blob(index);
size_t len = column_bytes(index);
blob->resize(len);
if (blob->size() != len) {
return false;
}
blob->assign(reinterpret_cast<const char*>(p), len);
return true;
}
int SQLStatement::column_bytes(int index) {
DCHECK(stmt_);
return sqlite3_column_bytes(stmt_, index);
}
int SQLStatement::column_bytes16(int index) {
DCHECK(stmt_);
return sqlite3_column_bytes16(stmt_, index);
}
double SQLStatement::column_double(int index) {
DCHECK(stmt_);
return sqlite3_column_double(stmt_, index);
}
bool SQLStatement::column_bool(int index) {
DCHECK(stmt_);
return sqlite3_column_int(stmt_, index) ? true : false;
}
int SQLStatement::column_int(int index) {
DCHECK(stmt_);
return sqlite3_column_int(stmt_, index);
}
sqlite_int64 SQLStatement::column_int64(int index) {
DCHECK(stmt_);
return sqlite3_column_int64(stmt_, index);
}
const char* SQLStatement::column_text(int index) {
DCHECK(stmt_);
return reinterpret_cast<const char*>(sqlite3_column_text(stmt_, index));
}
bool SQLStatement::column_string(int index, std::string* str) {
DCHECK(stmt_);
DCHECK(str);
const char* s = column_text(index);
str->assign(s ? s : std::string());
return s != NULL;
}
std::string SQLStatement::column_string(int index) {
std::string str;
column_string(index, &str);
return str;
}
const char16* SQLStatement::column_text16(int index) {
DCHECK(stmt_);
return static_cast<const char16*>(sqlite3_column_text16(stmt_, index));
}
bool SQLStatement::column_string16(int index, string16* str) {
DCHECK(stmt_);
DCHECK(str);
const char* s = column_text(index);
str->assign(s ? UTF8ToUTF16(s) : string16());
return (s != NULL);
}
string16 SQLStatement::column_string16(int index) {
string16 str;
column_string16(index, &str);
return str;
}
bool SQLStatement::column_wstring(int index, std::wstring* str) {
DCHECK(stmt_);
DCHECK(str);
const char* s = column_text(index);
str->assign(s ? UTF8ToWide(s) : std::wstring());
return (s != NULL);
}
std::wstring SQLStatement::column_wstring(int index) {
std::wstring wstr;
column_wstring(index, &wstr);
return wstr;
}