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 // Adding an Action to the database is a two step process that depends on |
| 227 // whether the count on an existing row can be incremented or a new row needs |
| 228 // to be inserted. |
| 229 // 1. Run the query in locate_str to search for a row which matches and can |
| 230 // have the count incremented. |
| 231 // 2a. If found, increment the count using update_str and the rowid found in |
| 232 // step 1, or |
| 233 // 2b. If not found, insert a new row using insert_str. |
| 234 std::string locate_str = |
| 235 "SELECT rowid FROM " + std::string(kTableName) + |
| 236 " WHERE time >= ? AND time < ?"; |
226 std::string insert_str = | 237 std::string insert_str = |
227 "INSERT INTO " + std::string(kTableName) + "(count, time"; | 238 "INSERT INTO " + std::string(kTableName) + "(count, time"; |
228 std::string update_str = | 239 std::string update_str = |
229 "UPDATE " + std::string(kTableName) + | 240 "UPDATE " + std::string(kTableName) + |
230 " SET count = count + ?, time = max(?, time)" | 241 " SET count = count + ?, time = max(?, time)" |
231 " WHERE time >= ? AND time < ?"; | 242 " WHERE rowid = ?"; |
232 | 243 |
233 for (size_t i = 0; i < arraysize(matched_columns); i++) { | 244 for (size_t i = 0; i < arraysize(matched_columns); i++) { |
| 245 locate_str = base::StringPrintf( |
| 246 "%s AND %s IS ?", locate_str.c_str(), matched_columns[i]); |
234 insert_str = | 247 insert_str = |
235 base::StringPrintf("%s, %s", insert_str.c_str(), matched_columns[i]); | 248 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 } | 249 } |
239 insert_str += ") VALUES (?, ?"; | 250 insert_str += ") VALUES (?, ?"; |
240 for (size_t i = 0; i < arraysize(matched_columns); i++) { | 251 for (size_t i = 0; i < arraysize(matched_columns); i++) { |
241 insert_str += ", ?"; | 252 insert_str += ", ?"; |
242 } | 253 } |
| 254 locate_str += " ORDER BY time DESC LIMIT 1"; |
243 insert_str += ")"; | 255 insert_str += ")"; |
244 | 256 |
245 for (ActionQueue::iterator i = queue.begin(); i != queue.end(); ++i) { | 257 for (ActionQueue::iterator i = queue.begin(); i != queue.end(); ++i) { |
246 const Action& action = *i->first; | 258 const Action& action = *i->first; |
247 int count = i->second; | 259 int count = i->second; |
248 | 260 |
249 base::Time day_start = action.time().LocalMidnight(); | 261 base::Time day_start = action.time().LocalMidnight(); |
250 base::Time next_day = Util::AddDays(day_start, 1); | 262 base::Time next_day = Util::AddDays(day_start, 1); |
251 | 263 |
252 // The contents in values must match up with fields in matched_columns. A | 264 // 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 } | 320 } |
309 | 321 |
310 if (action.other()) { | 322 if (action.other()) { |
311 if (!string_table_.StringToInt(db, Util::Serialize(action.other()), &id)) | 323 if (!string_table_.StringToInt(db, Util::Serialize(action.other()), &id)) |
312 return false; | 324 return false; |
313 matched_values.push_back(id); | 325 matched_values.push_back(id); |
314 } else { | 326 } else { |
315 matched_values.push_back(-1); | 327 matched_values.push_back(-1); |
316 } | 328 } |
317 | 329 |
318 // Assume there is an existing row for this action, and try to update the | 330 // Search for a matching row for this action whose count can be |
319 // count. | 331 // incremented. |
320 sql::Statement update_statement(db->GetCachedStatement( | 332 sql::Statement locate_statement(db->GetCachedStatement( |
321 sql::StatementID(SQL_FROM_HERE), update_str.c_str())); | 333 sql::StatementID(SQL_FROM_HERE), locate_str.c_str())); |
322 update_statement.BindInt(0, count); | 334 locate_statement.BindInt64(0, day_start.ToInternalValue()); |
323 update_statement.BindInt64(1, action.time().ToInternalValue()); | 335 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++) { | 336 for (size_t j = 0; j < matched_values.size(); j++) { |
327 // A call to BindNull when matched_values contains -1 is likely not | 337 // A call to BindNull when matched_values contains -1 is likely not |
328 // necessary as parameters default to null before they are explicitly | 338 // necessary as parameters default to null before they are explicitly |
329 // bound. But to be completely clear, and in case a cached statement | 339 // bound. But to be completely clear, and in case a cached statement |
330 // ever comes with some values already bound, we bind all parameters | 340 // ever comes with some values already bound, we bind all parameters |
331 // (even null ones) explicitly. | 341 // (even null ones) explicitly. |
332 if (matched_values[j] == -1) | 342 if (matched_values[j] == -1) |
333 update_statement.BindNull(j + 4); | 343 locate_statement.BindNull(j + 2); |
334 else | 344 else |
335 update_statement.BindInt64(j + 4, matched_values[j]); | 345 locate_statement.BindInt64(j + 2, matched_values[j]); |
336 } | 346 } |
337 if (!update_statement.Run()) | 347 |
| 348 if (locate_statement.Step()) { |
| 349 // A matching row was found. Update the count and time. |
| 350 int64 rowid = locate_statement.ColumnInt64(0); |
| 351 sql::Statement update_statement(db->GetCachedStatement( |
| 352 sql::StatementID(SQL_FROM_HERE), update_str.c_str())); |
| 353 update_statement.BindInt(0, count); |
| 354 update_statement.BindInt64(1, action.time().ToInternalValue()); |
| 355 update_statement.BindInt64(2, rowid); |
| 356 if (!update_statement.Run()) |
| 357 return false; |
| 358 } else if (locate_statement.Succeeded()) { |
| 359 // No matching row was found, so we need to insert one. |
| 360 sql::Statement insert_statement(db->GetCachedStatement( |
| 361 sql::StatementID(SQL_FROM_HERE), insert_str.c_str())); |
| 362 insert_statement.BindInt(0, count); |
| 363 insert_statement.BindInt64(1, action.time().ToInternalValue()); |
| 364 for (size_t j = 0; j < matched_values.size(); j++) { |
| 365 if (matched_values[j] == -1) |
| 366 insert_statement.BindNull(j + 2); |
| 367 else |
| 368 insert_statement.BindInt64(j + 2, matched_values[j]); |
| 369 } |
| 370 if (!insert_statement.Run()) |
| 371 return false; |
| 372 } else { |
| 373 // Database error. |
338 return false; | 374 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 } | 375 } |
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 } | 376 } |
363 | 377 |
364 if (clean_database) { | 378 if (clean_database) { |
365 base::Time cutoff = (Now() - retention_time()).LocalMidnight(); | 379 base::Time cutoff = (Now() - retention_time()).LocalMidnight(); |
366 if (!CleanOlderThan(db, cutoff)) | 380 if (!CleanOlderThan(db, cutoff)) |
367 return false; | 381 return false; |
368 last_database_cleaning_time_ = Now(); | 382 last_database_cleaning_time_ = Now(); |
369 } | 383 } |
370 | 384 |
371 if (!transaction.Commit()) | 385 if (!transaction.Commit()) |
(...skipping 368 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
740 return true; | 754 return true; |
741 } | 755 } |
742 | 756 |
743 void CountingPolicy::Close() { | 757 void CountingPolicy::Close() { |
744 // The policy object should have never been created if there's no DB thread. | 758 // The policy object should have never been created if there's no DB thread. |
745 DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::DB)); | 759 DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::DB)); |
746 ScheduleAndForget(activity_database(), &ActivityDatabase::Close); | 760 ScheduleAndForget(activity_database(), &ActivityDatabase::Close); |
747 } | 761 } |
748 | 762 |
749 } // namespace extensions | 763 } // namespace extensions |
OLD | NEW |