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 |