| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "sql/connection.h" | 5 #include "sql/connection.h" |
| 6 | 6 |
| 7 #include <string.h> | 7 #include <string.h> |
| 8 | 8 |
| 9 #include "base/files/file_path.h" | 9 #include "base/files/file_path.h" |
| 10 #include "base/files/file_util.h" | 10 #include "base/files/file_util.h" |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 134 | 134 |
| 135 // TODO(shess): NULL in file->pMethods has been observed on android_dbg | 135 // TODO(shess): NULL in file->pMethods has been observed on android_dbg |
| 136 // content_unittests, even though it should not be possible. | 136 // content_unittests, even though it should not be possible. |
| 137 // http://crbug.com/329982 | 137 // http://crbug.com/329982 |
| 138 if (!*file || !(*file)->pMethods) | 138 if (!*file || !(*file)->pMethods) |
| 139 return SQLITE_ERROR; | 139 return SQLITE_ERROR; |
| 140 | 140 |
| 141 return rc; | 141 return rc; |
| 142 } | 142 } |
| 143 | 143 |
| 144 // This should match UMA_HISTOGRAM_MEDIUM_TIMES(). |
| 145 base::HistogramBase* GetMediumTimeHistogram(const std::string& name) { |
| 146 return base::Histogram::FactoryTimeGet( |
| 147 name, |
| 148 base::TimeDelta::FromMilliseconds(10), |
| 149 base::TimeDelta::FromMinutes(3), |
| 150 50, |
| 151 base::HistogramBase::kUmaTargetedHistogramFlag); |
| 152 } |
| 153 |
| 144 } // namespace | 154 } // namespace |
| 145 | 155 |
| 146 namespace sql { | 156 namespace sql { |
| 147 | 157 |
| 148 // static | 158 // static |
| 149 Connection::ErrorIgnorerCallback* Connection::current_ignorer_cb_ = NULL; | 159 Connection::ErrorIgnorerCallback* Connection::current_ignorer_cb_ = NULL; |
| 150 | 160 |
| 151 // static | 161 // static |
| 152 bool Connection::ShouldIgnoreSqliteError(int error) { | 162 bool Connection::ShouldIgnoreSqliteError(int error) { |
| 153 if (!current_ignorer_cb_) | 163 if (!current_ignorer_cb_) |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 212 | 222 |
| 213 Connection::Connection() | 223 Connection::Connection() |
| 214 : db_(NULL), | 224 : db_(NULL), |
| 215 page_size_(0), | 225 page_size_(0), |
| 216 cache_size_(0), | 226 cache_size_(0), |
| 217 exclusive_locking_(false), | 227 exclusive_locking_(false), |
| 218 restrict_to_user_(false), | 228 restrict_to_user_(false), |
| 219 transaction_nesting_(0), | 229 transaction_nesting_(0), |
| 220 needs_rollback_(false), | 230 needs_rollback_(false), |
| 221 in_memory_(false), | 231 in_memory_(false), |
| 222 poisoned_(false) { | 232 poisoned_(false), |
| 233 stats_histogram_(NULL), |
| 234 commit_time_histogram_(NULL), |
| 235 autocommit_time_histogram_(NULL), |
| 236 update_time_histogram_(NULL), |
| 237 query_time_histogram_(NULL), |
| 238 clock_(new TimeSource()) { |
| 223 } | 239 } |
| 224 | 240 |
| 225 Connection::~Connection() { | 241 Connection::~Connection() { |
| 226 Close(); | 242 Close(); |
| 227 } | 243 } |
| 228 | 244 |
| 245 void Connection::RecordEvent(Events event, size_t count) { |
| 246 for (size_t i = 0; i < count; ++i) { |
| 247 UMA_HISTOGRAM_ENUMERATION("Sqlite.Stats", event, EVENT_MX); |
| 248 } |
| 249 |
| 250 if (stats_histogram_) { |
| 251 for (size_t i = 0; i < count; ++i) { |
| 252 stats_histogram_->Add(event); |
| 253 } |
| 254 } |
| 255 } |
| 256 |
| 257 void Connection::RecordCommitTime(const base::TimeDelta& delta) { |
| 258 RecordUpdateTime(delta); |
| 259 UMA_HISTOGRAM_MEDIUM_TIMES("Sqlite.CommitTime", delta); |
| 260 if (commit_time_histogram_) |
| 261 commit_time_histogram_->AddTime(delta); |
| 262 } |
| 263 |
| 264 void Connection::RecordAutoCommitTime(const base::TimeDelta& delta) { |
| 265 RecordUpdateTime(delta); |
| 266 UMA_HISTOGRAM_MEDIUM_TIMES("Sqlite.AutoCommitTime", delta); |
| 267 if (autocommit_time_histogram_) |
| 268 autocommit_time_histogram_->AddTime(delta); |
| 269 } |
| 270 |
| 271 void Connection::RecordUpdateTime(const base::TimeDelta& delta) { |
| 272 RecordQueryTime(delta); |
| 273 UMA_HISTOGRAM_MEDIUM_TIMES("Sqlite.UpdateTime", delta); |
| 274 if (update_time_histogram_) |
| 275 update_time_histogram_->AddTime(delta); |
| 276 } |
| 277 |
| 278 void Connection::RecordQueryTime(const base::TimeDelta& delta) { |
| 279 UMA_HISTOGRAM_MEDIUM_TIMES("Sqlite.QueryTime", delta); |
| 280 if (query_time_histogram_) |
| 281 query_time_histogram_->AddTime(delta); |
| 282 } |
| 283 |
| 284 void Connection::RecordTimeAndChanges( |
| 285 const base::TimeDelta& delta, bool read_only) { |
| 286 if (read_only) { |
| 287 RecordQueryTime(delta); |
| 288 } else { |
| 289 const int changes = sqlite3_changes(db_); |
| 290 if (sqlite3_get_autocommit(db_)) { |
| 291 RecordAutoCommitTime(delta); |
| 292 RecordEvent(EVENT_CHANGES_AUTOCOMMIT, changes); |
| 293 } else { |
| 294 RecordUpdateTime(delta); |
| 295 RecordEvent(EVENT_CHANGES, changes); |
| 296 } |
| 297 } |
| 298 } |
| 299 |
| 229 bool Connection::Open(const base::FilePath& path) { | 300 bool Connection::Open(const base::FilePath& path) { |
| 230 if (!histogram_tag_.empty()) { | 301 if (!histogram_tag_.empty()) { |
| 231 int64_t size_64 = 0; | 302 int64_t size_64 = 0; |
| 232 if (base::GetFileSize(path, &size_64)) { | 303 if (base::GetFileSize(path, &size_64)) { |
| 233 size_t sample = static_cast<size_t>(size_64 / 1024); | 304 size_t sample = static_cast<size_t>(size_64 / 1024); |
| 234 std::string full_histogram_name = "Sqlite.SizeKB." + histogram_tag_; | 305 std::string full_histogram_name = "Sqlite.SizeKB." + histogram_tag_; |
| 235 base::HistogramBase* histogram = | 306 base::HistogramBase* histogram = |
| 236 base::Histogram::FactoryGet( | 307 base::Histogram::FactoryGet( |
| 237 full_histogram_name, 1, 1000000, 50, | 308 full_histogram_name, 1, 1000000, 50, |
| 238 base::HistogramBase::kUmaTargetedHistogramFlag); | 309 base::HistogramBase::kUmaTargetedHistogramFlag); |
| (...skipping 330 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 569 // When we're going to rollback, fail on this begin and don't actually | 640 // When we're going to rollback, fail on this begin and don't actually |
| 570 // mark us as entering the nested transaction. | 641 // mark us as entering the nested transaction. |
| 571 return false; | 642 return false; |
| 572 } | 643 } |
| 573 | 644 |
| 574 bool success = true; | 645 bool success = true; |
| 575 if (!transaction_nesting_) { | 646 if (!transaction_nesting_) { |
| 576 needs_rollback_ = false; | 647 needs_rollback_ = false; |
| 577 | 648 |
| 578 Statement begin(GetCachedStatement(SQL_FROM_HERE, "BEGIN TRANSACTION")); | 649 Statement begin(GetCachedStatement(SQL_FROM_HERE, "BEGIN TRANSACTION")); |
| 650 RecordOneEvent(EVENT_BEGIN); |
| 579 if (!begin.Run()) | 651 if (!begin.Run()) |
| 580 return false; | 652 return false; |
| 581 } | 653 } |
| 582 transaction_nesting_++; | 654 transaction_nesting_++; |
| 583 return success; | 655 return success; |
| 584 } | 656 } |
| 585 | 657 |
| 586 void Connection::RollbackTransaction() { | 658 void Connection::RollbackTransaction() { |
| 587 if (!transaction_nesting_) { | 659 if (!transaction_nesting_) { |
| 588 DLOG_IF(FATAL, !poisoned_) << "Rolling back a nonexistent transaction"; | 660 DLOG_IF(FATAL, !poisoned_) << "Rolling back a nonexistent transaction"; |
| (...skipping 22 matching lines...) Expand all Loading... |
| 611 // Mark any nested transactions as failing after we've already got one. | 683 // Mark any nested transactions as failing after we've already got one. |
| 612 return !needs_rollback_; | 684 return !needs_rollback_; |
| 613 } | 685 } |
| 614 | 686 |
| 615 if (needs_rollback_) { | 687 if (needs_rollback_) { |
| 616 DoRollback(); | 688 DoRollback(); |
| 617 return false; | 689 return false; |
| 618 } | 690 } |
| 619 | 691 |
| 620 Statement commit(GetCachedStatement(SQL_FROM_HERE, "COMMIT")); | 692 Statement commit(GetCachedStatement(SQL_FROM_HERE, "COMMIT")); |
| 621 return commit.Run(); | 693 |
| 694 // Collect the commit time manually, sql::Statement would register it as query |
| 695 // time only. |
| 696 const base::TimeTicks before = Now(); |
| 697 bool ret = commit.RunWithoutTimers(); |
| 698 const base::TimeDelta delta = Now() - before; |
| 699 |
| 700 RecordCommitTime(delta); |
| 701 RecordOneEvent(EVENT_COMMIT); |
| 702 |
| 703 return ret; |
| 622 } | 704 } |
| 623 | 705 |
| 624 void Connection::RollbackAllTransactions() { | 706 void Connection::RollbackAllTransactions() { |
| 625 if (transaction_nesting_ > 0) { | 707 if (transaction_nesting_ > 0) { |
| 626 transaction_nesting_ = 0; | 708 transaction_nesting_ = 0; |
| 627 DoRollback(); | 709 DoRollback(); |
| 628 } | 710 } |
| 629 } | 711 } |
| 630 | 712 |
| 631 bool Connection::AttachDatabase(const base::FilePath& other_db_path, | 713 bool Connection::AttachDatabase(const base::FilePath& other_db_path, |
| (...skipping 11 matching lines...) Expand all Loading... |
| 643 } | 725 } |
| 644 | 726 |
| 645 bool Connection::DetachDatabase(const char* attachment_point) { | 727 bool Connection::DetachDatabase(const char* attachment_point) { |
| 646 DCHECK(ValidAttachmentPoint(attachment_point)); | 728 DCHECK(ValidAttachmentPoint(attachment_point)); |
| 647 | 729 |
| 648 Statement s(GetUniqueStatement("DETACH DATABASE ?")); | 730 Statement s(GetUniqueStatement("DETACH DATABASE ?")); |
| 649 s.BindString(0, attachment_point); | 731 s.BindString(0, attachment_point); |
| 650 return s.Run(); | 732 return s.Run(); |
| 651 } | 733 } |
| 652 | 734 |
| 735 // TODO(shess): Consider changing this to execute exactly one statement. If a |
| 736 // caller wishes to execute multiple statements, that should be explicit, and |
| 737 // perhaps tucked into an explicit transaction with rollback in case of error. |
| 653 int Connection::ExecuteAndReturnErrorCode(const char* sql) { | 738 int Connection::ExecuteAndReturnErrorCode(const char* sql) { |
| 654 AssertIOAllowed(); | 739 AssertIOAllowed(); |
| 655 if (!db_) { | 740 if (!db_) { |
| 656 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; | 741 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; |
| 657 return SQLITE_ERROR; | 742 return SQLITE_ERROR; |
| 658 } | 743 } |
| 659 return sqlite3_exec(db_, sql, NULL, NULL, NULL); | 744 DCHECK(sql); |
| 745 |
| 746 RecordOneEvent(EVENT_EXECUTE); |
| 747 int rc = SQLITE_OK; |
| 748 while ((rc == SQLITE_OK) && *sql) { |
| 749 sqlite3_stmt *stmt = NULL; |
| 750 const char *leftover_sql; |
| 751 |
| 752 const base::TimeTicks before = Now(); |
| 753 rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, &leftover_sql); |
| 754 sql = leftover_sql; |
| 755 |
| 756 // Stop if an error is encountered. |
| 757 if (rc != SQLITE_OK) |
| 758 break; |
| 759 |
| 760 // This happens if |sql| originally only contained comments or whitespace. |
| 761 // TODO(shess): Audit to see if this can become a DCHECK(). Having |
| 762 // extraneous comments and whitespace in the SQL statements increases |
| 763 // runtime cost and can easily be shifted out to the C++ layer. |
| 764 if (!stmt) |
| 765 continue; |
| 766 |
| 767 // Save for use after statement is finalized. |
| 768 const bool read_only = !!sqlite3_stmt_readonly(stmt); |
| 769 |
| 770 RecordOneEvent(Connection::EVENT_STATEMENT_RUN); |
| 771 while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { |
| 772 // TODO(shess): Audit to see if this can become a DCHECK. I think PRAGMA |
| 773 // is the only legitimate case for this. |
| 774 RecordOneEvent(Connection::EVENT_STATEMENT_ROWS); |
| 775 } |
| 776 |
| 777 // sqlite3_finalize() returns SQLITE_OK if the most recent sqlite3_step() |
| 778 // returned SQLITE_DONE or SQLITE_ROW, otherwise the error code. |
| 779 rc = sqlite3_finalize(stmt); |
| 780 if (rc == SQLITE_OK) |
| 781 RecordOneEvent(Connection::EVENT_STATEMENT_SUCCESS); |
| 782 |
| 783 // sqlite3_exec() does this, presumably to avoid spinning the parser for |
| 784 // trailing whitespace. |
| 785 // TODO(shess): Audit to see if this can become a DCHECK. |
| 786 while (IsAsciiWhitespace(*sql)) { |
| 787 sql++; |
| 788 } |
| 789 |
| 790 const base::TimeDelta delta = Now() - before; |
| 791 RecordTimeAndChanges(delta, read_only); |
| 792 } |
| 793 return rc; |
| 660 } | 794 } |
| 661 | 795 |
| 662 bool Connection::Execute(const char* sql) { | 796 bool Connection::Execute(const char* sql) { |
| 663 if (!db_) { | 797 if (!db_) { |
| 664 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; | 798 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; |
| 665 return false; | 799 return false; |
| 666 } | 800 } |
| 667 | 801 |
| 668 int error = ExecuteAndReturnErrorCode(sql); | 802 int error = ExecuteAndReturnErrorCode(sql); |
| 669 if (error != SQLITE_OK) | 803 if (error != SQLITE_OK) |
| (...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 879 AssertIOAllowed(); | 1013 AssertIOAllowed(); |
| 880 | 1014 |
| 881 if (db_) { | 1015 if (db_) { |
| 882 DLOG(FATAL) << "sql::Connection is already open."; | 1016 DLOG(FATAL) << "sql::Connection is already open."; |
| 883 return false; | 1017 return false; |
| 884 } | 1018 } |
| 885 | 1019 |
| 886 // Make sure sqlite3_initialize() is called before anything else. | 1020 // Make sure sqlite3_initialize() is called before anything else. |
| 887 InitializeSqlite(); | 1021 InitializeSqlite(); |
| 888 | 1022 |
| 1023 // Setup the stats histograms immediately rather than allocating lazily. |
| 1024 // Connections which won't exercise all of these probably shouldn't exist. |
| 1025 if (!histogram_tag_.empty()) { |
| 1026 stats_histogram_ = |
| 1027 base::LinearHistogram::FactoryGet( |
| 1028 "Sqlite.Stats." + histogram_tag_, 1, EVENT_MX, EVENT_MX + 1, |
| 1029 base::HistogramBase::kUmaTargetedHistogramFlag); |
| 1030 |
| 1031 // The timer setup matches UMA_HISTOGRAM_MEDIUM_TIMES(). 3 minutes is an |
| 1032 // unreasonable time for any single operation, so there is not much value to |
| 1033 // knowing if it was 3 minutes or 5 minutes. In reality at that point |
| 1034 // things are entirely busted. |
| 1035 commit_time_histogram_ = |
| 1036 GetMediumTimeHistogram("Sqlite.CommitTime." + histogram_tag_); |
| 1037 |
| 1038 autocommit_time_histogram_ = |
| 1039 GetMediumTimeHistogram("Sqlite.AutoCommitTime." + histogram_tag_); |
| 1040 |
| 1041 update_time_histogram_ = |
| 1042 GetMediumTimeHistogram("Sqlite.UpdateTime." + histogram_tag_); |
| 1043 |
| 1044 query_time_histogram_ = |
| 1045 GetMediumTimeHistogram("Sqlite.QueryTime." + histogram_tag_); |
| 1046 } |
| 1047 |
| 889 // If |poisoned_| is set, it means an error handler called | 1048 // If |poisoned_| is set, it means an error handler called |
| 890 // RazeAndClose(). Until regular Close() is called, the caller | 1049 // RazeAndClose(). Until regular Close() is called, the caller |
| 891 // should be treating the database as open, but is_open() currently | 1050 // should be treating the database as open, but is_open() currently |
| 892 // only considers the sqlite3 handle's state. | 1051 // only considers the sqlite3 handle's state. |
| 893 // TODO(shess): Revise is_open() to consider poisoned_, and review | 1052 // TODO(shess): Revise is_open() to consider poisoned_, and review |
| 894 // to see if any non-testing code even depends on it. | 1053 // to see if any non-testing code even depends on it. |
| 895 DLOG_IF(FATAL, poisoned_) << "sql::Connection is already open."; | 1054 DLOG_IF(FATAL, poisoned_) << "sql::Connection is already open."; |
| 896 poisoned_ = false; | 1055 poisoned_ = false; |
| 897 | 1056 |
| 898 int err = sqlite3_open(file_name.c_str(), &db_); | 1057 int err = sqlite3_open(file_name.c_str(), &db_); |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1021 if (was_poisoned && retry_flag == RETRY_ON_POISON) | 1180 if (was_poisoned && retry_flag == RETRY_ON_POISON) |
| 1022 return OpenInternal(file_name, NO_RETRY); | 1181 return OpenInternal(file_name, NO_RETRY); |
| 1023 return false; | 1182 return false; |
| 1024 } | 1183 } |
| 1025 | 1184 |
| 1026 return true; | 1185 return true; |
| 1027 } | 1186 } |
| 1028 | 1187 |
| 1029 void Connection::DoRollback() { | 1188 void Connection::DoRollback() { |
| 1030 Statement rollback(GetCachedStatement(SQL_FROM_HERE, "ROLLBACK")); | 1189 Statement rollback(GetCachedStatement(SQL_FROM_HERE, "ROLLBACK")); |
| 1031 rollback.Run(); | 1190 |
| 1191 // Collect the rollback time manually, sql::Statement would register it as |
| 1192 // query time only. |
| 1193 const base::TimeTicks before = Now(); |
| 1194 rollback.RunWithoutTimers(); |
| 1195 const base::TimeDelta delta = Now() - before; |
| 1196 |
| 1197 RecordUpdateTime(delta); |
| 1198 RecordOneEvent(EVENT_ROLLBACK); |
| 1199 |
| 1032 needs_rollback_ = false; | 1200 needs_rollback_ = false; |
| 1033 } | 1201 } |
| 1034 | 1202 |
| 1035 void Connection::StatementRefCreated(StatementRef* ref) { | 1203 void Connection::StatementRefCreated(StatementRef* ref) { |
| 1036 DCHECK(open_statements_.find(ref) == open_statements_.end()); | 1204 DCHECK(open_statements_.find(ref) == open_statements_.end()); |
| 1037 open_statements_.insert(ref); | 1205 open_statements_.insert(ref); |
| 1038 } | 1206 } |
| 1039 | 1207 |
| 1040 void Connection::StatementRefDeleted(StatementRef* ref) { | 1208 void Connection::StatementRefDeleted(StatementRef* ref) { |
| 1041 StatementRefSet::iterator i = open_statements_.find(ref); | 1209 StatementRefSet::iterator i = open_statements_.find(ref); |
| 1042 if (i == open_statements_.end()) | 1210 if (i == open_statements_.end()) |
| 1043 DLOG(FATAL) << "Could not find statement"; | 1211 DLOG(FATAL) << "Could not find statement"; |
| 1044 else | 1212 else |
| 1045 open_statements_.erase(i); | 1213 open_statements_.erase(i); |
| 1046 } | 1214 } |
| 1047 | 1215 |
| 1216 void Connection::set_histogram_tag(const std::string& tag) { |
| 1217 DCHECK(!is_open()); |
| 1218 histogram_tag_ = tag; |
| 1219 } |
| 1220 |
| 1048 void Connection::AddTaggedHistogram(const std::string& name, | 1221 void Connection::AddTaggedHistogram(const std::string& name, |
| 1049 size_t sample) const { | 1222 size_t sample) const { |
| 1050 if (histogram_tag_.empty()) | 1223 if (histogram_tag_.empty()) |
| 1051 return; | 1224 return; |
| 1052 | 1225 |
| 1053 // TODO(shess): The histogram macros create a bit of static storage | 1226 // TODO(shess): The histogram macros create a bit of static storage |
| 1054 // for caching the histogram object. This code shouldn't execute | 1227 // for caching the histogram object. This code shouldn't execute |
| 1055 // often enough for such caching to be crucial. If it becomes an | 1228 // often enough for such caching to be crucial. If it becomes an |
| 1056 // issue, the object could be cached alongside histogram_prefix_. | 1229 // issue, the object could be cached alongside histogram_prefix_. |
| 1057 std::string full_histogram_name = name + "." + histogram_tag_; | 1230 std::string full_histogram_name = name + "." + histogram_tag_; |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1130 ret = stmt.Succeeded(); | 1303 ret = stmt.Succeeded(); |
| 1131 } | 1304 } |
| 1132 | 1305 |
| 1133 // Best effort to put things back as they were before. | 1306 // Best effort to put things back as they were before. |
| 1134 const char kNoWritableSchema[] = "PRAGMA writable_schema = OFF"; | 1307 const char kNoWritableSchema[] = "PRAGMA writable_schema = OFF"; |
| 1135 ignore_result(Execute(kNoWritableSchema)); | 1308 ignore_result(Execute(kNoWritableSchema)); |
| 1136 | 1309 |
| 1137 return ret; | 1310 return ret; |
| 1138 } | 1311 } |
| 1139 | 1312 |
| 1313 base::TimeTicks TimeSource::Now() { |
| 1314 return base::TimeTicks::Now(); |
| 1315 } |
| 1316 |
| 1140 } // namespace sql | 1317 } // namespace sql |
| OLD | NEW |