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 201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
212 | 212 |
213 Connection::Connection() | 213 Connection::Connection() |
214 : db_(NULL), | 214 : db_(NULL), |
215 page_size_(0), | 215 page_size_(0), |
216 cache_size_(0), | 216 cache_size_(0), |
217 exclusive_locking_(false), | 217 exclusive_locking_(false), |
218 restrict_to_user_(false), | 218 restrict_to_user_(false), |
219 transaction_nesting_(0), | 219 transaction_nesting_(0), |
220 needs_rollback_(false), | 220 needs_rollback_(false), |
221 in_memory_(false), | 221 in_memory_(false), |
222 poisoned_(false) { | 222 poisoned_(false), |
223 stats_histogram_(NULL), | |
224 commit_time_histogram_(NULL), | |
225 autocommit_time_histogram_(NULL), | |
226 update_time_histogram_(NULL), | |
227 query_time_histogram_(NULL) { | |
223 } | 228 } |
224 | 229 |
225 Connection::~Connection() { | 230 Connection::~Connection() { |
226 Close(); | 231 Close(); |
227 } | 232 } |
228 | 233 |
234 void Connection::RecordEvent(Events event, size_t count) { | |
235 for (size_t i = 0; i < count; ++i) { | |
236 UMA_HISTOGRAM_ENUMERATION("Sqlite.Stats", event, EVENT_MX); | |
237 } | |
238 | |
239 if (stats_histogram_) { | |
240 for (size_t i = 0; i < count; ++i) { | |
241 stats_histogram_->Add(event); | |
242 } | |
243 } | |
244 } | |
245 | |
246 void Connection::CommitTime(const base::TimeDelta& delta) { | |
247 UpdateTime(delta); | |
248 UMA_HISTOGRAM_MEDIUM_TIMES("Sqlite.CommitTime", delta); | |
249 if (commit_time_histogram_) | |
250 commit_time_histogram_->AddTime(delta); | |
251 } | |
252 | |
253 void Connection::AutoCommitTime(const base::TimeDelta& delta) { | |
254 UpdateTime(delta); | |
255 UMA_HISTOGRAM_MEDIUM_TIMES("Sqlite.AutoCommitTime", delta); | |
256 if (autocommit_time_histogram_) | |
257 autocommit_time_histogram_->AddTime(delta); | |
258 } | |
259 | |
260 void Connection::UpdateTime(const base::TimeDelta& delta) { | |
261 QueryTime(delta); | |
262 UMA_HISTOGRAM_MEDIUM_TIMES("Sqlite.UpdateTime", delta); | |
263 if (update_time_histogram_) | |
264 update_time_histogram_->AddTime(delta); | |
265 } | |
266 | |
267 void Connection::QueryTime(const base::TimeDelta& delta) { | |
268 UMA_HISTOGRAM_MEDIUM_TIMES("Sqlite.QueryTime", delta); | |
269 if (query_time_histogram_) | |
270 query_time_histogram_->AddTime(delta); | |
271 } | |
272 | |
273 void Connection::ChangeHelper(const base::TimeDelta& delta, bool read_only) { | |
274 if (read_only) { | |
275 QueryTime(delta); | |
276 } else { | |
277 const int changes = sqlite3_changes(db_); | |
278 if (sqlite3_get_autocommit(db_)) { | |
279 AutoCommitTime(delta); | |
280 RecordEvent(EVENT_CHANGES_AUTOCOMMIT, changes); | |
281 } else { | |
282 UpdateTime(delta); | |
283 RecordEvent(EVENT_CHANGES, changes); | |
284 } | |
285 } | |
286 } | |
287 | |
229 bool Connection::Open(const base::FilePath& path) { | 288 bool Connection::Open(const base::FilePath& path) { |
230 if (!histogram_tag_.empty()) { | 289 if (!histogram_tag_.empty()) { |
231 int64_t size_64 = 0; | 290 int64_t size_64 = 0; |
232 if (base::GetFileSize(path, &size_64)) { | 291 if (base::GetFileSize(path, &size_64)) { |
233 size_t sample = static_cast<size_t>(size_64 / 1024); | 292 size_t sample = static_cast<size_t>(size_64 / 1024); |
234 std::string full_histogram_name = "Sqlite.SizeKB." + histogram_tag_; | 293 std::string full_histogram_name = "Sqlite.SizeKB." + histogram_tag_; |
235 base::HistogramBase* histogram = | 294 base::HistogramBase* histogram = |
236 base::Histogram::FactoryGet( | 295 base::Histogram::FactoryGet( |
237 full_histogram_name, 1, 1000000, 50, | 296 full_histogram_name, 1, 1000000, 50, |
238 base::HistogramBase::kUmaTargetedHistogramFlag); | 297 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 | 628 // When we're going to rollback, fail on this begin and don't actually |
570 // mark us as entering the nested transaction. | 629 // mark us as entering the nested transaction. |
571 return false; | 630 return false; |
572 } | 631 } |
573 | 632 |
574 bool success = true; | 633 bool success = true; |
575 if (!transaction_nesting_) { | 634 if (!transaction_nesting_) { |
576 needs_rollback_ = false; | 635 needs_rollback_ = false; |
577 | 636 |
578 Statement begin(GetCachedStatement(SQL_FROM_HERE, "BEGIN TRANSACTION")); | 637 Statement begin(GetCachedStatement(SQL_FROM_HERE, "BEGIN TRANSACTION")); |
638 RecordOneEvent(EVENT_BEGIN); | |
579 if (!begin.Run()) | 639 if (!begin.Run()) |
580 return false; | 640 return false; |
581 } | 641 } |
582 transaction_nesting_++; | 642 transaction_nesting_++; |
583 return success; | 643 return success; |
584 } | 644 } |
585 | 645 |
586 void Connection::RollbackTransaction() { | 646 void Connection::RollbackTransaction() { |
587 if (!transaction_nesting_) { | 647 if (!transaction_nesting_) { |
588 DLOG_IF(FATAL, !poisoned_) << "Rolling back a nonexistent transaction"; | 648 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. | 671 // Mark any nested transactions as failing after we've already got one. |
612 return !needs_rollback_; | 672 return !needs_rollback_; |
613 } | 673 } |
614 | 674 |
615 if (needs_rollback_) { | 675 if (needs_rollback_) { |
616 DoRollback(); | 676 DoRollback(); |
617 return false; | 677 return false; |
618 } | 678 } |
619 | 679 |
620 Statement commit(GetCachedStatement(SQL_FROM_HERE, "COMMIT")); | 680 Statement commit(GetCachedStatement(SQL_FROM_HERE, "COMMIT")); |
621 return commit.Run(); | 681 |
682 // Collect the commit time manually, sql::Statement would register it as query | |
683 // time only. | |
684 const base::TimeTicks before = base::TimeTicks::Now(); | |
685 bool ret = commit.RunWithoutTimers(); | |
686 const base::TimeDelta delta = base::TimeTicks::Now() - before; | |
687 | |
688 CommitTime(delta); | |
689 RecordOneEvent(EVENT_COMMIT); | |
690 | |
691 return ret; | |
622 } | 692 } |
623 | 693 |
624 void Connection::RollbackAllTransactions() { | 694 void Connection::RollbackAllTransactions() { |
625 if (transaction_nesting_ > 0) { | 695 if (transaction_nesting_ > 0) { |
626 transaction_nesting_ = 0; | 696 transaction_nesting_ = 0; |
627 DoRollback(); | 697 DoRollback(); |
628 } | 698 } |
629 } | 699 } |
630 | 700 |
631 bool Connection::AttachDatabase(const base::FilePath& other_db_path, | 701 bool Connection::AttachDatabase(const base::FilePath& other_db_path, |
(...skipping 11 matching lines...) Expand all Loading... | |
643 } | 713 } |
644 | 714 |
645 bool Connection::DetachDatabase(const char* attachment_point) { | 715 bool Connection::DetachDatabase(const char* attachment_point) { |
646 DCHECK(ValidAttachmentPoint(attachment_point)); | 716 DCHECK(ValidAttachmentPoint(attachment_point)); |
647 | 717 |
648 Statement s(GetUniqueStatement("DETACH DATABASE ?")); | 718 Statement s(GetUniqueStatement("DETACH DATABASE ?")); |
649 s.BindString(0, attachment_point); | 719 s.BindString(0, attachment_point); |
650 return s.Run(); | 720 return s.Run(); |
651 } | 721 } |
652 | 722 |
723 // TODO(shess): Consider changing this to execute exactly one statement. If a | |
724 // caller wishes to execute multiple statements, that should be explicit, and | |
725 // perhaps tucked into an explicit transaction with rollback in case of error. | |
653 int Connection::ExecuteAndReturnErrorCode(const char* sql) { | 726 int Connection::ExecuteAndReturnErrorCode(const char* sql) { |
654 AssertIOAllowed(); | 727 AssertIOAllowed(); |
655 if (!db_) { | 728 if (!db_) { |
656 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; | 729 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; |
657 return SQLITE_ERROR; | 730 return SQLITE_ERROR; |
658 } | 731 } |
659 return sqlite3_exec(db_, sql, NULL, NULL, NULL); | 732 DCHECK(sql); |
733 | |
734 RecordOneEvent(EVENT_EXECUTE); | |
735 int rc = SQLITE_OK; | |
736 while ((rc == SQLITE_OK) && *sql) { | |
737 sqlite3_stmt *stmt = NULL; | |
738 const char *leftover_sql; | |
739 | |
740 const base::TimeTicks before = base::TimeTicks::Now(); | |
741 rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, &leftover_sql); | |
742 sql = leftover_sql; | |
743 | |
744 // Stop if an error is encountered. | |
745 if (rc != SQLITE_OK) | |
746 break; | |
747 | |
748 // This happens if |sql| originally only contained comments or whitespace. | |
749 // TODO(shess): Audit to see if this can become a DCHECK(). Having | |
750 // extraneous comments and whitespace in the SQL statements increases | |
751 // runtime cost and can easily be shifted out to the C++ layer. | |
752 if (!stmt) | |
753 continue; | |
754 | |
755 // Save for use after statement is finalized. | |
756 const int read_only = sqlite3_stmt_readonly(stmt); | |
757 | |
758 RecordOneEvent(Connection::EVENT_STATEMENT_RUN); | |
759 while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { | |
760 // TODO(shess): Audit to see if this can become a DCHECK. I think PRAGMA | |
761 // is the only legitimate case for this. | |
762 RecordOneEvent(Connection::EVENT_STATEMENT_ROWS); | |
763 } | |
764 | |
765 // sqlite3_finalize() returns SQLITE_OK if the most recent sqlite3_step() | |
766 // returned SQLITE_DONE or SQLITE_ROW, otherwise the error code. | |
767 rc = sqlite3_finalize(stmt); | |
768 if (rc == SQLITE_OK) | |
769 RecordOneEvent(Connection::EVENT_STATEMENT_SUCCESS); | |
770 | |
771 // sqlite3_exec() does this, presumably to avoid spinning the parser for | |
772 // trailing whitespace. | |
773 // TODO(shess): Audit to see if this can become a DCHECK. | |
774 while (IsAsciiWhitespace(*sql)) { | |
775 sql++; | |
776 } | |
777 | |
778 const base::TimeDelta delta = base::TimeTicks::Now() - before; | |
779 ChangeHelper(delta, read_only); | |
780 } | |
781 return rc; | |
660 } | 782 } |
661 | 783 |
662 bool Connection::Execute(const char* sql) { | 784 bool Connection::Execute(const char* sql) { |
663 if (!db_) { | 785 if (!db_) { |
664 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; | 786 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; |
665 return false; | 787 return false; |
666 } | 788 } |
667 | 789 |
668 int error = ExecuteAndReturnErrorCode(sql); | 790 int error = ExecuteAndReturnErrorCode(sql); |
669 if (error != SQLITE_OK) | 791 if (error != SQLITE_OK) |
(...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
879 AssertIOAllowed(); | 1001 AssertIOAllowed(); |
880 | 1002 |
881 if (db_) { | 1003 if (db_) { |
882 DLOG(FATAL) << "sql::Connection is already open."; | 1004 DLOG(FATAL) << "sql::Connection is already open."; |
883 return false; | 1005 return false; |
884 } | 1006 } |
885 | 1007 |
886 // Make sure sqlite3_initialize() is called before anything else. | 1008 // Make sure sqlite3_initialize() is called before anything else. |
887 InitializeSqlite(); | 1009 InitializeSqlite(); |
888 | 1010 |
1011 // Setup the stats histograms immediately rather than allocating lazily. | |
1012 // Connections which won't exercise all of these probably shouldn't exist. | |
1013 if (!histogram_tag_.empty()) { | |
1014 stats_histogram_ = | |
1015 base::LinearHistogram::FactoryGet( | |
1016 "Sqlite.Stats." + histogram_tag_, 1, EVENT_MX, EVENT_MX + 1, | |
1017 base::HistogramBase::kUmaTargetedHistogramFlag); | |
1018 | |
1019 // The timer setup matches UMA_HISTOGRAM_MEDIUM_TIMES(). 3 minutes is an | |
1020 // unreasonable time for any single operation, so there is not much value to | |
1021 // knowing if it was 3 minutes or 5 minutes. In reality at that point | |
1022 // things are entirely busted. | |
1023 commit_time_histogram_ = | |
1024 base::Histogram::FactoryTimeGet( | |
1025 "Sqlite.CommitTime." + histogram_tag_, | |
1026 base::TimeDelta::FromMilliseconds(10), | |
1027 base::TimeDelta::FromMinutes(3), | |
1028 50, | |
1029 base::HistogramBase::kUmaTargetedHistogramFlag); | |
rmcilroy
2015/05/21 22:54:48
Maybe make a helper function which creates a Mediu
Scott Hess - ex-Googler
2015/05/21 23:42:36
Done.
| |
1030 | |
1031 autocommit_time_histogram_ = | |
1032 base::Histogram::FactoryTimeGet( | |
1033 "Sqlite.AutoCommitTime." + histogram_tag_, | |
1034 base::TimeDelta::FromMilliseconds(10), | |
1035 base::TimeDelta::FromMinutes(3), | |
1036 50, | |
1037 base::HistogramBase::kUmaTargetedHistogramFlag); | |
1038 | |
1039 update_time_histogram_ = | |
1040 base::Histogram::FactoryTimeGet( | |
1041 "Sqlite.UpdateTime." + histogram_tag_, | |
1042 base::TimeDelta::FromMilliseconds(10), | |
1043 base::TimeDelta::FromMinutes(3), | |
1044 50, | |
1045 base::HistogramBase::kUmaTargetedHistogramFlag); | |
1046 | |
1047 query_time_histogram_ = | |
1048 base::Histogram::FactoryTimeGet( | |
1049 "Sqlite.QueryTime." + histogram_tag_, | |
1050 base::TimeDelta::FromMilliseconds(10), | |
1051 base::TimeDelta::FromMinutes(3), | |
1052 50, | |
1053 base::HistogramBase::kUmaTargetedHistogramFlag); | |
1054 } | |
1055 | |
889 // If |poisoned_| is set, it means an error handler called | 1056 // If |poisoned_| is set, it means an error handler called |
890 // RazeAndClose(). Until regular Close() is called, the caller | 1057 // RazeAndClose(). Until regular Close() is called, the caller |
891 // should be treating the database as open, but is_open() currently | 1058 // should be treating the database as open, but is_open() currently |
892 // only considers the sqlite3 handle's state. | 1059 // only considers the sqlite3 handle's state. |
893 // TODO(shess): Revise is_open() to consider poisoned_, and review | 1060 // TODO(shess): Revise is_open() to consider poisoned_, and review |
894 // to see if any non-testing code even depends on it. | 1061 // to see if any non-testing code even depends on it. |
895 DLOG_IF(FATAL, poisoned_) << "sql::Connection is already open."; | 1062 DLOG_IF(FATAL, poisoned_) << "sql::Connection is already open."; |
896 poisoned_ = false; | 1063 poisoned_ = false; |
897 | 1064 |
898 int err = sqlite3_open(file_name.c_str(), &db_); | 1065 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) | 1188 if (was_poisoned && retry_flag == RETRY_ON_POISON) |
1022 return OpenInternal(file_name, NO_RETRY); | 1189 return OpenInternal(file_name, NO_RETRY); |
1023 return false; | 1190 return false; |
1024 } | 1191 } |
1025 | 1192 |
1026 return true; | 1193 return true; |
1027 } | 1194 } |
1028 | 1195 |
1029 void Connection::DoRollback() { | 1196 void Connection::DoRollback() { |
1030 Statement rollback(GetCachedStatement(SQL_FROM_HERE, "ROLLBACK")); | 1197 Statement rollback(GetCachedStatement(SQL_FROM_HERE, "ROLLBACK")); |
1031 rollback.Run(); | 1198 |
1199 // Collect the rollback time manually, sql::Statement would register it as | |
1200 // query time only. | |
1201 const base::TimeTicks before = base::TimeTicks::Now(); | |
1202 rollback.RunWithoutTimers(); | |
1203 const base::TimeDelta delta = base::TimeTicks::Now() - before; | |
1204 | |
1205 UpdateTime(delta); | |
1206 RecordOneEvent(EVENT_ROLLBACK); | |
1207 | |
1032 needs_rollback_ = false; | 1208 needs_rollback_ = false; |
1033 } | 1209 } |
1034 | 1210 |
1035 void Connection::StatementRefCreated(StatementRef* ref) { | 1211 void Connection::StatementRefCreated(StatementRef* ref) { |
1036 DCHECK(open_statements_.find(ref) == open_statements_.end()); | 1212 DCHECK(open_statements_.find(ref) == open_statements_.end()); |
1037 open_statements_.insert(ref); | 1213 open_statements_.insert(ref); |
1038 } | 1214 } |
1039 | 1215 |
1040 void Connection::StatementRefDeleted(StatementRef* ref) { | 1216 void Connection::StatementRefDeleted(StatementRef* ref) { |
1041 StatementRefSet::iterator i = open_statements_.find(ref); | 1217 StatementRefSet::iterator i = open_statements_.find(ref); |
1042 if (i == open_statements_.end()) | 1218 if (i == open_statements_.end()) |
1043 DLOG(FATAL) << "Could not find statement"; | 1219 DLOG(FATAL) << "Could not find statement"; |
1044 else | 1220 else |
1045 open_statements_.erase(i); | 1221 open_statements_.erase(i); |
1046 } | 1222 } |
1047 | 1223 |
1224 void Connection::set_histogram_tag(const std::string& tag) { | |
1225 DCHECK(!is_open()); | |
1226 histogram_tag_ = tag; | |
1227 } | |
1228 | |
1048 void Connection::AddTaggedHistogram(const std::string& name, | 1229 void Connection::AddTaggedHistogram(const std::string& name, |
1049 size_t sample) const { | 1230 size_t sample) const { |
1050 if (histogram_tag_.empty()) | 1231 if (histogram_tag_.empty()) |
1051 return; | 1232 return; |
1052 | 1233 |
1053 // TODO(shess): The histogram macros create a bit of static storage | 1234 // TODO(shess): The histogram macros create a bit of static storage |
1054 // for caching the histogram object. This code shouldn't execute | 1235 // for caching the histogram object. This code shouldn't execute |
1055 // often enough for such caching to be crucial. If it becomes an | 1236 // often enough for such caching to be crucial. If it becomes an |
1056 // issue, the object could be cached alongside histogram_prefix_. | 1237 // issue, the object could be cached alongside histogram_prefix_. |
1057 std::string full_histogram_name = name + "." + histogram_tag_; | 1238 std::string full_histogram_name = name + "." + histogram_tag_; |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1131 } | 1312 } |
1132 | 1313 |
1133 // Best effort to put things back as they were before. | 1314 // Best effort to put things back as they were before. |
1134 const char kNoWritableSchema[] = "PRAGMA writable_schema = OFF"; | 1315 const char kNoWritableSchema[] = "PRAGMA writable_schema = OFF"; |
1135 ignore_result(Execute(kNoWritableSchema)); | 1316 ignore_result(Execute(kNoWritableSchema)); |
1136 | 1317 |
1137 return ret; | 1318 return ret; |
1138 } | 1319 } |
1139 | 1320 |
1140 } // namespace sql | 1321 } // namespace sql |
OLD | NEW |