diff options
author | tfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-19 18:40:21 +0000 |
---|---|---|
committer | tfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-19 18:40:21 +0000 |
commit | f0a54b2f3f5afcf13f3ff981837e290c44e66680 (patch) | |
tree | 2c19b6aa7217deeb55bfeb6212ff11fbc0ddf813 /app | |
parent | 737402392b58715c71ed52b210ee62124f06b18c (diff) | |
download | chromium_src-f0a54b2f3f5afcf13f3ff981837e290c44e66680.zip chromium_src-f0a54b2f3f5afcf13f3ff981837e290c44e66680.tar.gz chromium_src-f0a54b2f3f5afcf13f3ff981837e290c44e66680.tar.bz2 |
Move app/sql/* files to sql/ directory.
I can't remove app/app.gyp and app/app_base.gypi yet because they are referenced
by third_party gyp files :(
BUG=72317
TEST=None
R=rsesek@chromium.org
move app/sql to sql
Review URL: http://codereview.chromium.org/7353026
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@93069 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'app')
-rw-r--r-- | app/DEPS | 11 | ||||
-rwxr-xr-x | app/PRESUBMIT.py | 28 | ||||
-rw-r--r-- | app/app.gyp | 10 | ||||
-rw-r--r-- | app/app_base.gypi | 20 | ||||
-rw-r--r-- | app/run_all_unittests.cc | 9 | ||||
-rw-r--r-- | app/sql/DEPS | 3 | ||||
-rw-r--r-- | app/sql/connection.cc | 420 | ||||
-rw-r--r-- | app/sql/connection.h | 387 | ||||
-rw-r--r-- | app/sql/connection_unittest.cc | 106 | ||||
-rw-r--r-- | app/sql/diagnostic_error_delegate.h | 50 | ||||
-rw-r--r-- | app/sql/init_status.h | 24 | ||||
-rw-r--r-- | app/sql/meta_table.cc | 152 | ||||
-rw-r--r-- | app/sql/meta_table.h | 83 | ||||
-rw-r--r-- | app/sql/sqlite_features_unittest.cc | 100 | ||||
-rw-r--r-- | app/sql/statement.cc | 270 | ||||
-rw-r--r-- | app/sql/statement.h | 167 | ||||
-rw-r--r-- | app/sql/statement_unittest.cc | 126 | ||||
-rw-r--r-- | app/sql/transaction.cc | 51 | ||||
-rw-r--r-- | app/sql/transaction.h | 57 | ||||
-rw-r--r-- | app/sql/transaction_unittest.cc | 133 |
20 files changed, 15 insertions, 2192 deletions
diff --git a/app/DEPS b/app/DEPS deleted file mode 100644 index bc667cc..0000000 --- a/app/DEPS +++ /dev/null @@ -1,11 +0,0 @@ -include_rules = [ - "+grit/app_locale_settings.h", - "+grit/app_strings.h", - "+net", - "+skia", - "+third_party/mozilla", - "+ui", - - "-chrome", - "-third_party/WebKit", -] diff --git a/app/PRESUBMIT.py b/app/PRESUBMIT.py deleted file mode 100755 index ed0cbd1..0000000 --- a/app/PRESUBMIT.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/python - -# Copyright (c) 2009 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. - -"""Makes sure that the app/ code is cpplint clean.""" - -INCLUDE_CPP_FILES_ONLY = ( - r'.*\.cc$', r'.*\.h$' -) - -EXCLUDE = ( - # Autogenerated window resources files are off limits - r'.*resource.h$', -) - -def CheckChangeOnUpload(input_api, output_api): - results = [] - black_list = input_api.DEFAULT_BLACK_LIST + EXCLUDE - sources = lambda x: input_api.FilterSourceFile( - x, white_list=INCLUDE_CPP_FILES_ONLY, black_list=black_list) - results.extend(input_api.canned_checks.CheckChangeLintsClean( - input_api, output_api, sources)) - return results - -def GetPreferredTrySlaves(): - return ['win', 'linux', 'linux_view', 'linux_chromeos', 'mac'] diff --git a/app/app.gyp b/app/app.gyp index 0f37bd7..514c28c 100644 --- a/app/app.gyp +++ b/app/app.gyp @@ -19,11 +19,11 @@ '../testing/gtest.gyp:gtest', ], 'sources': [ - 'run_all_unittests.cc', - 'sql/connection_unittest.cc', - 'sql/sqlite_features_unittest.cc', - 'sql/statement_unittest.cc', - 'sql/transaction_unittest.cc', + '../sql/run_all_unittests.cc', + '../sql/connection_unittest.cc', + '../sql/sqlite_features_unittest.cc', + '../sql/statement_unittest.cc', + '../sql/transaction_unittest.cc', ], 'include_dirs': [ '..', diff --git a/app/app_base.gypi b/app/app_base.gypi index 7dfe477..18b5efe 100644 --- a/app/app_base.gypi +++ b/app/app_base.gypi @@ -15,16 +15,16 @@ '../third_party/sqlite/sqlite.gyp:sqlite', ], 'sources': [ - 'sql/connection.cc', - 'sql/connection.h', - 'sql/diagnostic_error_delegate.h', - 'sql/init_status.h', - 'sql/meta_table.cc', - 'sql/meta_table.h', - 'sql/statement.cc', - 'sql/statement.h', - 'sql/transaction.cc', - 'sql/transaction.h', + '../sql/connection.cc', + '../sql/connection.h', + '../sql/diagnostic_error_delegate.h', + '../sql/init_status.h', + '../sql/meta_table.cc', + '../sql/meta_table.h', + '../sql/statement.cc', + '../sql/statement.h', + '../sql/transaction.cc', + '../sql/transaction.h', ], }, ], diff --git a/app/run_all_unittests.cc b/app/run_all_unittests.cc deleted file mode 100644 index 7fd6ef2..0000000 --- a/app/run_all_unittests.cc +++ /dev/null @@ -1,9 +0,0 @@ -// 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. - -#include "base/test/test_suite.h" - -int main(int argc, char** argv) { - return base::TestSuite(argc, argv).Run(); -} diff --git a/app/sql/DEPS b/app/sql/DEPS deleted file mode 100644 index 0f76c3a..0000000 --- a/app/sql/DEPS +++ /dev/null @@ -1,3 +0,0 @@ -include_rules = [ - "+third_party/sqlite", -] diff --git a/app/sql/connection.cc b/app/sql/connection.cc deleted file mode 100644 index 09eea45..0000000 --- a/app/sql/connection.cc +++ /dev/null @@ -1,420 +0,0 @@ -// 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. - -#include "app/sql/connection.h" - -#include <string.h> - -#include "app/sql/statement.h" -#include "base/file_path.h" -#include "base/logging.h" -#include "base/stringprintf.h" -#include "base/string_util.h" -#include "base/utf_string_conversions.h" -#include "third_party/sqlite/sqlite3.h" - -namespace { - -// Spin for up to a second waiting for the lock to clear when setting -// up the database. -// TODO(shess): Better story on this. http://crbug.com/56559 -const base::TimeDelta kBusyTimeout = base::TimeDelta::FromSeconds(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_; -}; - -} // namespace - -namespace sql { - -bool StatementID::operator<(const StatementID& other) const { - if (number_ != other.number_) - return number_ < other.number_; - return strcmp(str_, other.str_) < 0; -} - -ErrorDelegate::ErrorDelegate() { -} - -ErrorDelegate::~ErrorDelegate() { -} - -Connection::StatementRef::StatementRef() - : connection_(NULL), - stmt_(NULL) { -} - -Connection::StatementRef::StatementRef(Connection* connection, - sqlite3_stmt* stmt) - : connection_(connection), - stmt_(stmt) { - connection_->StatementRefCreated(this); -} - -Connection::StatementRef::~StatementRef() { - if (connection_) - connection_->StatementRefDeleted(this); - Close(); -} - -void Connection::StatementRef::Close() { - if (stmt_) { - sqlite3_finalize(stmt_); - stmt_ = NULL; - } - connection_ = NULL; // The connection may be getting deleted. -} - -Connection::Connection() - : db_(NULL), - page_size_(0), - cache_size_(0), - exclusive_locking_(false), - transaction_nesting_(0), - needs_rollback_(false) { -} - -Connection::~Connection() { - Close(); -} - -bool Connection::Open(const FilePath& path) { -#if defined(OS_WIN) - return OpenInternal(WideToUTF8(path.value())); -#elif defined(OS_POSIX) - return OpenInternal(path.value()); -#endif -} - -bool Connection::OpenInMemory() { - return OpenInternal(":memory:"); -} - -void Connection::Close() { - statement_cache_.clear(); - DCHECK(open_statements_.empty()); - if (db_) { - sqlite3_close(db_); - db_ = NULL; - } -} - -void Connection::Preload() { - if (!db_) { - NOTREACHED(); - return; - } - - // A statement must be open for the preload command to work. If the meta - // table doesn't exist, it probably means this is a new database and there - // is nothing to preload (so it's OK we do nothing). - if (!DoesTableExist("meta")) - return; - Statement dummy(GetUniqueStatement("SELECT * FROM meta")); - if (!dummy || !dummy.Step()) - return; - -#if !defined(USE_SYSTEM_SQLITE) - // This function is only defined in Chromium's version of sqlite. - // Do not call it when using system sqlite. - sqlite3_preload(db_); -#endif -} - -bool Connection::BeginTransaction() { - if (needs_rollback_) { - DCHECK_GT(transaction_nesting_, 0); - - // When we're going to rollback, fail on this begin and don't actually - // mark us as entering the nested transaction. - return false; - } - - bool success = true; - if (!transaction_nesting_) { - needs_rollback_ = false; - - Statement begin(GetCachedStatement(SQL_FROM_HERE, "BEGIN TRANSACTION")); - if (!begin || !begin.Run()) - return false; - } - transaction_nesting_++; - return success; -} - -void Connection::RollbackTransaction() { - if (!transaction_nesting_) { - NOTREACHED() << "Rolling back a nonexistent transaction"; - return; - } - - transaction_nesting_--; - - if (transaction_nesting_ > 0) { - // Mark the outermost transaction as needing rollback. - needs_rollback_ = true; - return; - } - - DoRollback(); -} - -bool Connection::CommitTransaction() { - if (!transaction_nesting_) { - NOTREACHED() << "Rolling back a nonexistent transaction"; - return false; - } - transaction_nesting_--; - - if (transaction_nesting_ > 0) { - // Mark any nested transactions as failing after we've already got one. - return !needs_rollback_; - } - - if (needs_rollback_) { - DoRollback(); - return false; - } - - Statement commit(GetCachedStatement(SQL_FROM_HERE, "COMMIT")); - if (!commit) - return false; - return commit.Run(); -} - -bool Connection::Execute(const char* sql) { - if (!db_) - return false; - return sqlite3_exec(db_, sql, NULL, NULL, NULL) == SQLITE_OK; -} - -bool Connection::ExecuteWithTimeout(const char* sql, base::TimeDelta timeout) { - if (!db_) - return false; - - ScopedBusyTimeout busy_timeout(db_); - busy_timeout.SetTimeout(timeout); - return sqlite3_exec(db_, sql, NULL, NULL, NULL) == SQLITE_OK; -} - -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()) { - // Statement is in the cache. It should still be active (we're the only - // one invalidating cached statements, and we'll remove it from the cache - // if we do that. Make sure we reset it before giving out the cached one in - // case it still has some stuff bound. - 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; // Only cache valid statements. - return statement; -} - -scoped_refptr<Connection::StatementRef> Connection::GetUniqueStatement( - const char* sql) { - if (!db_) - return new StatementRef(this, NULL); // Return inactive statement. - - sqlite3_stmt* stmt = NULL; - if (sqlite3_prepare_v2(db_, sql, -1, &stmt, NULL) != SQLITE_OK) { - // Treat this as non-fatal, it can occur in a number of valid cases, and - // callers should be doing their own error handling. - DLOG(WARNING) << "SQL compile error " << GetErrorMessage(); - return new StatementRef(this, NULL); - } - return new StatementRef(this, stmt); -} - -bool Connection::DoesTableExist(const char* table_name) const { - // GetUniqueStatement can't be const since statements may modify the - // database, but we know ours doesn't modify it, so the cast is safe. - Statement statement(const_cast<Connection*>(this)->GetUniqueStatement( - "SELECT name FROM sqlite_master " - "WHERE type='table' AND name=?")); - if (!statement) - return false; - statement.BindString(0, table_name); - return statement.Step(); // Table exists if any row was returned. -} - -bool Connection::DoesColumnExist(const char* table_name, - const char* column_name) const { - std::string sql("PRAGMA TABLE_INFO("); - sql.append(table_name); - sql.append(")"); - - // Our SQL is non-mutating, so this cast is OK. - Statement statement(const_cast<Connection*>(this)->GetUniqueStatement( - sql.c_str())); - if (!statement) - return false; - - while (statement.Step()) { - if (!statement.ColumnString(1).compare(column_name)) - return true; - } - return false; -} - -int64 Connection::GetLastInsertRowId() const { - if (!db_) { - NOTREACHED(); - return 0; - } - return sqlite3_last_insert_rowid(db_); -} - -int Connection::GetLastChangeCount() const { - if (!db_) { - NOTREACHED(); - 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) { - if (db_) { - NOTREACHED() << "sql::Connection is already open."; - return false; - } - - int err = sqlite3_open(file_name.c_str(), &db_); - if (err != SQLITE_OK) { - OnSqliteError(err, NULL); - db_ = NULL; - return false; - } - - // Enable extended result codes to provide more color on I/O errors. - // Not having extended result codes is not a fatal problem, as - // Chromium code does not attempt to handle I/O errors anyhow. The - // current implementation always returns SQLITE_OK, the DCHECK is to - // quickly notify someone if SQLite changes. - err = sqlite3_extended_result_codes(db_, 1); - DCHECK_EQ(err, SQLITE_OK) << "Could not enable extended result codes"; - - // If indicated, lock up the database before doing anything else, so - // that the following code doesn't have to deal with locking. - // TODO(shess): This code is brittle. Find the cases where code - // doesn't request |exclusive_locking_| and audit that it does the - // right thing with SQLITE_BUSY, and that it doesn't make - // assumptions about who might change things in the database. - // http://crbug.com/56559 - if (exclusive_locking_) { - // TODO(shess): This should probably be a full CHECK(). Code - // which requests exclusive locking but doesn't get it is almost - // certain to be ill-tested. - if (!Execute("PRAGMA locking_mode=EXCLUSIVE")) - NOTREACHED() << "Could not set locking mode: " << GetErrorMessage(); - } - - if (page_size_ != 0) { - // Enforce SQLite restrictions on |page_size_|. - DCHECK(!(page_size_ & (page_size_ - 1))) - << " page_size_ " << page_size_ << " is not a power of two."; - static const int kSqliteMaxPageSize = 32768; // from sqliteLimit.h - DCHECK_LE(page_size_, kSqliteMaxPageSize); - const std::string sql = StringPrintf("PRAGMA page_size=%d", page_size_); - if (!ExecuteWithTimeout(sql.c_str(), kBusyTimeout)) - NOTREACHED() << "Could not set page size: " << GetErrorMessage(); - } - - if (cache_size_ != 0) { - const std::string sql = StringPrintf("PRAGMA cache_size=%d", cache_size_); - if (!ExecuteWithTimeout(sql.c_str(), kBusyTimeout)) - NOTREACHED() << "Could not set cache size: " << GetErrorMessage(); - } - - return true; -} - -void Connection::DoRollback() { - Statement rollback(GetCachedStatement(SQL_FROM_HERE, "ROLLBACK")); - if (rollback) - rollback.Run(); -} - -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()) - NOTREACHED(); - else - open_statements_.erase(i); -} - -void Connection::ClearCache() { - statement_cache_.clear(); - - // The cache clear will get most statements. There may be still be references - // to some statements that are held by others (including one-shot statements). - // This will deactivate them so they can't be used again. - for (StatementRefSet::iterator i = open_statements_.begin(); - i != open_statements_.end(); ++i) - (*i)->Close(); -} - -int Connection::OnSqliteError(int err, sql::Statement *stmt) { - if (error_delegate_.get()) - return error_delegate_->OnError(err, this, stmt); - // The default handling is to assert on debug and to ignore on release. - NOTREACHED() << GetErrorMessage(); - return err; -} - -} // namespace sql diff --git a/app/sql/connection.h b/app/sql/connection.h deleted file mode 100644 index d82b8b8..0000000 --- a/app/sql/connection.h +++ /dev/null @@ -1,387 +0,0 @@ -// 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 APP_SQL_CONNECTION_H_ -#define APP_SQL_CONNECTION_H_ -#pragma once - -#include <map> -#include <set> -#include <string> - -#include "base/basictypes.h" -#include "base/memory/ref_counted.h" -#include "base/time.h" - -class FilePath; -struct sqlite3; -struct sqlite3_stmt; - -namespace sql { - -class Statement; - -// Uniquely identifies a statement. There are two modes of operation: -// -// - In the most common mode, you will use the source file and line number to -// identify your statement. This is a convienient way to get uniqueness for -// a statement that is only used in one place. Use the SQL_FROM_HERE macro -// to generate a StatementID. -// -// - In the "custom" mode you may use the statement from different places or -// need to manage it yourself for whatever reason. In this case, you should -// make up your own unique name and pass it to the StatementID. This name -// must be a static string, since this object only deals with pointers and -// assumes the underlying string doesn't change or get deleted. -// -// This object is copyable and assignable using the compiler-generated -// operator= and copy constructor. -class StatementID { - public: - // Creates a uniquely named statement with the given file ane line number. - // Normally you will use SQL_FROM_HERE instead of calling yourself. - StatementID(const char* file, int line) - : number_(line), - str_(file) { - } - - // Creates a uniquely named statement with the given user-defined name. - explicit StatementID(const char* unique_name) - : number_(-1), - str_(unique_name) { - } - - // This constructor is unimplemented and will generate a linker error if - // called. It is intended to try to catch people dynamically generating - // a statement name that will be deallocated and will cause a crash later. - // All strings must be static and unchanging! - explicit StatementID(const std::string& dont_ever_do_this); - - // We need this to insert into our map. - bool operator<(const StatementID& other) const; - - private: - int number_; - const char* str_; -}; - -#define SQL_FROM_HERE sql::StatementID(__FILE__, __LINE__) - -class Connection; - -// ErrorDelegate defines the interface to implement error handling and recovery -// for sqlite operations. This allows the rest of the classes to return true or -// false while the actual error code and causing statement are delivered using -// the OnError() callback. -// The tipical usage is to centralize the code designed to handle database -// corruption, low-level IO errors or locking violations. -class ErrorDelegate : public base::RefCounted<ErrorDelegate> { - public: - ErrorDelegate(); - - // |error| is an sqlite result code as seen in sqlite\preprocessed\sqlite3.h - // |connection| is db connection where the error happened and |stmt| is - // our best guess at the statement that triggered the error. Do not store - // these pointers. - // - // |stmt| MAY BE NULL if there is no statement causing the problem (i.e. on - // initialization). - // - // If the error condition has been fixed an the original statement succesfuly - // re-tried then returning SQLITE_OK is appropiate; otherwise is recomended - // that you return the original |error| or the appropiae error code. - virtual int OnError(int error, Connection* connection, Statement* stmt) = 0; - - protected: - friend class base::RefCounted<ErrorDelegate>; - - virtual ~ErrorDelegate(); -}; - -class Connection { - private: - class StatementRef; // Forward declaration, see real one below. - - public: - // The database is opened by calling Open[InMemory](). Any uncommitted - // transactions will be rolled back when this object is deleted. - Connection(); - ~Connection(); - - // Pre-init configuration ---------------------------------------------------- - - // Sets the page size that will be used when creating a new database. This - // must be called before Init(), and will only have an effect on new - // databases. - // - // From sqlite.org: "The page size must be a power of two greater than or - // equal to 512 and less than or equal to SQLITE_MAX_PAGE_SIZE. The maximum - // value for SQLITE_MAX_PAGE_SIZE is 32768." - void set_page_size(int page_size) { page_size_ = page_size; } - - // Sets the number of pages that will be cached in memory by sqlite. The - // total cache size in bytes will be page_size * cache_size. This must be - // called before Open() to have an effect. - void set_cache_size(int cache_size) { cache_size_ = cache_size; } - - // Call to put the database in exclusive locking mode. There is no "back to - // normal" flag because of some additional requirements sqlite puts on this - // transaition (requires another access to the DB) and because we don't - // actually need it. - // - // Exclusive mode means that the database is not unlocked at the end of each - // transaction, which means there may be less time spent initializing the - // next transaction because it doesn't have to re-aquire locks. - // - // This must be called before Open() to have an effect. - void set_exclusive_locking() { exclusive_locking_ = true; } - - // Sets the object that will handle errors. Recomended that it should be set - // before calling Open(). If not set, the default is to ignore errors on - // release and assert on debug builds. - void set_error_delegate(ErrorDelegate* delegate) { - error_delegate_ = delegate; - } - - // Initialization ------------------------------------------------------------ - - // Initializes the SQL connection for the given file, returning true if the - // file could be opened. You can call this or OpenInMemory. - bool Open(const FilePath& path); - - // Initializes the SQL connection for a temporary in-memory database. There - // will be no associated file on disk, and the initial database will be - // empty. You can call this or Open. - bool OpenInMemory(); - - // Returns trie if the database has been successfully opened. - bool is_open() const { return !!db_; } - - // Closes the database. This is automatically performed on destruction for - // you, but this allows you to close the database early. You must not call - // any other functions after closing it. It is permissable to call Close on - // an uninitialized or already-closed database. - void Close(); - - // Pre-loads the first <cache-size> pages into the cache from the file. - // If you expect to soon use a substantial portion of the database, this - // is much more efficient than allowing the pages to be populated organically - // since there is no per-page hard drive seeking. If the file is larger than - // the cache, the last part that doesn't fit in the cache will be brought in - // organically. - // - // This function assumes your class is using a meta table on the current - // database, as it openes a transaction on the meta table to force the - // database to be initialized. You should feel free to initialize the meta - // table after calling preload since the meta table will already be in the - // database if it exists, and if it doesn't exist, the database won't - // generally exist either. - void Preload(); - - // Transactions -------------------------------------------------------------- - - // Transaction management. We maintain a virtual transaction stack to emulate - // nested transactions since sqlite can't do nested transactions. The - // limitation is you can't roll back a sub transaction: if any transaction - // fails, all transactions open will also be rolled back. Any nested - // transactions after one has rolled back will return fail for Begin(). If - // Begin() fails, you must not call Commit or Rollback(). - // - // Normally you should use sql::Transaction to manage a transaction, which - // will scope it to a C++ context. - bool BeginTransaction(); - void RollbackTransaction(); - bool CommitTransaction(); - - // Returns the current transaction nesting, which will be 0 if there are - // no open transactions. - int transaction_nesting() const { return transaction_nesting_; } - - // Statements ---------------------------------------------------------------- - - // Executes the given SQL string, returning true on success. This is - // normally used for simple, 1-off statements that don't take any bound - // parameters and don't return any data (e.g. CREATE TABLE). - bool Execute(const char* sql); - - // Returns true if we have a statement with the given identifier already - // cached. This is normally not necessary to call, but can be useful if the - // caller has to dynamically build up SQL to avoid doing so if it's already - // cached. - bool HasCachedStatement(const StatementID& id) const; - - // Returns a statement for the given SQL using the statement cache. It can - // take a nontrivial amount of work to parse and compile a statement, so - // keeping commonly-used ones around for future use is important for - // performance. - // - // The SQL may have an error, so the caller must check validity of the - // statement before using it. - // - // The StatementID and the SQL must always correspond to one-another. The - // ID is the lookup into the cache, so crazy things will happen if you use - // different SQL with the same ID. - // - // You will normally use the SQL_FROM_HERE macro to generate a statement - // ID associated with the current line of code. This gives uniqueness without - // you having to manage unique names. See StatementID above for more. - // - // Example: - // sql::Statement stmt(connection_.GetCachedStatement( - // SQL_FROM_HERE, "SELECT * FROM foo")); - // if (!stmt) - // return false; // Error creating statement. - scoped_refptr<StatementRef> GetCachedStatement(const StatementID& id, - const char* sql); - - // Returns a non-cached statement for the given SQL. Use this for SQL that - // is only executed once or only rarely (there is overhead associated with - // keeping a statement cached). - // - // See GetCachedStatement above for examples and error information. - scoped_refptr<StatementRef> GetUniqueStatement(const char* sql); - - // Info querying ------------------------------------------------------------- - - // Returns true if the given table exists. - bool DoesTableExist(const char* table_name) const; - - // Returns true if a column with the given name exists in the given table. - bool DoesColumnExist(const char* table_name, const char* column_name) const; - - // Returns sqlite's internal ID for the last inserted row. Valid only - // immediately after an insert. - int64 GetLastInsertRowId() const; - - // Returns sqlite's count of the number of rows modified by the last - // statement executed. Will be 0 if no statement has executed or the database - // is closed. - int GetLastChangeCount() const; - - // Errors -------------------------------------------------------------------- - - // Returns the error code associated with the last sqlite operation. - int GetErrorCode() const; - - // Returns the errno associated with GetErrorCode(). See - // SQLITE_LAST_ERRNO in SQLite documentation. - int GetLastErrno() const; - - // Returns a pointer to a statically allocated string associated with the - // last sqlite operation. - const char* GetErrorMessage() const; - - private: - // Statement access StatementRef which we don't want to expose to erverybody - // (they should go through Statement). - friend class Statement; - - // Internal initialize function used by both Init and InitInMemory. The file - // name is always 8 bits since we want to use the 8-bit version of - // sqlite3_open. The string can also be sqlite's special ":memory:" string. - bool OpenInternal(const std::string& file_name); - - // A StatementRef is a refcounted wrapper around a sqlite statement pointer. - // Refcounting allows us to give these statements out to sql::Statement - // objects while also optionally maintaining a cache of compiled statements - // by just keeping a refptr to these objects. - // - // A statement ref can be valid, in which case it can be used, or invalid to - // indicate that the statement hasn't been created yet, has an error, or has - // been destroyed. - // - // The Connection may revoke a StatementRef in some error cases, so callers - // should always check validity before using. - class StatementRef : public base::RefCounted<StatementRef> { - public: - // Default constructor initializes to an invalid statement. - StatementRef(); - StatementRef(Connection* connection, sqlite3_stmt* stmt); - - // When true, the statement can be used. - bool is_valid() const { return !!stmt_; } - - // If we've not been linked to a connection, this will be NULL. Guaranteed - // non-NULL when is_valid(). - Connection* connection() const { return connection_; } - - // Returns the sqlite statement if any. If the statement is not active, - // this will return NULL. - sqlite3_stmt* stmt() const { return stmt_; } - - // Destroys the compiled statement and marks it NULL. The statement will - // no longer be active. - void Close(); - - private: - friend class base::RefCounted<StatementRef>; - - ~StatementRef(); - - Connection* connection_; - sqlite3_stmt* stmt_; - - DISALLOW_COPY_AND_ASSIGN(StatementRef); - }; - friend class StatementRef; - - // Executes a rollback statement, ignoring all transaction state. Used - // internally in the transaction management code. - void DoRollback(); - - // Called by a StatementRef when it's being created or destroyed. See - // open_statements_ below. - void StatementRefCreated(StatementRef* ref); - void StatementRefDeleted(StatementRef* ref); - - // Frees all cached statements from statement_cache_. - void ClearCache(); - - // Called by Statement objects when an sqlite function returns an error. - // The return value is the error code reflected back to client code. - int OnSqliteError(int err, Statement* stmt); - - // Like |Execute()|, but retries if the database is locked. - bool ExecuteWithTimeout(const char* sql, base::TimeDelta ms_timeout); - - // The actual sqlite database. Will be NULL before Init has been called or if - // Init resulted in an error. - sqlite3* db_; - - // Parameters we'll configure in sqlite before doing anything else. Zero means - // use the default value. - int page_size_; - int cache_size_; - bool exclusive_locking_; - - // All cached statements. Keeping a reference to these statements means that - // they'll remain active. - typedef std::map<StatementID, scoped_refptr<StatementRef> > - CachedStatementMap; - CachedStatementMap statement_cache_; - - // A list of all StatementRefs we've given out. Each ref must register with - // us when it's created or destroyed. This allows us to potentially close - // any open statements when we encounter an error. - typedef std::set<StatementRef*> StatementRefSet; - StatementRefSet open_statements_; - - // Number of currently-nested transactions. - int transaction_nesting_; - - // True if any of the currently nested transactions have been rolled back. - // When we get to the outermost transaction, this will determine if we do - // a rollback instead of a commit. - bool needs_rollback_; - - // This object handles errors resulting from all forms of executing sqlite - // commands or statements. It can be null which means default handling. - scoped_refptr<ErrorDelegate> error_delegate_; - - DISALLOW_COPY_AND_ASSIGN(Connection); -}; - -} // namespace sql - -#endif // APP_SQL_CONNECTION_H_ diff --git a/app/sql/connection_unittest.cc b/app/sql/connection_unittest.cc deleted file mode 100644 index 2c1a5a0..0000000 --- a/app/sql/connection_unittest.cc +++ /dev/null @@ -1,106 +0,0 @@ -// 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. - -#include "app/sql/connection.h" -#include "app/sql/statement.h" -#include "base/file_util.h" -#include "base/scoped_temp_dir.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/sqlite/sqlite3.h" - -class SQLConnectionTest : public testing::Test { - public: - SQLConnectionTest() {} - - void SetUp() { - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - ASSERT_TRUE(db_.Open(temp_dir_.path().AppendASCII("SQLConnectionTest.db"))); - } - - void TearDown() { - db_.Close(); - } - - sql::Connection& db() { return db_; } - - private: - ScopedTempDir temp_dir_; - sql::Connection db_; -}; - -TEST_F(SQLConnectionTest, Execute) { - // Valid statement should return true. - ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); - EXPECT_EQ(SQLITE_OK, db().GetErrorCode()); - - // Invalid statement should fail. - ASSERT_FALSE(db().Execute("CREATE TAB foo (a, b")); - EXPECT_EQ(SQLITE_ERROR, db().GetErrorCode()); -} - -TEST_F(SQLConnectionTest, CachedStatement) { - sql::StatementID id1("foo", 12); - - ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); - ASSERT_TRUE(db().Execute("INSERT INTO foo(a, b) VALUES (12, 13)")); - - // Create a new cached statement. - { - sql::Statement s(db().GetCachedStatement(id1, "SELECT a FROM foo")); - ASSERT_FALSE(!s); // Test ! operator for validity. - - ASSERT_TRUE(s.Step()); - EXPECT_EQ(12, s.ColumnInt(0)); - } - - // The statement should be cached still. - EXPECT_TRUE(db().HasCachedStatement(id1)); - - { - // Get the same statement using different SQL. This should ignore our - // SQL and use the cached one (so it will be valid). - sql::Statement s(db().GetCachedStatement(id1, "something invalid(")); - ASSERT_FALSE(!s); // Test ! operator for validity. - - ASSERT_TRUE(s.Step()); - EXPECT_EQ(12, s.ColumnInt(0)); - } - - // Make sure other statements aren't marked as cached. - EXPECT_FALSE(db().HasCachedStatement(SQL_FROM_HERE)); -} - -TEST_F(SQLConnectionTest, DoesStuffExist) { - // Test DoesTableExist. - EXPECT_FALSE(db().DoesTableExist("foo")); - ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); - EXPECT_TRUE(db().DoesTableExist("foo")); - - // Should be case sensitive. - EXPECT_FALSE(db().DoesTableExist("FOO")); - - // Test DoesColumnExist. - EXPECT_FALSE(db().DoesColumnExist("foo", "bar")); - EXPECT_TRUE(db().DoesColumnExist("foo", "a")); - - // Testing for a column on a nonexistent table. - EXPECT_FALSE(db().DoesColumnExist("bar", "b")); -} - -TEST_F(SQLConnectionTest, GetLastInsertRowId) { - ASSERT_TRUE(db().Execute("CREATE TABLE foo (id INTEGER PRIMARY KEY, value)")); - - ASSERT_TRUE(db().Execute("INSERT INTO foo (value) VALUES (12)")); - - // Last insert row ID should be valid. - int64 row = db().GetLastInsertRowId(); - EXPECT_LT(0, row); - - // It should be the primary key of the row we just inserted. - sql::Statement s(db().GetUniqueStatement("SELECT value FROM foo WHERE id=?")); - s.BindInt64(0, row); - ASSERT_TRUE(s.Step()); - EXPECT_EQ(12, s.ColumnInt(0)); -} - diff --git a/app/sql/diagnostic_error_delegate.h b/app/sql/diagnostic_error_delegate.h deleted file mode 100644 index 713c4aa..0000000 --- a/app/sql/diagnostic_error_delegate.h +++ /dev/null @@ -1,50 +0,0 @@ -// 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. - -#ifndef APP_SQL_DIAGNOSTIC_ERROR_DELEGATE_H_ -#define APP_SQL_DIAGNOSTIC_ERROR_DELEGATE_H_ -#pragma once - -#include "app/sql/connection.h" -#include "base/logging.h" -#include "base/metrics/histogram.h" - -namespace sql { - -// This class handles the exceptional sqlite errors that we might encounter -// if for example the db is corrupted. Right now we just generate a UMA -// histogram for release and an assert for debug builds. -// -// Why is it a template you ask? well, that is a funny story. The histograms -// need to be singletons that is why they are always static at the function -// scope, but we cannot use the Singleton class because they are not default -// constructible. The template parameter makes the compiler to create unique -// classes that don't share the same static variable. -template <class UniqueT> -class DiagnosticErrorDelegate : public ErrorDelegate { - public: - - virtual int OnError(int error, Connection* connection, - Statement* stmt) { - NOTREACHED() << "sqlite error " << error - << ", errno " << connection->GetLastErrno() - << ": " << connection->GetErrorMessage(); - RecordErrorInHistogram(error); - return error; - } - - private: - static void RecordErrorInHistogram(int error) { - // Trim off the extended error codes. - error &= 0xff; - - // The histogram values from sqlite result codes go currently from 1 to - // 26 currently but 50 gives them room to grow. - UMA_HISTOGRAM_ENUMERATION(UniqueT::name(), error, 50); - } -}; - -} // namespace sql - -#endif // APP_SQL_DIAGNOSTIC_ERROR_DELEGATE_H_ diff --git a/app/sql/init_status.h b/app/sql/init_status.h deleted file mode 100644 index 048c5e8..0000000 --- a/app/sql/init_status.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2009 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 APP_SQL_INIT_STATUS_H_ -#define APP_SQL_INIT_STATUS_H_ -#pragma once - -namespace sql { - -// Used as the return value for some databases' init functions. -enum InitStatus { - INIT_OK, - - // Some error, usually I/O related opening the file. - INIT_FAILURE, - - // The database is from a future version of the app and cannot be read. - INIT_TOO_NEW, -}; - -} // namespace sql - -#endif // APP_SQL_INIT_STATUS_H_ diff --git a/app/sql/meta_table.cc b/app/sql/meta_table.cc deleted file mode 100644 index 2700d67..0000000 --- a/app/sql/meta_table.cc +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (c) 2009 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 "app/sql/meta_table.h" - -#include "app/sql/connection.h" -#include "app/sql/statement.h" -#include "base/logging.h" -#include "base/string_util.h" - -namespace sql { - -// Key used in our meta table for version numbers. -static const char kVersionKey[] = "version"; -static const char kCompatibleVersionKey[] = "last_compatible_version"; - -MetaTable::MetaTable() : db_(NULL) { -} - -MetaTable::~MetaTable() { -} - -// static -bool MetaTable::DoesTableExist(sql::Connection* db) { - DCHECK(db); - return db->DoesTableExist("meta"); -} - -bool MetaTable::Init(Connection* db, int version, int compatible_version) { - DCHECK(!db_ && db); - db_ = db; - if (!DoesTableExist(db)) { - if (!db_->Execute("CREATE TABLE meta" - "(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY," - "value LONGVARCHAR)")) - return false; - - // Note: there is no index over the meta table. We currently only have a - // couple of keys, so it doesn't matter. If we start storing more stuff in - // there, we should create an index. - SetVersionNumber(version); - SetCompatibleVersionNumber(compatible_version); - } - return true; -} - -void MetaTable::Reset() { - db_ = NULL; -} - -bool MetaTable::SetValue(const char* key, const std::string& value) { - Statement s; - if (!PrepareSetStatement(&s, key)) - return false; - s.BindString(1, value); - return s.Run(); -} - -bool MetaTable::GetValue(const char* key, std::string* value) { - Statement s; - if (!PrepareGetStatement(&s, key)) - return false; - - *value = s.ColumnString(0); - return true; -} - -bool MetaTable::SetValue(const char* key, int value) { - Statement s; - if (!PrepareSetStatement(&s, key)) - return false; - - s.BindInt(1, value); - return s.Run(); -} - -bool MetaTable::GetValue(const char* key, int* value) { - Statement s; - if (!PrepareGetStatement(&s, key)) - return false; - - *value = s.ColumnInt(0); - return true; -} - -bool MetaTable::SetValue(const char* key, int64 value) { - Statement s; - if (!PrepareSetStatement(&s, key)) - return false; - s.BindInt64(1, value); - return s.Run(); -} - -bool MetaTable::GetValue(const char* key, int64* value) { - Statement s; - if (!PrepareGetStatement(&s, key)) - return false; - - *value = s.ColumnInt64(0); - return true; -} - -void MetaTable::SetVersionNumber(int version) { - SetValue(kVersionKey, version); -} - -int MetaTable::GetVersionNumber() { - int version = 0; - if (!GetValue(kVersionKey, &version)) - return 0; - return version; -} - -void MetaTable::SetCompatibleVersionNumber(int version) { - SetValue(kCompatibleVersionKey, version); -} - -int MetaTable::GetCompatibleVersionNumber() { - int version = 0; - if (!GetValue(kCompatibleVersionKey, &version)) - return 0; - return version; -} - -bool MetaTable::PrepareSetStatement(Statement* statement, const char* key) { - DCHECK(db_ && statement); - statement->Assign(db_->GetCachedStatement(SQL_FROM_HERE, - "INSERT OR REPLACE INTO meta (key,value) VALUES (?,?)")); - if (!statement->is_valid()) { - NOTREACHED() << db_->GetErrorMessage(); - return false; - } - statement->BindCString(0, key); - return true; -} - -bool MetaTable::PrepareGetStatement(Statement* statement, const char* key) { - DCHECK(db_ && statement); - statement->Assign(db_->GetCachedStatement(SQL_FROM_HERE, - "SELECT value FROM meta WHERE key=?")); - if (!statement->is_valid()) { - NOTREACHED() << db_->GetErrorMessage(); - return false; - } - statement->BindCString(0, key); - if (!statement->Step()) - return false; - return true; -} - -} // namespace sql diff --git a/app/sql/meta_table.h b/app/sql/meta_table.h deleted file mode 100644 index d5c76c8..0000000 --- a/app/sql/meta_table.h +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2009 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 APP_SQL_META_TABLE_H_ -#define APP_SQL_META_TABLE_H_ -#pragma once - -#include <string> - -#include "base/basictypes.h" - -namespace sql { - -class Connection; -class Statement; - -class MetaTable { - public: - MetaTable(); - ~MetaTable(); - - // Returns true if the 'meta' table exists. - static bool DoesTableExist(Connection* db); - - // Initializes the MetaTableHelper, creating the meta table if necessary. For - // new tables, it will initialize the version number to |version| and the - // compatible version number to |compatible_version|. - bool Init(Connection* db, int version, int compatible_version); - - // Resets this MetaTable object, making another call to Init() possible. - void Reset(); - - // The version number of the database. This should be the version number of - // the creator of the file. The version number will be 0 if there is no - // previously set version number. - // - // See also Get/SetCompatibleVersionNumber(). - void SetVersionNumber(int version); - int GetVersionNumber(); - - // The compatible version number is the lowest version of the code that this - // database can be read by. If there are minor changes or additions, old - // versions of the code can still work with the database without failing. - // - // For example, if an optional column is added to a table in version 3, the - // new code will set the version to 3, and the compatible version to 2, since - // the code expecting version 2 databases can still read and write the table. - // - // Rule of thumb: check the version number when you're upgrading, but check - // the compatible version number to see if you can read the file at all. If - // it's larger than you code is expecting, fail. - // - // The compatible version number will be 0 if there is no previously set - // compatible version number. - void SetCompatibleVersionNumber(int version); - int GetCompatibleVersionNumber(); - - // Set the given arbitrary key with the given data. Returns true on success. - bool SetValue(const char* key, const std::string& value); - bool SetValue(const char* key, int value); - bool SetValue(const char* key, int64 value); - - // Retrieves the value associated with the given key. This will use sqlite's - // type conversion rules. It will return true on success. - bool GetValue(const char* key, std::string* value); - bool GetValue(const char* key, int* value); - bool GetValue(const char* key, int64* value); - - private: - // Conveniences to prepare the two types of statements used by - // MetaTableHelper. - bool PrepareSetStatement(Statement* statement, const char* key); - bool PrepareGetStatement(Statement* statement, const char* key); - - Connection* db_; - - DISALLOW_COPY_AND_ASSIGN(MetaTable); -}; - -} // namespace sql - -#endif // APP_SQL_META_TABLE_H_ diff --git a/app/sql/sqlite_features_unittest.cc b/app/sql/sqlite_features_unittest.cc deleted file mode 100644 index 0ee63be..0000000 --- a/app/sql/sqlite_features_unittest.cc +++ /dev/null @@ -1,100 +0,0 @@ -// 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. - -#include <string> - -#include "app/sql/connection.h" -#include "app/sql/statement.h" -#include "base/file_path.h" -#include "base/file_util.h" -#include "base/path_service.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/sqlite/sqlite3.h" - -// Test that certain features are/are-not enabled in our SQLite. - -namespace { - - -class StatementErrorHandler : public sql::ErrorDelegate { - public: - StatementErrorHandler() : error_(SQLITE_OK) {} - - virtual int OnError(int error, sql::Connection* connection, - sql::Statement* stmt) { - error_ = error; - const char* sql_txt = stmt ? stmt->GetSQLStatement() : NULL; - sql_text_ = sql_txt ? sql_txt : "no statement available"; - return error; - } - - int error() const { return error_; } - - void reset_error() { - sql_text_.clear(); - error_ = SQLITE_OK; - } - - const char* sql_statement() const { return sql_text_.c_str(); } - - private: - int error_; - std::string sql_text_; -}; - -class SQLiteFeaturesTest : public testing::Test { - public: - SQLiteFeaturesTest() : error_handler_(new StatementErrorHandler) {} - - void SetUp() { - ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &path_)); - path_ = path_.AppendASCII("SQLStatementTest.db"); - file_util::Delete(path_, false); - ASSERT_TRUE(db_.Open(path_)); - // The |error_handler_| will be called if any sqlite statement operation - // returns an error code. - db_.set_error_delegate(error_handler_); - } - - void TearDown() { - // If any error happened the original sql statement can be found in - // error_handler_->sql_statement(). - EXPECT_EQ(SQLITE_OK, error_handler_->error()); - db_.Close(); - // If this fails something is going on with cleanup and later tests may - // fail, so we want to identify problems right away. - ASSERT_TRUE(file_util::Delete(path_, false)); - } - - sql::Connection& db() { return db_; } - - int sqlite_error() const { return error_handler_->error(); } - void reset_error() const { error_handler_->reset_error(); } - - private: - FilePath path_; - sql::Connection db_; - scoped_refptr<StatementErrorHandler> error_handler_; -}; - -// Do not include fts1 support, it is not useful, and nobody is -// looking at it. -TEST_F(SQLiteFeaturesTest, NoFTS1) { - ASSERT_FALSE(db().Execute("CREATE VIRTUAL TABLE foo USING fts1(x)")); -} - -// fts2 is used for older history files, so we're signed on for -// keeping our version up-to-date. -// TODO(shess): Think up a crazy way to get out from having to support -// this forever. -TEST_F(SQLiteFeaturesTest, FTS2) { - ASSERT_TRUE(db().Execute("CREATE VIRTUAL TABLE foo USING fts2(x)")); -} - -// fts3 is used for current history files, and also for WebDatabase. -TEST_F(SQLiteFeaturesTest, FTS3) { - ASSERT_TRUE(db().Execute("CREATE VIRTUAL TABLE foo USING fts3(x)")); -} - -} // namespace diff --git a/app/sql/statement.cc b/app/sql/statement.cc deleted file mode 100644 index 07be3d3..0000000 --- a/app/sql/statement.cc +++ /dev/null @@ -1,270 +0,0 @@ -// 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 "app/sql/statement.h" - -#include "base/logging.h" -#include "base/utf_string_conversions.h" -#include "third_party/sqlite/sqlite3.h" - -namespace sql { - -// This empty constructor initializes our reference with an empty one so that -// we don't have to NULL-check the ref_ to see if the statement is valid: we -// only have to check the ref's validity bit. -Statement::Statement() - : ref_(new Connection::StatementRef), - succeeded_(false) { -} - -Statement::Statement(scoped_refptr<Connection::StatementRef> ref) - : ref_(ref), - succeeded_(false) { -} - -Statement::~Statement() { - // Free the resources associated with this statement. We assume there's only - // one statement active for a given sqlite3_stmt at any time, so this won't - // mess with anything. - Reset(); -} - -void Statement::Assign(scoped_refptr<Connection::StatementRef> ref) { - Reset(); - ref_ = ref; -} - -bool Statement::Run() { - if (!is_valid()) - return false; - return CheckError(sqlite3_step(ref_->stmt())) == SQLITE_DONE; -} - -bool Statement::Step() { - if (!is_valid()) - return false; - return CheckError(sqlite3_step(ref_->stmt())) == SQLITE_ROW; -} - -void Statement::Reset() { - if (is_valid()) { - // We don't call CheckError() here because sqlite3_reset() returns - // the last error that Step() caused thereby generating a second - // spurious error callback. - sqlite3_clear_bindings(ref_->stmt()); - sqlite3_reset(ref_->stmt()); - } - succeeded_ = false; -} - -bool Statement::Succeeded() const { - if (!is_valid()) - return false; - return succeeded_; -} - -bool Statement::BindNull(int col) { - if (is_valid()) { - int err = CheckError(sqlite3_bind_null(ref_->stmt(), col + 1)); - return err == SQLITE_OK; - } - return false; -} - -bool Statement::BindBool(int col, bool val) { - return BindInt(col, val ? 1 : 0); -} - -bool Statement::BindInt(int col, int val) { - if (is_valid()) { - int err = CheckError(sqlite3_bind_int(ref_->stmt(), col + 1, val)); - return err == SQLITE_OK; - } - return false; -} - -bool Statement::BindInt64(int col, int64 val) { - if (is_valid()) { - int err = CheckError(sqlite3_bind_int64(ref_->stmt(), col + 1, val)); - return err == SQLITE_OK; - } - return false; -} - -bool Statement::BindDouble(int col, double val) { - if (is_valid()) { - int err = CheckError(sqlite3_bind_double(ref_->stmt(), col + 1, val)); - return err == SQLITE_OK; - } - return false; -} - -bool Statement::BindCString(int col, const char* val) { - if (is_valid()) { - int err = CheckError(sqlite3_bind_text(ref_->stmt(), col + 1, val, -1, - SQLITE_TRANSIENT)); - return err == SQLITE_OK; - } - return false; -} - -bool Statement::BindString(int col, const std::string& val) { - if (is_valid()) { - int err = CheckError(sqlite3_bind_text(ref_->stmt(), col + 1, val.data(), - val.size(), SQLITE_TRANSIENT)); - return err == SQLITE_OK; - } - return false; -} - -bool Statement::BindString16(int col, const string16& value) { - return BindString(col, UTF16ToUTF8(value)); -} - -bool Statement::BindBlob(int col, const void* val, int val_len) { - if (is_valid()) { - int err = CheckError(sqlite3_bind_blob(ref_->stmt(), col + 1, - val, val_len, SQLITE_TRANSIENT)); - return err == SQLITE_OK; - } - return false; -} - -int Statement::ColumnCount() const { - if (!is_valid()) { - NOTREACHED(); - return 0; - } - return sqlite3_column_count(ref_->stmt()); -} - -ColType Statement::ColumnType(int col) const { - // Verify that our enum matches sqlite's values. - COMPILE_ASSERT(COLUMN_TYPE_INTEGER == SQLITE_INTEGER, integer_no_match); - COMPILE_ASSERT(COLUMN_TYPE_FLOAT == SQLITE_FLOAT, float_no_match); - COMPILE_ASSERT(COLUMN_TYPE_TEXT == SQLITE_TEXT, integer_no_match); - COMPILE_ASSERT(COLUMN_TYPE_BLOB == SQLITE_BLOB, blob_no_match); - COMPILE_ASSERT(COLUMN_TYPE_NULL == SQLITE_NULL, null_no_match); - - return static_cast<ColType>(sqlite3_column_type(ref_->stmt(), col)); -} - -bool Statement::ColumnBool(int col) const { - return !!ColumnInt(col); -} - -int Statement::ColumnInt(int col) const { - if (!is_valid()) { - NOTREACHED(); - return 0; - } - return sqlite3_column_int(ref_->stmt(), col); -} - -int64 Statement::ColumnInt64(int col) const { - if (!is_valid()) { - NOTREACHED(); - return 0; - } - return sqlite3_column_int64(ref_->stmt(), col); -} - -double Statement::ColumnDouble(int col) const { - if (!is_valid()) { - NOTREACHED(); - return 0; - } - return sqlite3_column_double(ref_->stmt(), col); -} - -std::string Statement::ColumnString(int col) const { - if (!is_valid()) { - NOTREACHED(); - return ""; - } - const char* str = reinterpret_cast<const char*>( - sqlite3_column_text(ref_->stmt(), col)); - int len = sqlite3_column_bytes(ref_->stmt(), col); - - std::string result; - if (str && len > 0) - result.assign(str, len); - return result; -} - -string16 Statement::ColumnString16(int col) const { - if (!is_valid()) { - NOTREACHED(); - return string16(); - } - std::string s = ColumnString(col); - return !s.empty() ? UTF8ToUTF16(s) : string16(); -} - -int Statement::ColumnByteLength(int col) const { - if (!is_valid()) { - NOTREACHED(); - return 0; - } - return sqlite3_column_bytes(ref_->stmt(), col); -} - -const void* Statement::ColumnBlob(int col) const { - if (!is_valid()) { - NOTREACHED(); - return NULL; - } - - return sqlite3_column_blob(ref_->stmt(), col); -} - -bool Statement::ColumnBlobAsString(int col, std::string* blob) { - if (!is_valid()) { - NOTREACHED(); - return false; - } - const void* p = ColumnBlob(col); - size_t len = ColumnByteLength(col); - blob->resize(len); - if (blob->size() != len) { - return false; - } - blob->assign(reinterpret_cast<const char*>(p), len); - return true; -} - -void Statement::ColumnBlobAsVector(int col, std::vector<char>* val) const { - val->clear(); - if (!is_valid()) { - NOTREACHED(); - return; - } - - const void* data = sqlite3_column_blob(ref_->stmt(), col); - int len = sqlite3_column_bytes(ref_->stmt(), col); - if (data && len > 0) { - val->resize(len); - memcpy(&(*val)[0], data, len); - } -} - -void Statement::ColumnBlobAsVector( - int col, - std::vector<unsigned char>* val) const { - ColumnBlobAsVector(col, reinterpret_cast< std::vector<char>* >(val)); -} - -const char* Statement::GetSQLStatement() { - return sqlite3_sql(ref_->stmt()); -} - -int Statement::CheckError(int err) { - // Please don't add DCHECKs here, OnSqliteError() already has them. - succeeded_ = (err == SQLITE_OK || err == SQLITE_ROW || err == SQLITE_DONE); - if (!succeeded_ && is_valid()) - return ref_->connection()->OnSqliteError(err, this); - return err; -} - -} // namespace sql diff --git a/app/sql/statement.h b/app/sql/statement.h deleted file mode 100644 index 6bf3949..0000000 --- a/app/sql/statement.h +++ /dev/null @@ -1,167 +0,0 @@ -// 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 APP_SQL_STATEMENT_H_ -#define APP_SQL_STATEMENT_H_ -#pragma once - -#include <string> -#include <vector> - -#include "app/sql/connection.h" -#include "base/basictypes.h" -#include "base/memory/ref_counted.h" -#include "base/string16.h" - -namespace sql { - -// Possible return values from ColumnType in a statement. These should match -// the values in sqlite3.h. -enum ColType { - COLUMN_TYPE_INTEGER = 1, - COLUMN_TYPE_FLOAT = 2, - COLUMN_TYPE_TEXT = 3, - COLUMN_TYPE_BLOB = 4, - COLUMN_TYPE_NULL = 5, -}; - -// Normal usage: -// sql::Statement s(connection_.GetUniqueStatement(...)); -// if (!s) // You should check for errors before using the statement. -// return false; -// -// s.BindInt(0, a); -// if (s.Step()) -// return s.ColumnString(0); -// -// Step() and Run() just return true to signal success. If you want to handle -// specific errors such as database corruption, install an error handler in -// in the connection object using set_error_delegate(). -class Statement { - public: - // Creates an uninitialized statement. The statement will be invalid until - // you initialize it via Assign. - Statement(); - - explicit Statement(scoped_refptr<Connection::StatementRef> ref); - ~Statement(); - - // Initializes this object with the given statement, which may or may not - // be valid. Use is_valid() to check if it's OK. - void Assign(scoped_refptr<Connection::StatementRef> ref); - - // Returns true if the statement can be executed. All functions can still - // be used if the statement is invalid, but they will return failure or some - // default value. This is because the statement can become invalid in the - // middle of executing a command if there is a serioud error and the database - // has to be reset. - bool is_valid() const { return ref_->is_valid(); } - - // These operators allow conveniently checking if the statement is valid - // or not. See the pattern above for an example. - operator bool() const { return is_valid(); } - bool operator!() const { return !is_valid(); } - - // Running ------------------------------------------------------------------- - - // Executes the statement, returning true on success. This is like Step but - // for when there is no output, like an INSERT statement. - bool Run(); - - // Executes the statement, returning true if there is a row of data returned. - // You can keep calling Step() until it returns false to iterate through all - // the rows in your result set. - // - // When Step returns false, the result is either that there is no more data - // or there is an error. This makes it most convenient for loop usage. If you - // need to disambiguate these cases, use Succeeded(). - // - // Typical example: - // while (s.Step()) { - // ... - // } - // return s.Succeeded(); - bool Step(); - - // Resets the statement to its initial condition. This includes clearing all - // the bound variables and any current result row. - void Reset(); - - // Returns true if the last executed thing in this statement succeeded. If - // there was no last executed thing or the statement is invalid, this will - // return false. - bool Succeeded() const; - - // Binding ------------------------------------------------------------------- - - // These all take a 0-based argument index and return true on failure. You - // may not always care about the return value (they'll DCHECK if they fail). - // The main thing you may want to check is when binding large blobs or - // strings there may be out of memory. - bool BindNull(int col); - bool BindBool(int col, bool val); - bool BindInt(int col, int val); - bool BindInt64(int col, int64 val); - bool BindDouble(int col, double val); - bool BindCString(int col, const char* val); - bool BindString(int col, const std::string& val); - bool BindString16(int col, const string16& value); - bool BindBlob(int col, const void* value, int value_len); - - // Retrieving ---------------------------------------------------------------- - - // Returns the number of output columns in the result. - int ColumnCount() const; - - // Returns the type associated with the given column. - // - // Watch out: the type may be undefined if you've done something to cause a - // "type conversion." This means requesting the value of a column of a type - // where that type is not the native type. For safety, call ColumnType only - // on a column before getting the value out in any way. - ColType ColumnType(int col) const; - - // These all take a 0-based argument index. - bool ColumnBool(int col) const; - int ColumnInt(int col) const; - int64 ColumnInt64(int col) const; - double ColumnDouble(int col) const; - std::string ColumnString(int col) const; - string16 ColumnString16(int col) const; - - // When reading a blob, you can get a raw pointer to the underlying data, - // along with the length, or you can just ask us to copy the blob into a - // vector. Danger! ColumnBlob may return NULL if there is no data! - int ColumnByteLength(int col) const; - const void* ColumnBlob(int col) const; - bool ColumnBlobAsString(int col, std::string* blob); - void ColumnBlobAsVector(int col, std::vector<char>* val) const; - void ColumnBlobAsVector(int col, std::vector<unsigned char>* val) const; - - // Diagnostics -------------------------------------------------------------- - - // Returns the original text of sql statement. Do not keep a pointer to it. - const char* GetSQLStatement(); - - private: - // This is intended to check for serious errors and report them to the - // connection object. It takes a sqlite error code, and returns the same - // code. Currently this function just updates the succeeded flag, but will be - // enhanced in the future to do the notification. - int CheckError(int err); - - // The actual sqlite statement. This may be unique to us, or it may be cached - // by the connection, which is why it's refcounted. This pointer is - // guaranteed non-NULL. - scoped_refptr<Connection::StatementRef> ref_; - - // See Succeeded() for what this holds. - bool succeeded_; - - DISALLOW_COPY_AND_ASSIGN(Statement); -}; - -} // namespace sql - -#endif // APP_SQL_STATEMENT_H_ diff --git a/app/sql/statement_unittest.cc b/app/sql/statement_unittest.cc deleted file mode 100644 index 0085d7c..0000000 --- a/app/sql/statement_unittest.cc +++ /dev/null @@ -1,126 +0,0 @@ -// 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. - -#include <string> - -#include "app/sql/connection.h" -#include "app/sql/statement.h" -#include "base/file_util.h" -#include "base/scoped_temp_dir.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/sqlite/sqlite3.h" - -class StatementErrorHandler : public sql::ErrorDelegate { - public: - StatementErrorHandler() : error_(SQLITE_OK) {} - - virtual int OnError(int error, sql::Connection* connection, - sql::Statement* stmt) { - error_ = error; - const char* sql_txt = stmt ? stmt->GetSQLStatement() : NULL; - sql_text_ = sql_txt ? sql_txt : "no statement available"; - return error; - } - - int error() const { return error_; } - - void reset_error() { - sql_text_.clear(); - error_ = SQLITE_OK; - } - - const char* sql_statement() const { return sql_text_.c_str(); } - - private: - int error_; - std::string sql_text_; -}; - -class SQLStatementTest : public testing::Test { - public: - SQLStatementTest() : error_handler_(new StatementErrorHandler) {} - - void SetUp() { - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - ASSERT_TRUE(db_.Open(temp_dir_.path().AppendASCII("SQLStatementTest.db"))); - - // The |error_handler_| will be called if any sqlite statement operation - // returns an error code. - db_.set_error_delegate(error_handler_); - } - - void TearDown() { - // If any error happened the original sql statement can be found in - // error_handler_->sql_statement(). - EXPECT_EQ(SQLITE_OK, error_handler_->error()); - db_.Close(); - } - - sql::Connection& db() { return db_; } - - int sqlite_error() const { return error_handler_->error(); } - void reset_error() const { error_handler_->reset_error(); } - - private: - ScopedTempDir temp_dir_; - sql::Connection db_; - scoped_refptr<StatementErrorHandler> error_handler_; -}; - -TEST_F(SQLStatementTest, Assign) { - sql::Statement s; - EXPECT_FALSE(s); // bool conversion operator. - EXPECT_TRUE(!s); // ! operator. - EXPECT_FALSE(s.is_valid()); - - s.Assign(db().GetUniqueStatement("CREATE TABLE foo (a, b)")); - EXPECT_TRUE(s); - EXPECT_FALSE(!s); - EXPECT_TRUE(s.is_valid()); -} - -TEST_F(SQLStatementTest, Run) { - ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); - ASSERT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (3, 12)")); - - sql::Statement s(db().GetUniqueStatement("SELECT b FROM foo WHERE a=?")); - EXPECT_FALSE(s.Succeeded()); - - // Stepping it won't work since we haven't bound the value. - EXPECT_FALSE(s.Step()); - - // Run should fail since this produces output, and we should use Step(). This - // gets a bit wonky since sqlite says this is OK so succeeded is set. - s.Reset(); - s.BindInt(0, 3); - EXPECT_FALSE(s.Run()); - EXPECT_EQ(SQLITE_ROW, db().GetErrorCode()); - EXPECT_TRUE(s.Succeeded()); - - // Resetting it should put it back to the previous state (not runnable). - s.Reset(); - EXPECT_FALSE(s.Succeeded()); - - // Binding and stepping should produce one row. - s.BindInt(0, 3); - EXPECT_TRUE(s.Step()); - EXPECT_TRUE(s.Succeeded()); - EXPECT_EQ(12, s.ColumnInt(0)); - EXPECT_FALSE(s.Step()); - EXPECT_TRUE(s.Succeeded()); -} - -TEST_F(SQLStatementTest, BasicErrorCallback) { - ASSERT_TRUE(db().Execute("CREATE TABLE foo (a INTEGER PRIMARY KEY, b)")); - EXPECT_EQ(SQLITE_OK, sqlite_error()); - // Insert in the foo table the primary key. It is an error to insert - // something other than an number. This error causes the error callback - // handler to be called with SQLITE_MISMATCH as error code. - sql::Statement s(db().GetUniqueStatement("INSERT INTO foo (a) VALUES (?)")); - EXPECT_TRUE(s.is_valid()); - s.BindCString(0, "bad bad"); - EXPECT_FALSE(s.Run()); - EXPECT_EQ(SQLITE_MISMATCH, sqlite_error()); - reset_error(); -} diff --git a/app/sql/transaction.cc b/app/sql/transaction.cc deleted file mode 100644 index 10bcfb0..0000000 --- a/app/sql/transaction.cc +++ /dev/null @@ -1,51 +0,0 @@ -// 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 "app/sql/transaction.h" - -#include "app/sql/connection.h" -#include "base/logging.h" - -namespace sql { - -Transaction::Transaction(Connection* connection) - : connection_(connection), - is_open_(false) { -} - -Transaction::~Transaction() { - if (is_open_) - connection_->RollbackTransaction(); -} - -bool Transaction::Begin() { - if (is_open_) { - NOTREACHED() << "Beginning a transaction twice!"; - return false; - } - is_open_ = connection_->BeginTransaction(); - return is_open_; -} - -void Transaction::Rollback() { - if (!is_open_) { - NOTREACHED() << "Attempting to roll back a nonexistent transaction. " - << "Did you remember to call Begin() and check its return?"; - return; - } - is_open_ = false; - connection_->RollbackTransaction(); -} - -bool Transaction::Commit() { - if (!is_open_) { - NOTREACHED() << "Attempting to commit a nonexistent transaction. " - << "Did you remember to call Begin() and check its return?"; - return false; - } - is_open_ = false; - return connection_->CommitTransaction(); -} - -} // namespace sql diff --git a/app/sql/transaction.h b/app/sql/transaction.h deleted file mode 100644 index c65ca8d..0000000 --- a/app/sql/transaction.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2009 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 APP_SQL_TRANSACTION_H_ -#define APP_SQL_TRANSACTION_H_ -#pragma once - -#include "base/basictypes.h" - -namespace sql { - -class Connection; - -class Transaction { - public: - // Creates the scoped transaction object. You MUST call Begin() to begin the - // transaction. If you have begun a transaction and not committed it, the - // constructor will roll back the transaction. If you want to commit, you - // need to manually call Commit before this goes out of scope. - explicit Transaction(Connection* connection); - ~Transaction(); - - // Returns true when there is a transaction that has been successfully begun. - bool is_open() const { return is_open_; } - - // Begins the transaction. This uses the default sqlite "deferred" transaction - // type, which means that the DB lock is lazily acquired the next time the - // database is accessed, not in the begin transaction command. - // - // Returns false on failure. Note that if this fails, you shouldn't do - // anything you expect to be actually transactional, because it won't be! - bool Begin(); - - // Rolls back the transaction. This will happen automatically if you do - // nothing when the transaction goes out of scope. - void Rollback(); - - // Commits the transaction, returning true on success. This will return - // false if sqlite could not commit it, or if another transaction in the - // same outermost transaction has been rolled back (which necessitates a - // rollback of all transactions in that outermost one). - bool Commit(); - - private: - Connection* connection_; - - // True when the transaction is open, false when it's already been committed - // or rolled back. - bool is_open_; - - DISALLOW_COPY_AND_ASSIGN(Transaction); -}; - -} // namespace sql - -#endif // APP_SQL_TRANSACTION_H_ diff --git a/app/sql/transaction_unittest.cc b/app/sql/transaction_unittest.cc deleted file mode 100644 index 3d42a31..0000000 --- a/app/sql/transaction_unittest.cc +++ /dev/null @@ -1,133 +0,0 @@ -// 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. - -#include "app/sql/connection.h" -#include "app/sql/statement.h" -#include "app/sql/transaction.h" -#include "base/file_util.h" -#include "base/scoped_temp_dir.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/sqlite/sqlite3.h" - -class SQLTransactionTest : public testing::Test { - public: - SQLTransactionTest() {} - - void SetUp() { - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - ASSERT_TRUE(db_.Open( - temp_dir_.path().AppendASCII("SQLTransactionTest.db"))); - - ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); - } - - void TearDown() { - db_.Close(); - } - - sql::Connection& db() { return db_; } - - // Returns the number of rows in table "foo". - int CountFoo() { - sql::Statement count(db().GetUniqueStatement("SELECT count(*) FROM foo")); - count.Step(); - return count.ColumnInt(0); - } - - private: - ScopedTempDir temp_dir_; - sql::Connection db_; -}; - -TEST_F(SQLTransactionTest, Commit) { - { - sql::Transaction t(&db()); - EXPECT_FALSE(t.is_open()); - EXPECT_TRUE(t.Begin()); - EXPECT_TRUE(t.is_open()); - - EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)")); - - t.Commit(); - EXPECT_FALSE(t.is_open()); - } - - EXPECT_EQ(1, CountFoo()); -} - -TEST_F(SQLTransactionTest, Rollback) { - // Test some basic initialization, and that rollback runs when you exit the - // scope. - { - sql::Transaction t(&db()); - EXPECT_FALSE(t.is_open()); - EXPECT_TRUE(t.Begin()); - EXPECT_TRUE(t.is_open()); - - EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)")); - } - - // Nothing should have been committed since it was implicitly rolled back. - EXPECT_EQ(0, CountFoo()); - - // Test explicit rollback. - sql::Transaction t2(&db()); - EXPECT_FALSE(t2.is_open()); - EXPECT_TRUE(t2.Begin()); - - EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)")); - t2.Rollback(); - EXPECT_FALSE(t2.is_open()); - - // Nothing should have been committed since it was explicitly rolled back. - EXPECT_EQ(0, CountFoo()); -} - -// Rolling back any part of a transaction should roll back all of them. -TEST_F(SQLTransactionTest, NestedRollback) { - EXPECT_EQ(0, db().transaction_nesting()); - - // Outermost transaction. - { - sql::Transaction outer(&db()); - EXPECT_TRUE(outer.Begin()); - EXPECT_EQ(1, db().transaction_nesting()); - - // The first inner one gets committed. - { - sql::Transaction inner1(&db()); - EXPECT_TRUE(inner1.Begin()); - EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)")); - EXPECT_EQ(2, db().transaction_nesting()); - - inner1.Commit(); - EXPECT_EQ(1, db().transaction_nesting()); - } - - // One row should have gotten inserted. - EXPECT_EQ(1, CountFoo()); - - // The second inner one gets rolled back. - { - sql::Transaction inner2(&db()); - EXPECT_TRUE(inner2.Begin()); - EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)")); - EXPECT_EQ(2, db().transaction_nesting()); - - inner2.Rollback(); - EXPECT_EQ(1, db().transaction_nesting()); - } - - // A third inner one will fail in Begin since one has already been rolled - // back. - EXPECT_EQ(1, db().transaction_nesting()); - { - sql::Transaction inner3(&db()); - EXPECT_FALSE(inner3.Begin()); - EXPECT_EQ(1, db().transaction_nesting()); - } - } - EXPECT_EQ(0, db().transaction_nesting()); - EXPECT_EQ(0, CountFoo()); -} |