// Copyright (c) 2011 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.
#ifndef CHROME_COMMON_SQLITE_UTILS_H_
#define CHROME_COMMON_SQLITE_UTILS_H_
#pragma once
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/string16.h"
#include "base/utf_string_conversions.h"
#include "third_party/sqlite/sqlite3.h"
// forward declarations of classes defined here
class FilePath;
class SQLTransaction;
class SQLNestedTransaction;
class SQLNestedTransactionSite;
class scoped_sqlite3_stmt_ptr;
class SQLStatement;
//------------------------------------------------------------------------------
// Interface to be implemented by objects that can handle exceptional sqlite
// conditions. This way client code can focus on handling normal condtions.
//------------------------------------------------------------------------------
class SQLErrorHandler {
public:
virtual ~SQLErrorHandler() {}
// Handle a sqlite error. |error| is the return code an of sqlite operation
// which is considered an error. This handler is free to try repair, notify
// someone or even break into the debugger depending on the situation.
virtual int HandleError(int error, sqlite3* db) = 0;
// Returns the last value of |error| passed to HandleError.
virtual int GetLastError() const = 0;
};
//------------------------------------------------------------------------------
// The factory interface is used to create the different error handling
// strategies for debug, release and for diagnostic mode.
//------------------------------------------------------------------------------
class SQLErrorHandlerFactory {
public:
virtual ~SQLErrorHandlerFactory() {}
virtual SQLErrorHandler* Make() = 0;
};
//------------------------------------------------------------------------------
// A wrapper for sqlite transactions that rollsback when the wrapper
// goes out of scope if the caller has not already called Commit or Rollback.
// Note: the constructor does NOT Begin a transaction.
//------------------------------------------------------------------------------
class SQLTransaction {
public:
explicit SQLTransaction(sqlite3* db);
virtual ~SQLTransaction();
int Begin() {
// By default, we BEGIN IMMEDIATE to establish file locks at the
// onset of a transaction. This avoids SQLITE_BUSY errors, without
// waiting for the busy timeout period, which can occur when BEGIN
// DEFERRED is used.
return BeginImmediate();
}
int BeginExclusive() {
return BeginCommand("BEGIN EXCLUSIVE");
}
int BeginImmediate() {
return BeginCommand("BEGIN IMMEDIATE");
}
int BeginDeferred() {
return BeginCommand("BEGIN DEFERRED");
}
int Commit() {
return EndCommand("COMMIT");
}
int Rollback() {
return EndCommand("ROLLBACK");
}
bool HasBegun() {
return began_;
}
protected:
virtual int BeginCommand(const char* command);
virtual int EndCommand(const char* command);
sqlite3* db_;
bool began_;
DISALLOW_COPY_AND_ASSIGN(SQLTransaction);
};
//------------------------------------------------------------------------------
// A class for use with SQLNestedTransaction.
//------------------------------------------------------------------------------
class SQLNestedTransactionSite {
protected:
SQLNestedTransactionSite() : db_(NULL), top_transaction_(NULL) {}
virtual ~SQLNestedTransactionSite();
// The following virtual methods provide notification of true transaction
// boundaries as they are crossed by a top nested transaction.
// Intended to be overriden (See WebCacheDB)
// SQLNestedTransaction calls these after the underlying database
// operation has been performed.
virtual void OnBegin() {}
virtual void OnCommit() {}
virtual void OnRollback() {}
// Returns the sqlite3 database connection associated with this site
// Used by SQLNestedTransaction
sqlite3* GetSqlite3DB() { return db_; }
// Returns the current top nested transaction associated with this site
// Used by SQLNestedTransaction
SQLNestedTransaction* GetTopTransaction() {
return top_transaction_;
}
// Sets or clears the top nested transaction associated with this site
// Used by SQLNestedTransaction
void SetTopTransaction(SQLNestedTransaction* top);
sqlite3* db_;
SQLNestedTransaction* top_transaction_;
friend class SQLNestedTransaction;
};
//------------------------------------------------------------------------------
// SQLite does not support nested transactions. This class provides a gross
// approximation of nested transactions.
//
// Really there is only one transaction, the top transaction.
//
// A nested transaction commits with respect to the top transaction.
// That is, even though the nested transaction commits, the permanence of its
// effects depends on the top transaction committing. If the top
// transaction rollsback, the results of the nested transaction are backed out.
// If any nested transaction aborts, the top transaction ultimately rollsback
// as well.
//
// Note: If a nested transaction is open for a particular db connection, an
// attempt to open a non-nested transaction (class SQLTransaction) will fail.
// And vice versa.
//
// TODO(michaeln): demonstrate usage here
// TODO(michaeln): safegaurds to prevent mis-use
//------------------------------------------------------------------------------
class SQLNestedTransaction : public SQLTransaction {
public:
explicit SQLNestedTransaction(SQLNestedTransactionSite* site);
virtual ~SQLNestedTransaction();
protected:
virtual int BeginCommand(const char* command);
virtual int EndCommand(const char* command);
private:
bool needs_rollback_;
SQLNestedTransactionSite* site_;
DISALLOW_COPY_AND_ASSIGN(SQLNestedTransaction);
};
//------------------------------------------------------------------------------
// A scoped sqlite statement that finalizes when it goes out of scope.
//------------------------------------------------------------------------------
class scoped_sqlite3_stmt_ptr {
public:
~scoped_sqlite3_stmt_ptr() {
finalize();
}
scoped_sqlite3_stmt_ptr() : stmt_(NULL) {
}
explicit scoped_sqlite3_stmt_ptr(sqlite3_stmt* stmt)
: stmt_(stmt) {
}
sqlite3_stmt* get() const {
return stmt_;
}
void set(sqlite3_stmt* stmt) {
finalize();
stmt_ = stmt;
}
sqlite3_stmt* release() {
sqlite3_stmt* tmp = stmt_;
stmt_ = NULL;
return tmp;
}
// It is not safe to call sqlite3_finalize twice on the same stmt.
// Sqlite3's sqlite3_finalize() function should not be called directly
// without calling the release method. If sqlite3_finalize() must be
// called directly, the following usage is advised:
// scoped_sqlite3_stmt_ptr stmt;
// ... do something with stmt ...
// sqlite3_finalize(stmt.release());
int finalize() {
int err = sqlite3_finalize(stmt_);
stmt_ = NULL;
return err;
}
protected:
sqlite3_stmt* stmt_;
private:
DISALLOW_COPY_AND_ASSIGN(scoped_sqlite3_stmt_ptr);
};
//------------------------------------------------------------------------------
// A scoped sqlite statement with convenient C++ wrappers for sqlite3 APIs.
//------------------------------------------------------------------------------
class SQLStatement : public scoped_sqlite3_stmt_ptr {
public:
SQLStatement() {}
int prepare(sqlite3* db, const char* sql) {
return prepare(db, sql, -1);
}
int prepare(sqlite3* db, const char* sql, int sql_len);
int step();
int reset();
sqlite_int64 last_insert_rowid();
int changes();
sqlite3* db_handle();
//
// Parameter binding helpers (NOTE: index is 0-based)
//
int bind_parameter_count();
typedef void (*Function)(void*);
int bind_blob(int index, std::vector<unsigned char>* blob);
int bind_blob(int index, const void* value, int value_len);
int bind_blob(int index, const void* value, int value_len, Function dtor);
int bind_double(int index, double value);
int bind_bool(int index, bool value);
int bind_int(int index, int value);
int bind_int64(int index, sqlite_int64 value);
int bind_null(int index);
int bind_string(int index, const std::string& value) {
// don't use c_str so it doesn't have to fix up the null terminator
// (sqlite just uses the length)
return bind_text(index, value.data(),
static_cast<int>(value.length()), SQLITE_TRANSIENT);
}
int bind_string16(int index, const string16& value) {
// don't use c_str so it doesn't have to fix up the null terminator
// (sqlite just uses the length)
std::string value_utf8(UTF16ToUTF8(value));
return bind_text(index, value_utf8.data(),
static_cast<int>(value_utf8.length()), SQLITE_TRANSIENT);
}
int bind_wstring(int index, const std::wstring& value) {
// don't use c_str so it doesn't have to fix up the null terminator
// (sqlite just uses the length)
std::string value_utf8(WideToUTF8(value));
return bind_text(index, value_utf8.data(),
static_cast<int>(value_utf8.length()), SQLITE_TRANSIENT);
}
int bind_text(int index, const char* value) {
return bind_text(index, value, -1, SQLITE_TRANSIENT);
}
// value_len is number of characters or may be negative
// a for null-terminated value string
int bind_text(int index, const char* value, int value_len) {
return bind_text(index, value, value_len, SQLITE_TRANSIENT);
}
// value_len is number of characters or may be negative
// a for null-terminated value string
int bind_text(int index, const char* value, int value_len,
Function dtor);
int bind_text16(int index, const char16* value) {
return bind_text16(index, value, -1, SQLITE_TRANSIENT);
}
// value_len is number of characters or may be negative
// a for null-terminated value string
int bind_text16(int index, const char16* value, int value_len) {
return bind_text16(index, value, value_len, SQLITE_TRANSIENT);
}
// value_len is number of characters or may be negative
// a for null-terminated value string
int bind_text16(int index, const char16* value, int value_len,
Function dtor);
int bind_value(int index, const sqlite3_value* value);
//
// Column helpers (NOTE: index is 0-based)
//
int column_count();
int column_type(int index);
const void* column_blob(int index);
bool column_blob_as_vector(int index, std::vector<unsigned char>* blob);
bool column_blob_as_string(int index, std::string* blob);
int column_bytes(int index);
int column_bytes16(int index);
double column_double(int index);
bool column_bool(int index);
int column_int(int index);
sqlite_int64 column_int64(int index);
const char* column_text(int index);
bool column_string(int index, std::string* str);
std::string column_string(int index);
const char16* column_text16(int index);
bool column_string16(int index, string16* str);
string16 column_string16(int index);
bool column_wstring(int index, std::wstring* str);
std::wstring column_wstring(int index);
private:
DISALLOW_COPY_AND_ASSIGN(SQLStatement);
};
namespace sqlite_utils {
//------------------------------------------------------------------------------
// A scoped sqlite database that closes when it goes out of scope.
//------------------------------------------------------------------------------
class DBClose {
public:
inline void operator()(sqlite3* x) const {
sqlite3_close(x);
}
};
typedef scoped_ptr_malloc<sqlite3, DBClose> scoped_sqlite_db_ptr;
// Opens the DB in the file pointed to by |filepath|. This method forces the
// database to be in UTF-8 mode on all platforms. See
// http://www.sqlite.org/capi3ref.html#sqlite3_open for an explanation of the
// return value.
int OpenSqliteDb(const FilePath& filepath, sqlite3** database);
// Returns true if there is a table with the given name in the database.
// For the version where a database name is specified, it may be NULL or the
// empty string if no database name is necessary.
bool DoesSqliteTableExist(sqlite3* db,
const char* db_name,
const char* table_name);
inline bool DoesSqliteTableExist(sqlite3* db, const char* table_name) {
return DoesSqliteTableExist(db, NULL, table_name);
}
// Test whether a table has a column matching the provided name and type.
// Returns true if the column exist and false otherwise. There are two
// versions, one that takes a database name, the other that doesn't. The
// database name can be NULL or empty if no name is desired.
//
// Column type is optional, it can be NULL or empty. If specified, we the
// function will check that the column is of the correct type (case-sensetive).
bool DoesSqliteColumnExist(sqlite3* db,
const char* datbase_name,
const char* table_name,
const char* column_name,
const char* column_type);
inline bool DoesSqliteColumnExist(sqlite3* db,
const char* table_name,
const char* column_name,
const char* column_type) {
return DoesSqliteColumnExist(db, NULL, table_name, column_name, column_type);
}
// Test whether a table has one or more rows. Returns true if the table
// has one or more rows and false if the table is empty or doesn't exist.
bool DoesSqliteTableHaveRow(sqlite3* db, const char* table_name);
} // namespace sqlite_utils
#endif // CHROME_COMMON_SQLITE_UTILS_H_