// 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_