diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-11 21:30:56 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-11 21:30:56 +0000 |
commit | e5ffd0e471417e75ddcd5af20c3254c0ec2f1f5d (patch) | |
tree | 60e8d7c1de4ee33cc063cfa98a168e8fe9fcf27b /chrome/browser/history | |
parent | 92c3dc6b3fffbaf9fa2fa409120ca051bf317234 (diff) | |
download | chromium_src-e5ffd0e471417e75ddcd5af20c3254c0ec2f1f5d.zip chromium_src-e5ffd0e471417e75ddcd5af20c3254c0ec2f1f5d.tar.gz chromium_src-e5ffd0e471417e75ddcd5af20c3254c0ec2f1f5d.tar.bz2 |
Add a new wrapper for sqlite. This is mostly a large cleanup of the existing
one, combined with the statement cache in a nice way. It is designed to
entirely wrap sqlite so that we can catch corrupt errors in the future and
"do something" when we get them without having to change all the calling code.
There is also a new meta_table file which is almost exactly like the old one
but which uses the new sql interface.
This patch changes Chrome's history TextDatabase to use this new wrapper as a
proof of concept, because this usage is relatively well-confined.
Review URL: http://codereview.chromium.org/199047
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@26022 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/history')
-rw-r--r-- | chrome/browser/history/text_database.cc | 205 | ||||
-rw-r--r-- | chrome/browser/history/text_database.h | 29 |
2 files changed, 100 insertions, 134 deletions
diff --git a/chrome/browser/history/text_database.cc b/chrome/browser/history/text_database.cc index 5ffdc95..d56788ca 100644 --- a/chrome/browser/history/text_database.cc +++ b/chrome/browser/history/text_database.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// 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. @@ -7,11 +7,12 @@ #include "chrome/browser/history/text_database.h" +#include "app/sql/connection.h" +#include "app/sql/statement.h" +#include "app/sql/transaction.h" #include "base/file_util.h" #include "base/logging.h" #include "base/string_util.h" -#include "chrome/common/sqlite_compiled_statement.h" -#include "chrome/common/sqlite_utils.h" // There are two tables in each database, one full-text search (FTS) table which // indexes the contents and title of the pages. The other is a regular SQLITE @@ -51,48 +52,19 @@ const char kBodyColumnIndex[] = "2"; // The string prepended to the database identifier to generate the filename. const FilePath::CharType kFilePrefix[] = FILE_PATH_LITERAL("History Index "); -// We do not allow rollback, but this simple scoper makes it easy to always -// remember to commit a begun transaction. This protects against some errors -// caused by a crash in the middle of a transaction, although doesn't give us -// the full protection of a transaction's rollback abilities. -class ScopedTransactionCommitter { - public: - ScopedTransactionCommitter(TextDatabase* db) : db_(db) { - db_->BeginTransaction(); - } - ~ScopedTransactionCommitter() { - db_->CommitTransaction(); - } - private: - TextDatabase* db_; -}; - - } // namespace TextDatabase::TextDatabase(const FilePath& path, DBIdent id, bool allow_create) - : db_(NULL), - statement_cache_(NULL), - path_(path), + : path_(path), ident_(id), - allow_create_(allow_create), - transaction_nesting_(0) { + allow_create_(allow_create) { // Compute the file name. file_name_ = path_.Append(IDToFileName(ident_)); } TextDatabase::~TextDatabase() { - if (statement_cache_) { - // Must release these statements before closing the DB. - delete statement_cache_; - statement_cache_ = NULL; - } - if (db_) { - sqlite3_close(db_); - db_ = NULL; - } } // static @@ -144,30 +116,28 @@ bool TextDatabase::Init() { return false; } - // Attach the database to our index file. - if (OpenSqliteDb(file_name_, &db_) != SQLITE_OK) - return false; - statement_cache_ = new SqliteStatementCache(db_); - // Set the database page size to something a little larger to give us // better performance (we're typically seek rather than bandwidth limited). // This only has an effect before any tables have been created, otherwise // this is a NOP. Must be a power of 2 and a max of 8192. - sqlite3_exec(db_, "PRAGMA page_size=4096", NULL, NULL, NULL); + db_.set_page_size(2096); // The default cache size is 2000 which give >8MB of data. Since we will often // have 2-3 of these objects, each with their own 8MB, this adds up very fast. // We therefore reduce the size so when there are multiple objects, we're not // too big. - sqlite3_exec(db_, "PRAGMA cache_size=512", NULL, NULL, NULL); + db_.set_cache_size(512); // Run the database in exclusive mode. Nobody else should be accessing the // database while we're running, and this will give somewhat improved perf. - sqlite3_exec(db_, "PRAGMA locking_mode=EXCLUSIVE", NULL, NULL, NULL); + db_.set_exclusive_locking(); + + // Attach the database to our index file. + if (!db_.Init(file_name_)) + return false; // Meta table tracking version information. - if (!meta_table_.Init(std::string(), kCurrentVersionNumber, - kCompatibleVersionNumber, db_)) + if (!meta_table_.Init(&db_, kCurrentVersionNumber, kCompatibleVersionNumber)) return false; if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) { // This version is too new. We don't bother notifying the user on this @@ -184,46 +154,39 @@ bool TextDatabase::Init() { } void TextDatabase::BeginTransaction() { - if (!transaction_nesting_) - sqlite3_exec(db_, "BEGIN TRANSACTION", NULL, NULL, NULL); - transaction_nesting_++; + db_.BeginTransaction(); } void TextDatabase::CommitTransaction() { - DCHECK(transaction_nesting_); - transaction_nesting_--; - if (!transaction_nesting_) - sqlite3_exec(db_, "COMMIT", NULL, NULL, NULL); + db_.CommitTransaction(); } bool TextDatabase::CreateTables() { // FTS table of page contents. - if (!DoesSqliteTableExist(db_, "pages")) { - if (sqlite3_exec(db_, - "CREATE VIRTUAL TABLE pages USING fts2(" + if (!db_.DoesTableExist("pages")) { + if (!db_.Execute("CREATE VIRTUAL TABLE pages USING fts2(" "TOKENIZE icu," "url LONGVARCHAR," "title LONGVARCHAR," - "body LONGVARCHAR)", NULL, NULL, NULL) != SQLITE_OK) + "body LONGVARCHAR)")) return false; } // Non-FTS table containing URLs and times so we can efficiently find them // using a regular index (all FTS columns are special and are treated as // full-text-search, which is not what we want when retrieving this data). - if (!DoesSqliteTableExist(db_, "info")) { + if (!db_.DoesTableExist("info")) { // Note that there is no point in creating an index over time. Since // we must always query the entire FTS table (it can not efficiently do // subsets), we will always end up doing that first, and joining the info // table off of that. - if (sqlite3_exec(db_, "CREATE TABLE info(time INTEGER NOT NULL)", - NULL, NULL, NULL) != SQLITE_OK) + if (!db_.Execute("CREATE TABLE info(time INTEGER NOT NULL)")) return false; } // Create the index. This will fail when the index already exists, so we just // ignore the error. - sqlite3_exec(db_, "CREATE INDEX info_time ON info(time)", NULL, NULL, NULL); + db_.Execute("CREATE INDEX info_time ON info(time)"); return true; } @@ -231,85 +194,97 @@ bool TextDatabase::AddPageData(Time time, const std::string& url, const std::string& title, const std::string& contents) { - ScopedTransactionCommitter committer(this); + sql::Transaction committer(&db_); + if (!committer.Begin()) + return false; // Add to the pages table. - SQLITE_UNIQUE_STATEMENT(add_to_pages, *statement_cache_, - "INSERT INTO pages (url, title, body) VALUES (?,?,?)"); - if (!add_to_pages.is_valid()) + sql::Statement add_to_pages(db_.GetCachedStatement(SQL_FROM_HERE, + "INSERT INTO pages (url, title, body) VALUES (?,?,?)")); + if (!add_to_pages) { + NOTREACHED() << db_.GetErrorMessage(); return false; - add_to_pages->bind_string(0, url); - add_to_pages->bind_string(1, title); - add_to_pages->bind_string(2, contents); - if (add_to_pages->step() != SQLITE_DONE) { - NOTREACHED() << sqlite3_errmsg(db_); + } + add_to_pages.BindString(0, url); + add_to_pages.BindString(1, title); + add_to_pages.BindString(2, contents); + if (!add_to_pages.Run()) { + NOTREACHED() << db_.GetErrorMessage(); return false; } - int64 rowid = sqlite3_last_insert_rowid(db_); + int64 rowid = db_.GetLastInsertRowId(); // Add to the info table with the same rowid. - SQLITE_UNIQUE_STATEMENT(add_to_info, *statement_cache_, - "INSERT INTO info (rowid, time) VALUES (?,?)"); - if (!add_to_info.is_valid()) + sql::Statement add_to_info(db_.GetCachedStatement(SQL_FROM_HERE, + "INSERT INTO info (rowid, time) VALUES (?,?)")); + if (!add_to_info) { + NOTREACHED() << db_.GetErrorMessage(); return false; - add_to_info->bind_int64(0, rowid); - add_to_info->bind_int64(1, time.ToInternalValue()); - if (add_to_info->step() != SQLITE_DONE) { - NOTREACHED() << sqlite3_errmsg(db_); + } + add_to_info.BindInt64(0, rowid); + add_to_info.BindInt64(1, time.ToInternalValue()); + if (!add_to_info.Run()) { + NOTREACHED() << db_.GetErrorMessage(); return false; } - return true; + return committer.Commit(); } void TextDatabase::DeletePageData(Time time, const std::string& url) { // First get all rows that match. Selecing on time (which has an index) allows // us to avoid brute-force searches on the full-text-index table (there will // generally be only one match per time). - SQLITE_UNIQUE_STATEMENT(select_ids, *statement_cache_, + sql::Statement select_ids(db_.GetCachedStatement(SQL_FROM_HERE, "SELECT info.rowid " "FROM info JOIN pages ON info.rowid = pages.rowid " - "WHERE info.time=? AND pages.url=?"); - if (!select_ids.is_valid()) + "WHERE info.time=? AND pages.url=?")); + if (!select_ids) return; - select_ids->bind_int64(0, time.ToInternalValue()); - select_ids->bind_string(1, url); + select_ids.BindInt64(0, time.ToInternalValue()); + select_ids.BindString(1, url); std::set<int64> rows_to_delete; - while (select_ids->step() == SQLITE_ROW) - rows_to_delete.insert(select_ids->column_int64(0)); + while (select_ids.Step()) + rows_to_delete.insert(select_ids.ColumnInt64(0)); // Delete from the pages table. - SQLITE_UNIQUE_STATEMENT(delete_page, *statement_cache_, - "DELETE FROM pages WHERE rowid=?"); - if (!delete_page.is_valid()) + sql::Statement delete_page(db_.GetCachedStatement(SQL_FROM_HERE, + "DELETE FROM pages WHERE rowid=?")); + if (!delete_page) return; for (std::set<int64>::const_iterator i = rows_to_delete.begin(); i != rows_to_delete.end(); ++i) { - delete_page->bind_int64(0, *i); - delete_page->step(); - delete_page->reset(); + delete_page.BindInt64(0, *i); + if (!delete_page.Run()) { + NOTREACHED(); + return; + } + delete_page.Reset(); } // Delete from the info table. - SQLITE_UNIQUE_STATEMENT(delete_info, *statement_cache_, - "DELETE FROM info WHERE rowid=?"); - if (!delete_info.is_valid()) + sql::Statement delete_info(db_.GetCachedStatement(SQL_FROM_HERE, + "DELETE FROM info WHERE rowid=?")); + if (!delete_info) return; for (std::set<int64>::const_iterator i = rows_to_delete.begin(); i != rows_to_delete.end(); ++i) { - delete_info->bind_int64(0, *i); - delete_info->step(); - delete_info->reset(); + delete_info.BindInt64(0, *i); + if (!delete_info.Run()) { + NOTREACHED(); + return; + } + delete_info.Reset(); } } void TextDatabase::Optimize() { - SQLITE_UNIQUE_STATEMENT(statement, *statement_cache_, - "SELECT OPTIMIZE(pages) FROM pages LIMIT 1"); - if (!statement.is_valid()) + sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, + "SELECT OPTIMIZE(pages) FROM pages LIMIT 1")); + if (!statement) return; - statement->step(); + statement.Run(); } void TextDatabase::GetTextMatches(const std::string& query, @@ -319,13 +294,13 @@ void TextDatabase::GetTextMatches(const std::string& query, Time* first_time_searched) { *first_time_searched = options.begin_time; - SQLITE_UNIQUE_STATEMENT(statement, *statement_cache_, + sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, "SELECT url, title, time, offsets(pages), body " "FROM pages LEFT OUTER JOIN info ON pages.rowid = info.rowid " "WHERE pages MATCH ? AND time >= ? AND time < ? " "ORDER BY time DESC " - "LIMIT ?"); - if (!statement.is_valid()) + "LIMIT ?")); + if (!statement) return; // When their values indicate "unspecified", saturate the numbers to the max @@ -337,17 +312,17 @@ void TextDatabase::GetTextMatches(const std::string& query, int effective_max_count = options.max_count ? options.max_count : std::numeric_limits<int>::max(); - statement->bind_string(0, query); - statement->bind_int64(1, effective_begin_time); - statement->bind_int64(2, effective_end_time); - statement->bind_int(3, effective_max_count); + statement.BindString(0, query); + statement.BindInt64(1, effective_begin_time); + statement.BindInt64(2, effective_end_time); + statement.BindInt(3, effective_max_count); - while (statement->step() == SQLITE_ROW) { + while (statement.Step()) { // TODO(brettw) allow canceling the query in the middle. // if (canceled_or_something) // break; - GURL url(statement->column_string(0)); + GURL url(statement.ColumnString(0)); if (options.most_recent_visit_only) { URLSet::const_iterator found_url = found_urls->find(url); if (found_url != found_urls->end()) @@ -359,14 +334,14 @@ void TextDatabase::GetTextMatches(const std::string& query, Match& match = results->at(results->size() - 1); match.url.Swap(&url); - match.title = UTF8ToWide(statement->column_string(1)); - match.time = Time::FromInternalValue(statement->column_int64(2)); + match.title = UTF8ToWide(statement.ColumnString(1)); + match.time = Time::FromInternalValue(statement.ColumnInt64(2)); // Extract any matches in the title. - std::string offsets_str = statement->column_string(3); + std::string offsets_str = statement.ColumnString(3); Snippet::ExtractMatchPositions(offsets_str, kTitleColumnIndex, &match.title_match_positions); - Snippet::ConvertMatchPositionsToWide(statement->column_string(1), + Snippet::ConvertMatchPositionsToWide(statement.ColumnString(1), &match.title_match_positions); // Extract the matches in the body. @@ -375,7 +350,7 @@ void TextDatabase::GetTextMatches(const std::string& query, &match_positions); // Compute the snippet based on those matches. - std::string body = statement->column_string(4); + std::string body = statement.ColumnString(4); match.snippet.ComputeSnippet(match_positions, body); } @@ -392,7 +367,7 @@ void TextDatabase::GetTextMatches(const std::string& query, *first_time_searched = results->back().time; } - statement->reset(); + statement.Reset(); } } // namespace history diff --git a/chrome/browser/history/text_database.h b/chrome/browser/history/text_database.h index 797e3e9..e15ac52 100644 --- a/chrome/browser/history/text_database.h +++ b/chrome/browser/history/text_database.h @@ -1,22 +1,20 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// 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 CHROME_BROWSER_HISTORY_TEXT_DATABASE_H__ -#define CHROME_BROWSER_HISTORY_TEXT_DATABASE_H__ +#ifndef CHROME_BROWSER_HISTORY_TEXT_DATABASE_H_ +#define CHROME_BROWSER_HISTORY_TEXT_DATABASE_H_ #include <set> #include <vector> +#include "app/sql/connection.h" +#include "app/sql/meta_table.h" #include "base/basictypes.h" #include "base/file_path.h" #include "chrome/browser/history/history_types.h" -#include "chrome/browser/meta_table_helper.h" #include "googleurl/src/gurl.h" -struct sqlite3; -class SqliteStatementCache; - namespace history { // Encapsulation of a full-text indexed database file. @@ -144,9 +142,8 @@ class TextDatabase { // Ensures that the tables and indices are created. Returns true on success. bool CreateTables(); - // See the constructor. - sqlite3* db_; - SqliteStatementCache* statement_cache_; + // The sql database. Not valid until Init is called. + sql::Connection db_; const FilePath path_; const DBIdent ident_; @@ -155,17 +152,11 @@ class TextDatabase { // Full file name of the file on disk, computed in Init(). FilePath file_name_; - // Nesting levels of transactions. Since sqlite only allows one open - // transaction, we simulate nested transactions by mapping the outermost one - // to a real transaction. Since this object never needs to do ROLLBACK, losing - // the ability for all transactions to rollback is inconsequential. - int transaction_nesting_; - - MetaTableHelper meta_table_; + sql::MetaTable meta_table_; - DISALLOW_EVIL_CONSTRUCTORS(TextDatabase); + DISALLOW_COPY_AND_ASSIGN(TextDatabase); }; } // namespace history -#endif // CHROME_BROWSER_HISTORY_TEXT_DATABASE_H__ +#endif // CHROME_BROWSER_HISTORY_TEXT_DATABASE_H_ |