diff options
author | Torne (Richard Coles) <torne@google.com> | 2013-03-28 15:31:22 +0000 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2013-03-28 15:31:22 +0000 |
commit | 2a99a7e74a7f215066514fe81d2bfa6639d9eddd (patch) | |
tree | 7c2d04841fcd599fd83b0f0bb1100e1c89a35bae /sql | |
parent | 61c449bbbb53310a8c041d8cefdd6b01a126cc7e (diff) | |
download | chromium_org-2a99a7e74a7f215066514fe81d2bfa6639d9eddd.tar.gz |
Merge from Chromium at DEPS revision r190564
This commit was generated by merge_to_master.py.
Change-Id: Icadecbce29854b8fa25fd335b2c1949b5ca5d170
Diffstat (limited to 'sql')
-rw-r--r-- | sql/connection.cc | 207 | ||||
-rw-r--r-- | sql/connection.h | 71 | ||||
-rw-r--r-- | sql/connection_unittest.cc | 122 | ||||
-rw-r--r-- | sql/run_all_unittests.cc | 2 | ||||
-rw-r--r-- | sql/sql.gyp | 4 | ||||
-rw-r--r-- | sql/sql.target.linux-arm.mk (renamed from sql/sql.target.mk) | 32 | ||||
-rw-r--r-- | sql/sql.target.linux-x86.mk | 152 | ||||
-rw-r--r-- | sql/sqlite_features_unittest.cc | 8 | ||||
-rw-r--r-- | sql/statement.cc | 12 | ||||
-rw-r--r-- | sql/statement_unittest.cc | 8 | ||||
-rw-r--r-- | sql/transaction_unittest.cc | 8 |
11 files changed, 513 insertions, 113 deletions
diff --git a/sql/connection.cc b/sql/connection.cc index 1de9fc5cfa..5f6b5906e0 100644 --- a/sql/connection.cc +++ b/sql/connection.cc @@ -6,8 +6,9 @@ #include <string.h> -#include "base/file_path.h" +#include "base/files/file_path.h" #include "base/logging.h" +#include "base/metrics/histogram.h" #include "base/string_util.h" #include "base/stringprintf.h" #include "base/utf_string_conversions.h" @@ -73,30 +74,23 @@ bool StatementID::operator<(const StatementID& other) const { ErrorDelegate::~ErrorDelegate() { } -Connection::StatementRef::StatementRef() - : connection_(NULL), - stmt_(NULL) { -} - -Connection::StatementRef::StatementRef(sqlite3_stmt* stmt) - : connection_(NULL), - stmt_(stmt) { -} - Connection::StatementRef::StatementRef(Connection* connection, - sqlite3_stmt* stmt) + sqlite3_stmt* stmt, + bool was_valid) : connection_(connection), - stmt_(stmt) { - connection_->StatementRefCreated(this); + stmt_(stmt), + was_valid_(was_valid) { + if (connection) + connection_->StatementRefCreated(this); } Connection::StatementRef::~StatementRef() { if (connection_) connection_->StatementRefDeleted(this); - Close(); + Close(false); } -void Connection::StatementRef::Close() { +void Connection::StatementRef::Close(bool forced) { if (stmt_) { // Call to AssertIOAllowed() cannot go at the beginning of the function // because Close() is called unconditionally from destructor to clean @@ -110,6 +104,11 @@ void Connection::StatementRef::Close() { stmt_ = NULL; } connection_ = NULL; // The connection may be getting deleted. + + // Forced close is expected to happen from a statement error + // handler. In that case maintain the sense of |was_valid_| which + // previously held for this ref. + was_valid_ = was_valid_ && forced; } Connection::Connection() @@ -120,6 +119,7 @@ Connection::Connection() transaction_nesting_(0), needs_rollback_(false), in_memory_(false), + poisoned_(false), error_delegate_(NULL) { } @@ -127,7 +127,7 @@ Connection::~Connection() { Close(); } -bool Connection::Open(const FilePath& path) { +bool Connection::Open(const base::FilePath& path) { #if defined(OS_WIN) return OpenInternal(WideToUTF8(path.value())); #elif defined(OS_POSIX) @@ -140,22 +140,28 @@ bool Connection::OpenInMemory() { return OpenInternal(":memory:"); } -void Connection::Close() { +void Connection::CloseInternal(bool forced) { // TODO(shess): Calling "PRAGMA journal_mode = DELETE" at this point // will delete the -journal file. For ChromiumOS or other more // embedded systems, this is probably not appropriate, whereas on // desktop it might make some sense. // sqlite3_close() needs all prepared statements to be finalized. - // Release all cached statements, then assert that the client has - // released all statements. + + // Release cached statements. statement_cache_.clear(); - DCHECK(open_statements_.empty()); - // Additionally clear the prepared statements, because they contain - // weak references to this connection. This case has come up when - // error-handling code is hit in production. - ClearCache(); + // With cached statements released, in-use statements will remain. + // Closing the database while statements are in use is an API + // violation, except for forced close (which happens from within a + // statement's error handler). + DCHECK(forced || open_statements_.empty()); + + // Deactivate any outstanding statements so sqlite3_close() works. + for (StatementRefSet::iterator i = open_statements_.begin(); + i != open_statements_.end(); ++i) + (*i)->Close(forced); + open_statements_.clear(); if (db_) { // Call to AssertIOAllowed() cannot go at the beginning of the function @@ -171,11 +177,23 @@ void Connection::Close() { } } +void Connection::Close() { + // If the database was already closed by RazeAndClose(), then no + // need to close again. Clear the |poisoned_| bit so that incorrect + // API calls are caught. + if (poisoned_) { + poisoned_ = false; + return; + } + + CloseInternal(false); +} + void Connection::Preload() { AssertIOAllowed(); if (!db_) { - DLOG(FATAL) << "Cannot preload null db"; + DLOG_IF(FATAL, !poisoned_) << "Cannot preload null db"; return; } @@ -201,7 +219,7 @@ bool Connection::Raze() { AssertIOAllowed(); if (!db_) { - DLOG(FATAL) << "Cannot raze null db"; + DLOG_IF(FATAL, !poisoned_) << "Cannot raze null db"; return false; } @@ -222,7 +240,8 @@ bool Connection::Raze() { << " page_size_ " << page_size_ << " is not a power of two."; const int kSqliteMaxPageSize = 32768; // from sqliteLimit.h DCHECK_LE(page_size_, kSqliteMaxPageSize); - const std::string sql = StringPrintf("PRAGMA page_size=%d", page_size_); + const std::string sql = + base::StringPrintf("PRAGMA page_size=%d", page_size_); if (!null_db.Execute(sql.c_str())) return false; } @@ -291,7 +310,7 @@ bool Connection::Raze() { bool Connection::RazeWithTimout(base::TimeDelta timeout) { if (!db_) { - DLOG(FATAL) << "Cannot raze null db"; + DLOG_IF(FATAL, !poisoned_) << "Cannot raze null db"; return false; } @@ -300,6 +319,29 @@ bool Connection::RazeWithTimout(base::TimeDelta timeout) { return Raze(); } +bool Connection::RazeAndClose() { + if (!db_) { + DLOG_IF(FATAL, !poisoned_) << "Cannot raze null db"; + return false; + } + + // Raze() cannot run in a transaction. + while (transaction_nesting_) { + RollbackTransaction(); + } + + bool result = Raze(); + + CloseInternal(true); + + // Mark the database so that future API calls fail appropriately, + // but don't DCHECK (because after calling this function they are + // expected to fail). + poisoned_ = true; + + return result; +} + bool Connection::BeginTransaction() { if (needs_rollback_) { DCHECK_GT(transaction_nesting_, 0); @@ -323,7 +365,7 @@ bool Connection::BeginTransaction() { void Connection::RollbackTransaction() { if (!transaction_nesting_) { - DLOG(FATAL) << "Rolling back a nonexistent transaction"; + DLOG_IF(FATAL, !poisoned_) << "Rolling back a nonexistent transaction"; return; } @@ -340,7 +382,7 @@ void Connection::RollbackTransaction() { bool Connection::CommitTransaction() { if (!transaction_nesting_) { - DLOG(FATAL) << "Rolling back a nonexistent transaction"; + DLOG_IF(FATAL, !poisoned_) << "Rolling back a nonexistent transaction"; return false; } transaction_nesting_--; @@ -361,12 +403,19 @@ bool Connection::CommitTransaction() { int Connection::ExecuteAndReturnErrorCode(const char* sql) { AssertIOAllowed(); - if (!db_) - return false; + if (!db_) { + DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; + return SQLITE_ERROR; + } return sqlite3_exec(db_, sql, NULL, NULL, NULL); } bool Connection::Execute(const char* sql) { + if (!db_) { + DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; + return false; + } + int error = ExecuteAndReturnErrorCode(sql); if (error != SQLITE_OK) error = OnSqliteError(error, NULL); @@ -380,8 +429,10 @@ bool Connection::Execute(const char* sql) { } bool Connection::ExecuteWithTimeout(const char* sql, base::TimeDelta timeout) { - if (!db_) + if (!db_) { + DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; return false; + } ScopedBusyTimeout busy_timeout(db_); busy_timeout.SetTimeout(timeout); @@ -416,8 +467,9 @@ scoped_refptr<Connection::StatementRef> Connection::GetUniqueStatement( const char* sql) { AssertIOAllowed(); + // Return inactive statement. if (!db_) - return new StatementRef(); // Return inactive statement. + return new StatementRef(NULL, NULL, poisoned_); sqlite3_stmt* stmt = NULL; int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, NULL); @@ -427,28 +479,34 @@ scoped_refptr<Connection::StatementRef> Connection::GetUniqueStatement( // It could also be database corruption. OnSqliteError(rc, NULL); - return new StatementRef(); + return new StatementRef(NULL, NULL, false); } - return new StatementRef(this, stmt); + return new StatementRef(this, stmt, true); } scoped_refptr<Connection::StatementRef> Connection::GetUntrackedStatement( const char* sql) const { + // Return inactive statement. if (!db_) - return new StatementRef(); // Return inactive statement. + return new StatementRef(NULL, NULL, poisoned_); sqlite3_stmt* stmt = NULL; int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, NULL); if (rc != SQLITE_OK) { // This is evidence of a syntax error in the incoming SQL. DLOG(FATAL) << "SQL compile error " << GetErrorMessage(); - return new StatementRef(); + return new StatementRef(NULL, NULL, false); } - return new StatementRef(stmt); + return new StatementRef(NULL, stmt, true); } bool Connection::IsSQLValid(const char* sql) { AssertIOAllowed(); + if (!db_) { + DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; + return false; + } + sqlite3_stmt* stmt = NULL; if (sqlite3_prepare_v2(db_, sql, -1, &stmt, NULL) != SQLITE_OK) return false; @@ -491,7 +549,7 @@ bool Connection::DoesColumnExist(const char* table_name, int64 Connection::GetLastInsertRowId() const { if (!db_) { - DLOG(FATAL) << "Illegal use of connection without a db"; + DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; return 0; } return sqlite3_last_insert_rowid(db_); @@ -499,7 +557,7 @@ int64 Connection::GetLastInsertRowId() const { int Connection::GetLastChangeCount() const { if (!db_) { - DLOG(FATAL) << "Illegal use of connection without a db"; + DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; return 0; } return sqlite3_changes(db_); @@ -536,14 +594,36 @@ bool Connection::OpenInternal(const std::string& file_name) { return false; } + // If |poisoned_| is set, it means an error handler called + // RazeAndClose(). Until regular Close() is called, the caller + // should be treating the database as open, but is_open() currently + // only considers the sqlite3 handle's state. + // TODO(shess): Revise is_open() to consider poisoned_, and review + // to see if any non-testing code even depends on it. + DLOG_IF(FATAL, poisoned_) << "sql::Connection is already open."; + int err = sqlite3_open(file_name.c_str(), &db_); if (err != SQLITE_OK) { + // Histogram failures specific to initial open for debugging + // purposes. + UMA_HISTOGRAM_ENUMERATION("Sqlite.OpenFailure", err & 0xff, 50); + OnSqliteError(err, NULL); Close(); db_ = NULL; return false; } + // sqlite3_open() does not actually read the database file (unless a + // hot journal is found). Successfully executing this pragma on an + // existing database requires a valid header on page 1. + // TODO(shess): For now, just probing to see what the lay of the + // land is. If it's mostly SQLITE_NOTADB, then the database should + // be razed. + err = ExecuteAndReturnErrorCode("PRAGMA auto_vacuum"); + if (err != SQLITE_OK) + UMA_HISTOGRAM_ENUMERATION("Sqlite.OpenProbeFailure", err & 0xff, 50); + // 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 @@ -587,13 +667,15 @@ bool Connection::OpenInternal(const std::string& file_name) { << " page_size_ " << page_size_ << " is not a power of two."; const int kSqliteMaxPageSize = 32768; // from sqliteLimit.h DCHECK_LE(page_size_, kSqliteMaxPageSize); - const std::string sql = StringPrintf("PRAGMA page_size=%d", page_size_); + const std::string sql = + base::StringPrintf("PRAGMA page_size=%d", page_size_); if (!ExecuteWithTimeout(sql.c_str(), kBusyTimeout)) DLOG(FATAL) << "Could not set page size: " << GetErrorMessage(); } if (cache_size_ != 0) { - const std::string sql = StringPrintf("PRAGMA cache_size=%d", cache_size_); + const std::string sql = + base::StringPrintf("PRAGMA cache_size=%d", cache_size_); if (!ExecuteWithTimeout(sql.c_str(), kBusyTimeout)) DLOG(FATAL) << "Could not set cache size: " << GetErrorMessage(); } @@ -626,20 +708,35 @@ void Connection::StatementRefDeleted(StatementRef* ref) { 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) { + // Strip extended error codes. + int base_err = err&0xff; + + static size_t kSqliteErrorMax = 50; + UMA_HISTOGRAM_ENUMERATION("Sqlite.Error", base_err, kSqliteErrorMax); + if (!error_histogram_name_.empty()) { + // TODO(shess): The histogram macros create a bit of static + // storage for caching the histogram object. Since SQLite is + // being used for I/O, generally without error, this code + // shouldn't execute often enough for such caching to be crucial. + // If it becomes an issue, the object could be cached alongside + // error_histogram_name_. + base::HistogramBase* histogram = + base::LinearHistogram::FactoryGet( + error_histogram_name_, 1, kSqliteErrorMax, kSqliteErrorMax + 1, + base::HistogramBase::kUmaTargetedHistogramFlag); + if (histogram) + histogram->Add(base_err); + } + + // Always log the error. + LOG(ERROR) << "sqlite error " << err + << ", errno " << GetLastErrno() + << ": " << GetErrorMessage(); + if (error_delegate_.get()) return error_delegate_->OnError(err, this, stmt); + // The default handling is to assert on debug and to ignore on release. DLOG(FATAL) << GetErrorMessage(); return err; diff --git a/sql/connection.h b/sql/connection.h index 8cf4d715f5..cebc774fa5 100644 --- a/sql/connection.h +++ b/sql/connection.h @@ -17,10 +17,13 @@ #include "base/time.h" #include "sql/sql_export.h" -class FilePath; struct sqlite3; struct sqlite3_stmt; +namespace base { +class FilePath; +} + namespace sql { class Statement; @@ -143,18 +146,25 @@ class SQL_EXPORT Connection { error_delegate_.reset(delegate); } + // SQLite error codes for errors on all connections are logged to + // enum histogram "Sqlite.Error". Setting this additionally logs + // errors to the histogram |name|. + void set_error_histogram_name(const std::string& name) { + error_histogram_name_ = name; + } + // 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) WARN_UNUSED_RESULT; + bool Open(const base::FilePath& path) WARN_UNUSED_RESULT; // 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() WARN_UNUSED_RESULT; - // Returns trie if the database has been successfully opened. + // Returns true if the database has been successfully opened. bool is_open() const { return !!db_; } // Closes the database. This is automatically performed on destruction for @@ -211,6 +221,16 @@ class SQL_EXPORT Connection { bool Raze(); bool RazeWithTimout(base::TimeDelta timeout); + // Breaks all outstanding transactions (as initiated by + // BeginTransaction()), calls Raze() to destroy the database, then + // closes the database. After this is called, any operations + // against the connections (or statements prepared by the + // connection) should fail safely. + // + // The value from Raze() is returned, with Close() called in all + // cases. + bool RazeAndClose(); + // Transactions -------------------------------------------------------------- // Transaction management. We maintain a virtual transaction stack to emulate @@ -331,6 +351,10 @@ class SQL_EXPORT Connection { // sqlite3_open. The string can also be sqlite's special ":memory:" string. bool OpenInternal(const std::string& file_name); + // Internal close function used by Close() and RazeAndClose(). + // |forced| indicates that orderly-shutdown checks should not apply. + void CloseInternal(bool forced); + // Check whether the current thread is allowed to make IO calls, but only // if database wasn't open in memory. Function is inlined to be a no-op in // official build. @@ -355,16 +379,27 @@ class SQL_EXPORT Connection { // should always check validity before using. class SQL_EXPORT StatementRef : public base::RefCounted<StatementRef> { public: - // Default constructor initializes to an invalid statement. - StatementRef(); - explicit StatementRef(sqlite3_stmt* stmt); - StatementRef(Connection* connection, sqlite3_stmt* stmt); + // |connection| is the sql::Connection instance associated with + // the statement, and is used for tracking outstanding statements + // and for error handling. Set to NULL for invalid or untracked + // refs. |stmt| is the actual statement, and should only be NULL + // to create an invalid ref. |was_valid| indicates whether the + // statement should be considered valid for diagnistic purposes. + // |was_valid| can be true for NULL |stmt| if the connection has + // been forcibly closed by an error handler. + StatementRef(Connection* connection, sqlite3_stmt* stmt, bool was_valid); // 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(). + // When true, the statement is either currently valid, or was + // previously valid but the connection was forcibly closed. Used + // for diagnostic checks. + bool was_valid() const { return was_valid_; } + + // If we've not been linked to a connection, this will be NULL. + // TODO(shess): connection_ can be NULL in case of GetUntrackedStatement(), + // which prevents Statement::OnError() from forwarding errors. Connection* connection() const { return connection_; } // Returns the sqlite statement if any. If the statement is not active, @@ -372,8 +407,9 @@ class SQL_EXPORT Connection { sqlite3_stmt* stmt() const { return stmt_; } // Destroys the compiled statement and marks it NULL. The statement will - // no longer be active. - void Close(); + // no longer be active. |forced| is used to indicate if orderly-shutdown + // checks should apply (see Connection::RazeAndClose()). + void Close(bool forced); // Check whether the current thread is allowed to make IO calls, but only // if database wasn't open in memory. @@ -386,6 +422,7 @@ class SQL_EXPORT Connection { Connection* connection_; sqlite3_stmt* stmt_; + bool was_valid_; DISALLOW_COPY_AND_ASSIGN(StatementRef); }; @@ -400,9 +437,6 @@ class SQL_EXPORT Connection { 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); @@ -453,10 +487,19 @@ class SQL_EXPORT Connection { // with Open(). bool in_memory_; + // |true| if the connection was closed using RazeAndClose(). Used + // to enable diagnostics to distinguish calls to never-opened + // databases (incorrect use of the API) from calls to once-valid + // databases. + bool poisoned_; + // This object handles errors resulting from all forms of executing sqlite // commands or statements. It can be null which means default handling. scoped_ptr<ErrorDelegate> error_delegate_; + // Auxiliary error-code histogram. + std::string error_histogram_name_; + DISALLOW_COPY_AND_ASSIGN(Connection); }; diff --git a/sql/connection_unittest.cc b/sql/connection_unittest.cc index 8036409613..2aaeb27b33 100644 --- a/sql/connection_unittest.cc +++ b/sql/connection_unittest.cc @@ -3,10 +3,11 @@ // found in the LICENSE file. #include "base/file_util.h" -#include "base/scoped_temp_dir.h" +#include "base/files/scoped_temp_dir.h" +#include "base/logging.h" #include "sql/connection.h" -#include "sql/statement.h" #include "sql/meta_table.h" +#include "sql/statement.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/sqlite/sqlite3.h" @@ -14,23 +15,23 @@ class SQLConnectionTest : public testing::Test { public: SQLConnectionTest() {} - void SetUp() { + virtual void SetUp() { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); ASSERT_TRUE(db_.Open(db_path())); } - void TearDown() { + virtual void TearDown() { db_.Close(); } sql::Connection& db() { return db_; } - FilePath db_path() { + base::FilePath db_path() { return temp_dir_.path().AppendASCII("SQLConnectionTest.db"); } private: - ScopedTempDir temp_dir_; + base::ScopedTempDir temp_dir_; sql::Connection db_; }; @@ -279,6 +280,111 @@ TEST_F(SQLConnectionTest, RazeLocked) { ASSERT_TRUE(db().Raze()); } +// Basic test of RazeAndClose() operation. +TEST_F(SQLConnectionTest, RazeAndClose) { + const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)"; + const char* kPopulateSql = "INSERT INTO foo (value) VALUES (12)"; + + // Test that RazeAndClose() closes the database, and that the + // database is empty when re-opened. + ASSERT_TRUE(db().Execute(kCreateSql)); + ASSERT_TRUE(db().Execute(kPopulateSql)); + ASSERT_TRUE(db().RazeAndClose()); + ASSERT_FALSE(db().is_open()); + db().Close(); + ASSERT_TRUE(db().Open(db_path())); + { + sql::Statement s(db().GetUniqueStatement("SELECT * FROM sqlite_master")); + ASSERT_FALSE(s.Step()); + } + + // Test that RazeAndClose() can break transactions. + ASSERT_TRUE(db().Execute(kCreateSql)); + ASSERT_TRUE(db().Execute(kPopulateSql)); + ASSERT_TRUE(db().BeginTransaction()); + ASSERT_TRUE(db().RazeAndClose()); + ASSERT_FALSE(db().is_open()); + ASSERT_FALSE(db().CommitTransaction()); + db().Close(); + ASSERT_TRUE(db().Open(db_path())); + { + sql::Statement s(db().GetUniqueStatement("SELECT * FROM sqlite_master")); + ASSERT_FALSE(s.Step()); + } +} + +// Test that various operations fail without crashing after +// RazeAndClose(). +TEST_F(SQLConnectionTest, RazeAndCloseDiagnostics) { + const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)"; + const char* kPopulateSql = "INSERT INTO foo (value) VALUES (12)"; + const char* kSimpleSql = "SELECT 1"; + + ASSERT_TRUE(db().Execute(kCreateSql)); + ASSERT_TRUE(db().Execute(kPopulateSql)); + + // Test baseline expectations. + db().Preload(); + ASSERT_TRUE(db().DoesTableExist("foo")); + ASSERT_TRUE(db().IsSQLValid(kSimpleSql)); + ASSERT_EQ(SQLITE_OK, db().ExecuteAndReturnErrorCode(kSimpleSql)); + ASSERT_TRUE(db().Execute(kSimpleSql)); + ASSERT_TRUE(db().is_open()); + { + sql::Statement s(db().GetUniqueStatement(kSimpleSql)); + ASSERT_TRUE(s.Step()); + } + { + sql::Statement s(db().GetCachedStatement(SQL_FROM_HERE, kSimpleSql)); + ASSERT_TRUE(s.Step()); + } + ASSERT_TRUE(db().BeginTransaction()); + ASSERT_TRUE(db().CommitTransaction()); + ASSERT_TRUE(db().BeginTransaction()); + db().RollbackTransaction(); + + ASSERT_TRUE(db().RazeAndClose()); + + // At this point, they should all fail, but not crash. + db().Preload(); + ASSERT_FALSE(db().DoesTableExist("foo")); + ASSERT_FALSE(db().IsSQLValid(kSimpleSql)); + ASSERT_EQ(SQLITE_ERROR, db().ExecuteAndReturnErrorCode(kSimpleSql)); + ASSERT_FALSE(db().Execute(kSimpleSql)); + ASSERT_FALSE(db().is_open()); + { + sql::Statement s(db().GetUniqueStatement(kSimpleSql)); + ASSERT_FALSE(s.Step()); + } + { + sql::Statement s(db().GetCachedStatement(SQL_FROM_HERE, kSimpleSql)); + ASSERT_FALSE(s.Step()); + } + ASSERT_FALSE(db().BeginTransaction()); + ASSERT_FALSE(db().CommitTransaction()); + ASSERT_FALSE(db().BeginTransaction()); + db().RollbackTransaction(); + + // Close normally to reset the poisoned flag. + db().Close(); + + // DEATH tests not supported on Android or iOS. +#if !defined(OS_ANDROID) && !defined(OS_IOS) + // Once the real Close() has been called, various calls enforce API + // usage by becoming fatal in debug mode. Since DEATH tests are + // expensive, just test one of them. + if (DLOG_IS_ON(FATAL)) { + ASSERT_DEATH({ + db().IsSQLValid(kSimpleSql); + }, "Illegal use of connection without a db"); + } +#endif +} + +// TODO(shess): Spin up a background thread to hold other_db, to more +// closely match real life. That would also allow testing +// RazeWithTimeout(). + #if defined(OS_ANDROID) TEST_F(SQLConnectionTest, SetTempDirForSQL) { @@ -291,7 +397,3 @@ TEST_F(SQLConnectionTest, SetTempDirForSQL) { ASSERT_TRUE(meta_table.Init(&db(), 4, 4)); } #endif - -// TODO(shess): Spin up a background thread to hold other_db, to more -// closely match real life. That would also allow testing -// RazeWithTimeout(). diff --git a/sql/run_all_unittests.cc b/sql/run_all_unittests.cc index 969b09164a..2c8d29c6d0 100644 --- a/sql/run_all_unittests.cc +++ b/sql/run_all_unittests.cc @@ -2,10 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/test/main_hook.h" #include "base/test/test_suite.h" int main(int argc, char** argv) { - MainHook hook(main, argc, argv); return base::TestSuite(argc, argv).Run(); } diff --git a/sql/sql.gyp b/sql/sql.gyp index 86962b3d02..0d8f8be967 100644 --- a/sql/sql.gyp +++ b/sql/sql.gyp @@ -29,6 +29,8 @@ 'transaction.cc', 'transaction.h', ], + # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. + 'msvs_disabled_warnings': [4267, ], }, { 'target_name': 'sql_unittests', @@ -64,6 +66,8 @@ ], }], ], + # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. + 'msvs_disabled_warnings': [4267, ], }, ], 'conditions': [ diff --git a/sql/sql.target.mk b/sql/sql.target.linux-arm.mk index 029beef85b..046707eff9 100644 --- a/sql/sql.target.mk +++ b/sql/sql.target.linux-arm.mk @@ -32,6 +32,8 @@ LOCAL_SRC_FILES := \ # Flags passed to both C and C++ files. MY_CFLAGS := \ + -fstack-protector \ + --param=ssp-buffer-size=4 \ -Werror \ -fno-exceptions \ -fno-strict-aliasing \ @@ -41,15 +43,9 @@ MY_CFLAGS := \ -fvisibility=hidden \ -pipe \ -fPIC \ - -mthumb \ - -march=armv7-a \ - -mtune=cortex-a8 \ - -mfloat-abi=softfp \ - -mfpu=vfpv3-d16 \ -fno-tree-sra \ -fuse-ld=gold \ -Wno-psabi \ - -mthumb-interwork \ -ffunction-sections \ -funwind-tables \ -g \ @@ -57,11 +53,10 @@ MY_CFLAGS := \ -fno-short-enums \ -finline-limit=64 \ -Wa,--noexecstack \ - -Wno-error=extra \ - -Wno-error=ignored-qualifiers \ - -Wno-error=type-limits \ - -Wno-error=non-virtual-dtor \ - -Wno-error=sign-promo \ + -U_FORTIFY_SOURCE \ + -Wno-extra \ + -Wno-ignored-qualifiers \ + -Wno-type-limits \ -Os \ -g \ -fomit-frame-pointer \ @@ -71,7 +66,9 @@ MY_CFLAGS := \ MY_CFLAGS_C := MY_DEFS := \ + '-DUSE_SKIA' \ '-D_FILE_OFFSET_BITS=64' \ + '-DUSE_LINUX_BREAKPAD' \ '-DNO_TCMALLOC' \ '-DDISABLE_NACL' \ '-DCHROMIUM_BUILD' \ @@ -81,7 +78,7 @@ MY_DEFS := \ '-DENABLE_GPU=1' \ '-DUSE_OPENSSL=1' \ '-DENABLE_EGLIMAGE=1' \ - '-DUSE_SKIA=1' \ + '-DENABLE_LANGUAGE_DETECTION=1' \ '-DSQL_IMPLEMENTATION' \ '-D__STDC_CONSTANT_MACROS' \ '-D__STDC_FORMAT_MACROS' \ @@ -89,8 +86,7 @@ MY_DEFS := \ '-D__GNU_SOURCE=1' \ '-DUSE_STLPORT=1' \ '-D_STLP_USE_PTR_SPECIALIZATIONS=1' \ - '-DCHROME_SYMBOLS_ID=""' \ - '-DANDROID_UPSTREAM_BRINGUP=1' \ + '-DCHROME_BUILD_ID=""' \ '-DDYNAMIC_ANNOTATIONS_ENABLED=1' \ '-DWTF_USE_DYNAMIC_ANNOTATIONS=1' \ '-D_DEBUG' @@ -99,6 +95,8 @@ LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) # Include paths placed before CFLAGS/CPPFLAGS LOCAL_C_INCLUDES := \ + $(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \ + $(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \ $(LOCAL_PATH) \ $(LOCAL_PATH)/third_party/sqlite \ $(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \ @@ -114,11 +112,15 @@ LOCAL_CPPFLAGS := \ -fvisibility-inlines-hidden \ -Wsign-compare \ -Wno-abi \ - -Wno-error=c++0x-compat + -Wno-error=c++0x-compat \ + -Wno-non-virtual-dtor \ + -Wno-sign-promo ### Rules for final target. LOCAL_LDFLAGS := \ + -Wl,-z,now \ + -Wl,-z,relro \ -Wl,-z,noexecstack \ -fPIC \ -Wl,-z,relro \ diff --git a/sql/sql.target.linux-x86.mk b/sql/sql.target.linux-x86.mk new file mode 100644 index 0000000000..1b5acd2dd0 --- /dev/null +++ b/sql/sql.target.linux-x86.mk @@ -0,0 +1,152 @@ +# This file is generated by gyp; do not edit. + +include $(CLEAR_VARS) + +LOCAL_MODULE_CLASS := STATIC_LIBRARIES +LOCAL_MODULE := sql_sql_gyp +LOCAL_MODULE_SUFFIX := .a +LOCAL_MODULE_TAGS := optional +gyp_intermediate_dir := $(call local-intermediates-dir) +gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared) + +# Make sure our deps are built first. +GYP_TARGET_DEPENDENCIES := + +GYP_GENERATED_OUTPUTS := + +# Make sure our deps and generated files are built first. +LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS) + +LOCAL_CPP_EXTENSION := .cc +LOCAL_GENERATED_SOURCES := + +GYP_COPIED_SOURCE_ORIGIN_DIRS := + +LOCAL_SRC_FILES := \ + sql/connection.cc \ + sql/error_delegate_util.cc \ + sql/meta_table.cc \ + sql/statement.cc \ + sql/transaction.cc + + +# Flags passed to both C and C++ files. +MY_CFLAGS := \ + --param=ssp-buffer-size=4 \ + -Werror \ + -fno-exceptions \ + -fno-strict-aliasing \ + -Wall \ + -Wno-unused-parameter \ + -Wno-missing-field-initializers \ + -fvisibility=hidden \ + -pipe \ + -fPIC \ + -m32 \ + -mmmx \ + -march=pentium4 \ + -msse2 \ + -mfpmath=sse \ + -ffunction-sections \ + -funwind-tables \ + -g \ + -fno-short-enums \ + -finline-limit=64 \ + -Wa,--noexecstack \ + -U_FORTIFY_SOURCE \ + -Wno-extra \ + -Wno-ignored-qualifiers \ + -Wno-type-limits \ + -fno-stack-protector \ + -Os \ + -g \ + -fomit-frame-pointer \ + -fdata-sections \ + -ffunction-sections + +MY_CFLAGS_C := + +MY_DEFS := \ + '-DUSE_SKIA' \ + '-D_FILE_OFFSET_BITS=64' \ + '-DUSE_LINUX_BREAKPAD' \ + '-DNO_TCMALLOC' \ + '-DDISABLE_NACL' \ + '-DCHROMIUM_BUILD' \ + '-DUSE_LIBJPEG_TURBO=1' \ + '-DUSE_PROPRIETARY_CODECS' \ + '-DENABLE_PEPPER_THREADING' \ + '-DENABLE_GPU=1' \ + '-DUSE_OPENSSL=1' \ + '-DENABLE_EGLIMAGE=1' \ + '-DENABLE_LANGUAGE_DETECTION=1' \ + '-DSQL_IMPLEMENTATION' \ + '-D__STDC_CONSTANT_MACROS' \ + '-D__STDC_FORMAT_MACROS' \ + '-DANDROID' \ + '-D__GNU_SOURCE=1' \ + '-DUSE_STLPORT=1' \ + '-D_STLP_USE_PTR_SPECIALIZATIONS=1' \ + '-DCHROME_BUILD_ID=""' \ + '-DDYNAMIC_ANNOTATIONS_ENABLED=1' \ + '-DWTF_USE_DYNAMIC_ANNOTATIONS=1' \ + '-D_DEBUG' + +LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) + +# Include paths placed before CFLAGS/CPPFLAGS +LOCAL_C_INCLUDES := \ + $(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \ + $(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \ + $(LOCAL_PATH) \ + $(LOCAL_PATH)/third_party/sqlite \ + $(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \ + $(GYP_ABS_ANDROID_TOP_DIR)/bionic \ + $(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport + +LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES) + +# Flags passed to only C++ (and not C) files. +LOCAL_CPPFLAGS := \ + -fno-rtti \ + -fno-threadsafe-statics \ + -fvisibility-inlines-hidden \ + -Wsign-compare \ + -Wno-error=c++0x-compat \ + -Wno-non-virtual-dtor \ + -Wno-sign-promo + +### Rules for final target. + +LOCAL_LDFLAGS := \ + -Wl,-z,now \ + -Wl,-z,relro \ + -Wl,-z,noexecstack \ + -fPIC \ + -m32 \ + -nostdlib \ + -Wl,--no-undefined \ + -Wl,--exclude-libs=ALL \ + -Wl,-O1 \ + -Wl,--as-needed \ + -Wl,--gc-sections + + +LOCAL_STATIC_LIBRARIES := + +# Enable grouping to fix circular references +LOCAL_GROUP_STATIC_LIBRARIES := true + +LOCAL_SHARED_LIBRARIES := \ + libstlport \ + libdl + +# Add target alias to "gyp_all_modules" target. +.PHONY: gyp_all_modules +gyp_all_modules: sql_sql_gyp + +# Alias gyp target name. +.PHONY: sql +sql: sql_sql_gyp + +include $(BUILD_STATIC_LIBRARY) diff --git a/sql/sqlite_features_unittest.cc b/sql/sqlite_features_unittest.cc index 7e750e97c6..2f6a49eb48 100644 --- a/sql/sqlite_features_unittest.cc +++ b/sql/sqlite_features_unittest.cc @@ -5,7 +5,7 @@ #include <string> #include "base/file_util.h" -#include "base/scoped_temp_dir.h" +#include "base/files/scoped_temp_dir.h" #include "sql/connection.h" #include "sql/statement.h" #include "testing/gtest/include/gtest/gtest.h" @@ -42,7 +42,7 @@ class SQLiteFeaturesTest : public testing::Test { public: SQLiteFeaturesTest() : error_(SQLITE_OK) {} - void SetUp() { + virtual void SetUp() { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); ASSERT_TRUE(db_.Open(temp_dir_.path().AppendASCII("SQLStatementTest.db"))); @@ -51,7 +51,7 @@ class SQLiteFeaturesTest : public testing::Test { db_.set_error_delegate(new StatementErrorHandler(&error_, &sql_text_)); } - void TearDown() { + virtual void TearDown() { // If any error happened the original sql statement can be found in // |sql_text_|. EXPECT_EQ(SQLITE_OK, error_); @@ -65,7 +65,7 @@ class SQLiteFeaturesTest : public testing::Test { } private: - ScopedTempDir temp_dir_; + base::ScopedTempDir temp_dir_; sql::Connection db_; // The error code of the most recent error. diff --git a/sql/statement.cc b/sql/statement.cc index 84dfd2eb9a..d92be010fd 100644 --- a/sql/statement.cc +++ b/sql/statement.cc @@ -15,7 +15,7 @@ namespace sql { // 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), + : ref_(new Connection::StatementRef(NULL, NULL, false)), succeeded_(false) { } @@ -37,13 +37,15 @@ void Statement::Assign(scoped_refptr<Connection::StatementRef> ref) { } void Statement::Clear() { - Assign(new Connection::StatementRef); + Assign(new Connection::StatementRef(NULL, NULL, false)); succeeded_ = false; } bool Statement::CheckValid() const { - if (!is_valid()) - DLOG(FATAL) << "Cannot call mutating statements on an invalid statement."; + // Allow operations to fail silently if a statement was invalidated + // because the database was closed by an error handler. + DLOG_IF(FATAL, !ref_->was_valid()) + << "Cannot call mutating statements on an invalid statement."; return is_valid(); } @@ -306,7 +308,7 @@ bool Statement::CheckOk(int err) const { 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()) + if (!succeeded_ && ref_ && ref_->connection()) return ref_->connection()->OnSqliteError(err, this); return err; } diff --git a/sql/statement_unittest.cc b/sql/statement_unittest.cc index a7a23d8294..ffa8aef1f8 100644 --- a/sql/statement_unittest.cc +++ b/sql/statement_unittest.cc @@ -5,7 +5,7 @@ #include <string> #include "base/file_util.h" -#include "base/scoped_temp_dir.h" +#include "base/files/scoped_temp_dir.h" #include "sql/connection.h" #include "sql/statement.h" #include "testing/gtest/include/gtest/gtest.h" @@ -40,7 +40,7 @@ class SQLStatementTest : public testing::Test { public: SQLStatementTest() : error_(SQLITE_OK) {} - void SetUp() { + virtual void SetUp() { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); ASSERT_TRUE(db_.Open(temp_dir_.path().AppendASCII("SQLStatementTest.db"))); // The error delegate will set |error_| and |sql_text_| when any sqlite @@ -48,7 +48,7 @@ class SQLStatementTest : public testing::Test { db_.set_error_delegate(new StatementErrorHandler(&error_, &sql_text_)); } - void TearDown() { + virtual void TearDown() { // If any error happened the original sql statement can be found in // |sql_text_|. EXPECT_EQ(SQLITE_OK, error_); @@ -65,7 +65,7 @@ class SQLStatementTest : public testing::Test { } private: - ScopedTempDir temp_dir_; + base::ScopedTempDir temp_dir_; sql::Connection db_; // The error code of the most recent error. diff --git a/sql/transaction_unittest.cc b/sql/transaction_unittest.cc index f306a5cb6e..ceaa4dbd7d 100644 --- a/sql/transaction_unittest.cc +++ b/sql/transaction_unittest.cc @@ -3,7 +3,7 @@ // found in the LICENSE file. #include "base/file_util.h" -#include "base/scoped_temp_dir.h" +#include "base/files/scoped_temp_dir.h" #include "sql/connection.h" #include "sql/statement.h" #include "sql/transaction.h" @@ -14,7 +14,7 @@ class SQLTransactionTest : public testing::Test { public: SQLTransactionTest() {} - void SetUp() { + virtual void SetUp() { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); ASSERT_TRUE(db_.Open( temp_dir_.path().AppendASCII("SQLTransactionTest.db"))); @@ -22,7 +22,7 @@ class SQLTransactionTest : public testing::Test { ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); } - void TearDown() { + virtual void TearDown() { db_.Close(); } @@ -36,7 +36,7 @@ class SQLTransactionTest : public testing::Test { } private: - ScopedTempDir temp_dir_; + base::ScopedTempDir temp_dir_; sql::Connection db_; }; |