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 |