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 bool 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); |
| 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 |