Chromium Code Reviews| 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 |