Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 // A policy for storing activity log data to a database that performs | 5 // A policy for storing activity log data to a database that performs |
| 6 // aggregation to reduce the size of the database. The database layout is | 6 // aggregation to reduce the size of the database. The database layout is |
| 7 // nearly the same as FullStreamUIPolicy, which stores a complete log, with a | 7 // nearly the same as FullStreamUIPolicy, which stores a complete log, with a |
| 8 // few changes: | 8 // few changes: |
| 9 // - a "count" column is added to track how many log records were merged | 9 // - a "count" column is added to track how many log records were merged |
| 10 // together into this row | 10 // together into this row |
| (...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 216 Now() - last_database_cleaning_time_ > | 216 Now() - last_database_cleaning_time_ > |
| 217 base::TimeDelta::FromHours(kCleaningDelayInHours)); | 217 base::TimeDelta::FromHours(kCleaningDelayInHours)); |
| 218 | 218 |
| 219 if (queue.empty() && !clean_database) | 219 if (queue.empty() && !clean_database) |
| 220 return true; | 220 return true; |
| 221 | 221 |
| 222 sql::Transaction transaction(db); | 222 sql::Transaction transaction(db); |
| 223 if (!transaction.Begin()) | 223 if (!transaction.Begin()) |
| 224 return false; | 224 return false; |
| 225 | 225 |
| 226 std::string locate_str = | |
| 227 "SELECT rowid FROM " + std::string(kTableName) + | |
| 228 " WHERE time >= ? AND time < ?"; | |
|
felt
2013/09/06 20:53:57
by the way, I recently discovered the existence of
mvrable
2013/09/06 21:48:33
I hadn't thought of using BETWEEN (I usually don't
| |
| 226 std::string insert_str = | 229 std::string insert_str = |
| 227 "INSERT INTO " + std::string(kTableName) + "(count, time"; | 230 "INSERT INTO " + std::string(kTableName) + "(count, time"; |
| 228 std::string update_str = | 231 std::string update_str = |
| 229 "UPDATE " + std::string(kTableName) + | 232 "UPDATE " + std::string(kTableName) + |
| 230 " SET count = count + ?, time = max(?, time)" | 233 " SET count = count + ?, time = max(?, time)" |
| 231 " WHERE time >= ? AND time < ?"; | 234 " WHERE rowid = ?"; |
| 232 | 235 |
| 233 for (size_t i = 0; i < arraysize(matched_columns); i++) { | 236 for (size_t i = 0; i < arraysize(matched_columns); i++) { |
| 237 locate_str = base::StringPrintf( | |
| 238 "%s AND %s IS ?", locate_str.c_str(), matched_columns[i]); | |
| 234 insert_str = | 239 insert_str = |
| 235 base::StringPrintf("%s, %s", insert_str.c_str(), matched_columns[i]); | 240 base::StringPrintf("%s, %s", insert_str.c_str(), matched_columns[i]); |
| 236 update_str = base::StringPrintf( | |
| 237 "%s AND %s IS ?", update_str.c_str(), matched_columns[i]); | |
| 238 } | 241 } |
| 239 insert_str += ") VALUES (?, ?"; | 242 insert_str += ") VALUES (?, ?"; |
| 240 for (size_t i = 0; i < arraysize(matched_columns); i++) { | 243 for (size_t i = 0; i < arraysize(matched_columns); i++) { |
| 241 insert_str += ", ?"; | 244 insert_str += ", ?"; |
| 242 } | 245 } |
| 246 locate_str += " ORDER BY time DESC LIMIT 1"; | |
| 243 insert_str += ")"; | 247 insert_str += ")"; |
| 244 | 248 |
| 245 for (ActionQueue::iterator i = queue.begin(); i != queue.end(); ++i) { | 249 for (ActionQueue::iterator i = queue.begin(); i != queue.end(); ++i) { |
| 246 const Action& action = *i->first; | 250 const Action& action = *i->first; |
| 247 int count = i->second; | 251 int count = i->second; |
| 248 | 252 |
| 249 base::Time day_start = action.time().LocalMidnight(); | 253 base::Time day_start = action.time().LocalMidnight(); |
| 250 base::Time next_day = Util::AddDays(day_start, 1); | 254 base::Time next_day = Util::AddDays(day_start, 1); |
| 251 | 255 |
| 252 // The contents in values must match up with fields in matched_columns. A | 256 // The contents in values must match up with fields in matched_columns. A |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 308 } | 312 } |
| 309 | 313 |
| 310 if (action.other()) { | 314 if (action.other()) { |
| 311 if (!string_table_.StringToInt(db, Util::Serialize(action.other()), &id)) | 315 if (!string_table_.StringToInt(db, Util::Serialize(action.other()), &id)) |
| 312 return false; | 316 return false; |
| 313 matched_values.push_back(id); | 317 matched_values.push_back(id); |
| 314 } else { | 318 } else { |
| 315 matched_values.push_back(-1); | 319 matched_values.push_back(-1); |
| 316 } | 320 } |
| 317 | 321 |
| 318 // Assume there is an existing row for this action, and try to update the | 322 // Search for a matching row for this action whose count can be |
| 319 // count. | 323 // incremented. |
| 320 sql::Statement update_statement(db->GetCachedStatement( | 324 sql::Statement locate_statement(db->GetCachedStatement( |
| 321 sql::StatementID(SQL_FROM_HERE), update_str.c_str())); | 325 sql::StatementID(SQL_FROM_HERE), locate_str.c_str())); |
| 322 update_statement.BindInt(0, count); | 326 locate_statement.BindInt64(0, day_start.ToInternalValue()); |
| 323 update_statement.BindInt64(1, action.time().ToInternalValue()); | 327 locate_statement.BindInt64(1, next_day.ToInternalValue()); |
| 324 update_statement.BindInt64(2, day_start.ToInternalValue()); | |
| 325 update_statement.BindInt64(3, next_day.ToInternalValue()); | |
| 326 for (size_t j = 0; j < matched_values.size(); j++) { | 328 for (size_t j = 0; j < matched_values.size(); j++) { |
| 327 // A call to BindNull when matched_values contains -1 is likely not | 329 // A call to BindNull when matched_values contains -1 is likely not |
| 328 // necessary as parameters default to null before they are explicitly | 330 // necessary as parameters default to null before they are explicitly |
| 329 // bound. But to be completely clear, and in case a cached statement | 331 // bound. But to be completely clear, and in case a cached statement |
| 330 // ever comes with some values already bound, we bind all parameters | 332 // ever comes with some values already bound, we bind all parameters |
| 331 // (even null ones) explicitly. | 333 // (even null ones) explicitly. |
| 332 if (matched_values[j] == -1) | 334 if (matched_values[j] == -1) |
| 333 update_statement.BindNull(j + 4); | 335 locate_statement.BindNull(j + 2); |
| 334 else | 336 else |
| 335 update_statement.BindInt64(j + 4, matched_values[j]); | 337 locate_statement.BindInt64(j + 2, matched_values[j]); |
| 336 } | 338 } |
| 337 if (!update_statement.Run()) | 339 |
| 340 if (locate_statement.Step()) { | |
| 341 // A matching row was found. Update the count and time. | |
| 342 int64 rowid = locate_statement.ColumnInt64(0); | |
| 343 sql::Statement update_statement(db->GetCachedStatement( | |
| 344 sql::StatementID(SQL_FROM_HERE), update_str.c_str())); | |
| 345 update_statement.BindInt(0, count); | |
| 346 update_statement.BindInt64(1, action.time().ToInternalValue()); | |
| 347 update_statement.BindInt64(2, rowid); | |
| 348 if (!update_statement.Run()) | |
| 349 return false; | |
| 350 } else if (locate_statement.Succeeded()) { | |
| 351 // No matching row was found, so we need to insert one. | |
| 352 sql::Statement insert_statement(db->GetCachedStatement( | |
| 353 sql::StatementID(SQL_FROM_HERE), insert_str.c_str())); | |
| 354 insert_statement.BindInt(0, count); | |
| 355 insert_statement.BindInt64(1, action.time().ToInternalValue()); | |
| 356 for (size_t j = 0; j < matched_values.size(); j++) { | |
| 357 if (matched_values[j] == -1) | |
| 358 insert_statement.BindNull(j + 2); | |
| 359 else | |
| 360 insert_statement.BindInt64(j + 2, matched_values[j]); | |
| 361 } | |
| 362 if (!insert_statement.Run()) | |
| 363 return false; | |
| 364 } else { | |
| 365 // Database error. | |
| 338 return false; | 366 return false; |
| 339 | |
| 340 // Check if the update succeeded (was the count of updated rows non-zero)? | |
| 341 // If it failed because no matching row existed, fall back to inserting a | |
| 342 // new record. | |
| 343 if (db->GetLastChangeCount() > 0) { | |
| 344 if (db->GetLastChangeCount() > 1) { | |
| 345 LOG(WARNING) << "Found and updated multiple rows in the activity log " | |
| 346 << "database; counts may be off!"; | |
| 347 } | |
| 348 continue; | |
| 349 } | 367 } |
| 350 sql::Statement insert_statement(db->GetCachedStatement( | |
| 351 sql::StatementID(SQL_FROM_HERE), insert_str.c_str())); | |
| 352 insert_statement.BindInt(0, count); | |
| 353 insert_statement.BindInt64(1, action.time().ToInternalValue()); | |
| 354 for (size_t j = 0; j < matched_values.size(); j++) { | |
| 355 if (matched_values[j] == -1) | |
| 356 insert_statement.BindNull(j + 2); | |
| 357 else | |
| 358 insert_statement.BindInt64(j + 2, matched_values[j]); | |
| 359 } | |
| 360 if (!insert_statement.Run()) | |
| 361 return false; | |
| 362 } | 368 } |
| 363 | 369 |
| 364 if (clean_database) { | 370 if (clean_database) { |
| 365 base::Time cutoff = (Now() - retention_time()).LocalMidnight(); | 371 base::Time cutoff = (Now() - retention_time()).LocalMidnight(); |
| 366 if (!CleanOlderThan(db, cutoff)) | 372 if (!CleanOlderThan(db, cutoff)) |
| 367 return false; | 373 return false; |
| 368 last_database_cleaning_time_ = Now(); | 374 last_database_cleaning_time_ = Now(); |
| 369 } | 375 } |
| 370 | 376 |
| 371 if (!transaction.Commit()) | 377 if (!transaction.Commit()) |
| (...skipping 368 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 740 return true; | 746 return true; |
| 741 } | 747 } |
| 742 | 748 |
| 743 void CountingPolicy::Close() { | 749 void CountingPolicy::Close() { |
| 744 // The policy object should have never been created if there's no DB thread. | 750 // The policy object should have never been created if there's no DB thread. |
| 745 DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::DB)); | 751 DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::DB)); |
| 746 ScheduleAndForget(activity_database(), &ActivityDatabase::Close); | 752 ScheduleAndForget(activity_database(), &ActivityDatabase::Close); |
| 747 } | 753 } |
| 748 | 754 |
| 749 } // namespace extensions | 755 } // namespace extensions |
| OLD | NEW |