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 "chrome/browser/history/visitsegment_database.h" | 5 #include "chrome/browser/history/visitsegment_database.h" |
| 6 | 6 |
| 7 #include <math.h> | 7 #include <math.h> |
| 8 | 8 |
| 9 #include <algorithm> | 9 #include <algorithm> |
| 10 #include <string> | 10 #include <string> |
| 11 #include <vector> | 11 #include <vector> |
| 12 | 12 |
| 13 #include "base/command_line.h" | |
| 14 #include "base/logging.h" | 13 #include "base/logging.h" |
| 15 #include "base/stl_util.h" | 14 #include "base/stl_util.h" |
| 16 #include "base/strings/string_util.h" | 15 #include "base/strings/string_util.h" |
| 17 #include "base/strings/utf_string_conversions.h" | 16 #include "base/strings/utf_string_conversions.h" |
| 18 #include "chrome/browser/history/page_usage_data.h" | 17 #include "chrome/browser/history/page_usage_data.h" |
| 19 #include "chrome/common/chrome_switches.h" | |
| 20 #include "sql/statement.h" | 18 #include "sql/statement.h" |
| 21 #include "sql/transaction.h" | 19 #include "sql/transaction.h" |
| 22 | 20 |
| 23 // The following tables are used to store url segment information. | 21 // The following tables are used to store url segment information. |
| 24 // | 22 // |
| 25 // segments | 23 // segments |
| 26 // id Primary key | 24 // id Primary key |
| 27 // name A unique string to represent that segment. (URL derived) | 25 // name A unique string to represent that segment. (URL derived) |
| 28 // url_id ID of the url currently used to represent this segment. | 26 // url_id ID of the url currently used to represent this segment. |
| 29 // | 27 // |
| 30 // segment_usage | 28 // segment_usage |
| 31 // id Primary key | 29 // id Primary key |
| 32 // segment_id Corresponding segment id | 30 // segment_id Corresponding segment id |
| 33 // time_slot time stamp identifying for what day this entry is about | 31 // time_slot time stamp identifying for what day this entry is about |
| 34 // visit_count Number of visit in the segment | 32 // visit_count Number of visit in the segment |
| 35 // | 33 // |
| 36 // segment_duration | |
|
sky
2013/12/18 00:13:59
This was only ever used if you explicitly turned o
| |
| 37 // id Primary key | |
| 38 // segment_id Corresponding segment id | |
| 39 // time_slot time stamp identifying what day this entry is for | |
| 40 // duration Total time during the time_slot the user has been on | |
| 41 // the page. This is a serialized TimeDelta value. | |
| 42 // segment_duration is only created if chrome::kTrackActiveVisitTime is set. | |
| 43 | 34 |
| 44 namespace history { | 35 namespace history { |
| 45 | 36 |
| 46 VisitSegmentDatabase::VisitSegmentDatabase() | 37 VisitSegmentDatabase::VisitSegmentDatabase() { |
| 47 : has_duration_table_(CommandLine::ForCurrentProcess()->HasSwitch( | |
| 48 switches::kTrackActiveVisitTime)) { | |
| 49 } | 38 } |
| 50 | 39 |
| 51 VisitSegmentDatabase::~VisitSegmentDatabase() { | 40 VisitSegmentDatabase::~VisitSegmentDatabase() { |
| 52 } | 41 } |
| 53 | 42 |
| 54 bool VisitSegmentDatabase::InitSegmentTables() { | 43 bool VisitSegmentDatabase::InitSegmentTables() { |
| 55 // Segments table. | 44 // Segments table. |
| 56 if (!GetDB().DoesTableExist("segments")) { | 45 if (!GetDB().DoesTableExist("segments")) { |
| 57 if (!GetDB().Execute("CREATE TABLE segments (" | 46 if (!GetDB().Execute("CREATE TABLE segments (" |
| 58 "id INTEGER PRIMARY KEY," | 47 "id INTEGER PRIMARY KEY," |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 87 "segment_usage(time_slot, segment_id)")) { | 76 "segment_usage(time_slot, segment_id)")) { |
| 88 return false; | 77 return false; |
| 89 } | 78 } |
| 90 } | 79 } |
| 91 | 80 |
| 92 // Added in a later version, so we always need to try to creat this index. | 81 // Added in a later version, so we always need to try to creat this index. |
| 93 if (!GetDB().Execute("CREATE INDEX IF NOT EXISTS segments_usage_seg_id " | 82 if (!GetDB().Execute("CREATE INDEX IF NOT EXISTS segments_usage_seg_id " |
| 94 "ON segment_usage(segment_id)")) | 83 "ON segment_usage(segment_id)")) |
| 95 return false; | 84 return false; |
| 96 | 85 |
| 97 // TODO(sky): if we decide to keep this feature duration should be added to | |
| 98 // segument_usage. | |
| 99 if (has_duration_table_ && !GetDB().DoesTableExist("segment_duration")) { | |
| 100 if (!GetDB().Execute("CREATE TABLE segment_duration (" | |
| 101 "id INTEGER PRIMARY KEY," | |
| 102 "segment_id INTEGER NOT NULL," | |
| 103 "time_slot INTEGER NOT NULL," | |
| 104 "duration INTEGER DEFAULT 0 NOT NULL)")) { | |
| 105 return false; | |
| 106 } | |
| 107 if (!GetDB().Execute( | |
| 108 "CREATE INDEX segment_duration_time_slot_segment_id ON " | |
| 109 "segment_duration(time_slot, segment_id)")) { | |
| 110 return false; | |
| 111 } | |
| 112 } else if (!has_duration_table_ && | |
| 113 !GetDB().Execute("DROP TABLE IF EXISTS segment_duration")) { | |
| 114 return false; | |
| 115 } | |
| 116 | |
| 117 return true; | 86 return true; |
| 118 } | 87 } |
| 119 | 88 |
| 120 bool VisitSegmentDatabase::DropSegmentTables() { | 89 bool VisitSegmentDatabase::DropSegmentTables() { |
| 121 // Dropping the tables will implicitly delete the indices. | 90 // Dropping the tables will implicitly delete the indices. |
| 122 return GetDB().Execute("DROP TABLE segments") && | 91 return GetDB().Execute("DROP TABLE segments") && |
| 123 GetDB().Execute("DROP TABLE segment_usage") && | 92 GetDB().Execute("DROP TABLE segment_usage"); |
| 124 GetDB().Execute("DROP TABLE IF EXISTS segment_duration"); | |
| 125 } | 93 } |
| 126 | 94 |
| 127 // Note: the segment name is derived from the URL but is not a URL. It is | 95 // Note: the segment name is derived from the URL but is not a URL. It is |
| 128 // a string that can be easily recreated from various URLS. Maybe this should | 96 // a string that can be easily recreated from various URLS. Maybe this should |
| 129 // be an MD5 to limit the length. | 97 // be an MD5 to limit the length. |
| 130 // | 98 // |
| 131 // static | 99 // static |
| 132 std::string VisitSegmentDatabase::ComputeSegmentName(const GURL& url) { | 100 std::string VisitSegmentDatabase::ComputeSegmentName(const GURL& url) { |
| 133 // TODO(brettw) this should probably use the registry controlled | 101 // TODO(brettw) this should probably use the registry controlled |
| 134 // domains service. | 102 // domains service. |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 148 // Remove other stuff we don't want. | 116 // Remove other stuff we don't want. |
| 149 r.ClearUsername(); | 117 r.ClearUsername(); |
| 150 r.ClearPassword(); | 118 r.ClearPassword(); |
| 151 r.ClearQuery(); | 119 r.ClearQuery(); |
| 152 r.ClearRef(); | 120 r.ClearRef(); |
| 153 r.ClearPort(); | 121 r.ClearPort(); |
| 154 | 122 |
| 155 return url.ReplaceComponents(r).spec(); | 123 return url.ReplaceComponents(r).spec(); |
| 156 } | 124 } |
| 157 | 125 |
| 158 // static | |
| 159 base::Time VisitSegmentDatabase::SegmentTime(base::Time time) { | |
| 160 return time.LocalMidnight(); | |
| 161 } | |
| 162 | |
| 163 SegmentID VisitSegmentDatabase::GetSegmentNamed( | 126 SegmentID VisitSegmentDatabase::GetSegmentNamed( |
| 164 const std::string& segment_name) { | 127 const std::string& segment_name) { |
| 165 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 128 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
| 166 "SELECT id FROM segments WHERE name = ?")); | 129 "SELECT id FROM segments WHERE name = ?")); |
| 167 statement.BindString(0, segment_name); | 130 statement.BindString(0, segment_name); |
| 168 | 131 |
| 169 if (statement.Step()) | 132 if (statement.Step()) |
| 170 return statement.ColumnInt64(0); | 133 return statement.ColumnInt64(0); |
| 171 return 0; | 134 return 0; |
| 172 } | 135 } |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 199 statement.BindInt64(1, url_id); | 162 statement.BindInt64(1, url_id); |
| 200 | 163 |
| 201 if (statement.Run()) | 164 if (statement.Run()) |
| 202 return GetDB().GetLastInsertRowId(); | 165 return GetDB().GetLastInsertRowId(); |
| 203 return 0; | 166 return 0; |
| 204 } | 167 } |
| 205 | 168 |
| 206 bool VisitSegmentDatabase::IncreaseSegmentVisitCount(SegmentID segment_id, | 169 bool VisitSegmentDatabase::IncreaseSegmentVisitCount(SegmentID segment_id, |
| 207 base::Time ts, | 170 base::Time ts, |
| 208 int amount) { | 171 int amount) { |
| 209 base::Time t = SegmentTime(ts); | 172 base::Time t = ts.LocalMidnight(); |
| 210 | 173 |
| 211 sql::Statement select(GetDB().GetCachedStatement(SQL_FROM_HERE, | 174 sql::Statement select(GetDB().GetCachedStatement(SQL_FROM_HERE, |
| 212 "SELECT id, visit_count FROM segment_usage " | 175 "SELECT id, visit_count FROM segment_usage " |
| 213 "WHERE time_slot = ? AND segment_id = ?")); | 176 "WHERE time_slot = ? AND segment_id = ?")); |
| 214 select.BindInt64(0, t.ToInternalValue()); | 177 select.BindInt64(0, t.ToInternalValue()); |
| 215 select.BindInt64(1, segment_id); | 178 select.BindInt64(1, segment_id); |
| 216 | 179 |
| 217 if (!select.is_valid()) | 180 if (!select.is_valid()) |
| 218 return false; | 181 return false; |
| 219 | 182 |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 232 insert.BindInt64(1, t.ToInternalValue()); | 195 insert.BindInt64(1, t.ToInternalValue()); |
| 233 insert.BindInt64(2, static_cast<int64>(amount)); | 196 insert.BindInt64(2, static_cast<int64>(amount)); |
| 234 | 197 |
| 235 return insert.Run(); | 198 return insert.Run(); |
| 236 } | 199 } |
| 237 } | 200 } |
| 238 | 201 |
| 239 void VisitSegmentDatabase::QuerySegmentUsage( | 202 void VisitSegmentDatabase::QuerySegmentUsage( |
| 240 base::Time from_time, | 203 base::Time from_time, |
| 241 int max_result_count, | 204 int max_result_count, |
| 242 std::vector<PageUsageData*>* result) { | 205 std::vector<PageUsageData*>* results) { |
| 206 // This function gathers the highest-ranked segments in two queries. | |
| 207 // The first gathers scores for all segments. | |
| 208 // The second gathers segment data (url, title, etc.) for the highest-ranked | |
| 209 // segments. | |
| 210 | |
| 243 // Gather all the segment scores. | 211 // Gather all the segment scores. |
| 244 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 212 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
| 245 "SELECT segment_id, time_slot, visit_count " | 213 "SELECT segment_id, time_slot, visit_count " |
| 246 "FROM segment_usage WHERE time_slot >= ? " | 214 "FROM segment_usage WHERE time_slot >= ? " |
| 247 "ORDER BY segment_id")); | 215 "ORDER BY segment_id")); |
| 248 if (!statement.is_valid()) | 216 if (!statement.is_valid()) |
| 249 return; | 217 return; |
| 250 | 218 |
| 251 QuerySegmentsCommon(&statement, from_time, max_result_count, | 219 base::Time ts = from_time.LocalMidnight(); |
| 252 QUERY_VISIT_COUNT, result); | 220 statement.BindInt64(0, ts.ToInternalValue()); |
| 253 } | |
| 254 | |
| 255 bool VisitSegmentDatabase::DeleteSegmentData(base::Time older_than) { | |
| 256 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | |
| 257 "DELETE FROM segment_usage WHERE time_slot < ?")); | |
| 258 statement.BindInt64(0, SegmentTime(older_than).ToInternalValue()); | |
| 259 | |
| 260 if (!statement.Run()) | |
| 261 return false; | |
| 262 | |
| 263 if (!has_duration_table_) | |
| 264 return true; | |
| 265 | |
| 266 sql::Statement duration_statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | |
| 267 "DELETE FROM segment_duration WHERE time_slot < ?")); | |
| 268 duration_statement.BindInt64(0, SegmentTime(older_than).ToInternalValue()); | |
| 269 | |
| 270 return duration_statement.Run(); | |
| 271 } | |
| 272 | |
| 273 bool VisitSegmentDatabase::DeleteSegmentForURL(URLID url_id) { | |
| 274 sql::Statement delete_usage(GetDB().GetCachedStatement(SQL_FROM_HERE, | |
| 275 "DELETE FROM segment_usage WHERE segment_id IN " | |
| 276 "(SELECT id FROM segments WHERE url_id = ?)")); | |
| 277 delete_usage.BindInt64(0, url_id); | |
| 278 | |
| 279 if (!delete_usage.Run()) | |
| 280 return false; | |
| 281 | |
| 282 if (has_duration_table_) { | |
| 283 sql::Statement delete_duration(GetDB().GetCachedStatement(SQL_FROM_HERE, | |
| 284 "DELETE FROM segment_duration WHERE segment_id IN " | |
| 285 "(SELECT id FROM segments WHERE url_id = ?)")); | |
| 286 delete_duration.BindInt64(0, url_id); | |
| 287 | |
| 288 if (!delete_duration.Run()) | |
| 289 return false; | |
| 290 } | |
| 291 | |
| 292 sql::Statement delete_seg(GetDB().GetCachedStatement(SQL_FROM_HERE, | |
| 293 "DELETE FROM segments WHERE url_id = ?")); | |
| 294 delete_seg.BindInt64(0, url_id); | |
| 295 | |
| 296 return delete_seg.Run(); | |
| 297 } | |
| 298 | |
| 299 SegmentDurationID VisitSegmentDatabase::CreateSegmentDuration( | |
| 300 SegmentID segment_id, | |
| 301 base::Time time, | |
| 302 base::TimeDelta delta) { | |
| 303 if (!has_duration_table_) | |
| 304 return 0; | |
| 305 | |
| 306 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | |
| 307 "INSERT INTO segment_duration (segment_id, time_slot, duration) " | |
| 308 "VALUES (?,?,?)")); | |
| 309 statement.BindInt64(0, segment_id); | |
| 310 statement.BindInt64(1, SegmentTime(time).ToInternalValue()); | |
| 311 statement.BindInt64(2, delta.ToInternalValue()); | |
| 312 return statement.Run() ? GetDB().GetLastInsertRowId() : 0; | |
| 313 } | |
| 314 | |
| 315 bool VisitSegmentDatabase::SetSegmentDuration(SegmentDurationID duration_id, | |
| 316 base::TimeDelta time_delta) { | |
| 317 if (!has_duration_table_) | |
| 318 return false; | |
| 319 | |
| 320 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | |
| 321 "UPDATE segment_duration SET duration = ? WHERE id = ?")); | |
| 322 statement.BindInt64(0, time_delta.ToInternalValue()); | |
| 323 statement.BindInt64(1, duration_id); | |
| 324 return statement.Run(); | |
| 325 } | |
| 326 | |
| 327 bool VisitSegmentDatabase::GetSegmentDuration(SegmentID segment_id, | |
| 328 base::Time time, | |
| 329 SegmentDurationID* duration_id, | |
| 330 base::TimeDelta* time_delta) { | |
| 331 if (!has_duration_table_) | |
| 332 return false; | |
| 333 | |
| 334 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | |
| 335 "SELECT id, duration FROM segment_duration " | |
| 336 "WHERE segment_id = ? AND time_slot = ? ")); | |
| 337 if (!statement.is_valid()) | |
| 338 return false; | |
| 339 | |
| 340 statement.BindInt64(0, segment_id); | |
| 341 statement.BindInt64(1, SegmentTime(time).ToInternalValue()); | |
| 342 | |
| 343 if (!statement.Step()) | |
| 344 return false; | |
| 345 | |
| 346 *duration_id = statement.ColumnInt64(0); | |
| 347 *time_delta = base::TimeDelta::FromInternalValue(statement.ColumnInt64(1)); | |
| 348 return true; | |
| 349 } | |
| 350 | |
| 351 void VisitSegmentDatabase::QuerySegmentDuration( | |
| 352 base::Time from_time, | |
| 353 int max_result_count, | |
| 354 std::vector<PageUsageData*>* result) { | |
| 355 if (!has_duration_table_) | |
| 356 return; | |
| 357 | |
| 358 // Gather all the segment scores. | |
| 359 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | |
| 360 "SELECT segment_id, time_slot, duration " | |
| 361 "FROM segment_duration WHERE time_slot >= ? " | |
| 362 "ORDER BY segment_id")); | |
| 363 if (!statement.is_valid()) | |
| 364 return; | |
| 365 | |
| 366 QuerySegmentsCommon(&statement, from_time, max_result_count, QUERY_DURATION, | |
| 367 result); | |
| 368 } | |
| 369 | |
| 370 bool VisitSegmentDatabase::MigratePresentationIndex() { | |
| 371 sql::Transaction transaction(&GetDB()); | |
| 372 return transaction.Begin() && | |
| 373 GetDB().Execute("DROP TABLE presentation") && | |
| 374 GetDB().Execute("CREATE TABLE segments_tmp (" | |
| 375 "id INTEGER PRIMARY KEY," | |
| 376 "name VARCHAR," | |
| 377 "url_id INTEGER NON NULL)") && | |
| 378 GetDB().Execute("INSERT INTO segments_tmp SELECT " | |
| 379 "id, name, url_id FROM segments") && | |
| 380 GetDB().Execute("DROP TABLE segments") && | |
| 381 GetDB().Execute("ALTER TABLE segments_tmp RENAME TO segments") && | |
| 382 transaction.Commit(); | |
| 383 } | |
| 384 | |
| 385 | |
| 386 void VisitSegmentDatabase::QuerySegmentsCommon( | |
| 387 sql::Statement* statement, | |
| 388 base::Time from_time, | |
| 389 int max_result_count, | |
| 390 QueryType query_type, | |
| 391 std::vector<PageUsageData*>* result) { | |
| 392 // This function gathers the highest-ranked segments in two queries. | |
| 393 // The first gathers scores for all segments. | |
| 394 // The second gathers segment data (url, title, etc.) for the highest-ranked | |
| 395 // segments. | |
| 396 | |
| 397 base::Time ts = SegmentTime(from_time); | |
| 398 statement->BindInt64(0, ts.ToInternalValue()); | |
| 399 | 221 |
| 400 base::Time now = base::Time::Now(); | 222 base::Time now = base::Time::Now(); |
| 401 SegmentID last_segment_id = 0; | 223 SegmentID last_segment_id = 0; |
| 402 PageUsageData* pud = NULL; | 224 PageUsageData* pud = NULL; |
| 403 float score = 0; | 225 float score = 0; |
| 404 base::TimeDelta duration; | 226 while (statement.Step()) { |
| 405 while (statement->Step()) { | 227 SegmentID segment_id = statement.ColumnInt64(0); |
| 406 SegmentID segment_id = statement->ColumnInt64(0); | |
| 407 if (segment_id != last_segment_id) { | 228 if (segment_id != last_segment_id) { |
| 408 if (pud) { | 229 if (pud) { |
| 409 pud->SetScore(score); | 230 pud->SetScore(score); |
| 410 pud->SetDuration(duration); | 231 results->push_back(pud); |
| 411 result->push_back(pud); | |
| 412 } | 232 } |
| 413 | 233 |
| 414 pud = new PageUsageData(segment_id); | 234 pud = new PageUsageData(segment_id); |
| 415 score = 0; | 235 score = 0; |
| 416 last_segment_id = segment_id; | 236 last_segment_id = segment_id; |
| 417 duration = base::TimeDelta(); | |
| 418 } | 237 } |
| 419 | 238 |
| 420 base::Time timeslot = | 239 base::Time timeslot = |
| 421 base::Time::FromInternalValue(statement->ColumnInt64(1)); | 240 base::Time::FromInternalValue(statement.ColumnInt64(1)); |
| 422 int count; | 241 int visit_count = statement.ColumnInt(2); |
| 423 if (query_type == QUERY_VISIT_COUNT) { | 242 int days_ago = (now - timeslot).InDays(); |
| 424 count = statement->ColumnInt(2); | |
| 425 } else { | |
| 426 base::TimeDelta current_duration( | |
| 427 base::TimeDelta::FromInternalValue(statement->ColumnInt64(2))); | |
| 428 duration += current_duration; | |
| 429 // Souldn't overflow since we group by day. | |
| 430 count = static_cast<int>(current_duration.InSeconds()); | |
| 431 } | |
| 432 float day_score = 1.0f + log(static_cast<float>(count)); | |
| 433 | 243 |
| 244 // Score for this day in isolation. | |
| 245 float day_visits_score = 1.0f + log(static_cast<float>(visit_count)); | |
| 434 // Recent visits count more than historical ones, so we multiply in a boost | 246 // Recent visits count more than historical ones, so we multiply in a boost |
| 435 // related to how long ago this day was. | 247 // related to how long ago this day was. |
| 436 // This boost is a curve that smoothly goes through these values: | 248 // This boost is a curve that smoothly goes through these values: |
| 437 // Today gets 3x, a week ago 2x, three weeks ago 1.5x, falling off to 1x | 249 // Today gets 3x, a week ago 2x, three weeks ago 1.5x, falling off to 1x |
| 438 // at the limit of how far we reach into the past. | 250 // at the limit of how far we reach into the past. |
| 439 int days_ago = (now - timeslot).InDays(); | |
| 440 float recency_boost = 1.0f + (2.0f * (1.0f / (1.0f + days_ago/7.0f))); | 251 float recency_boost = 1.0f + (2.0f * (1.0f / (1.0f + days_ago/7.0f))); |
| 441 score += recency_boost * day_score; | 252 score += recency_boost * day_visits_score; |
| 442 } | 253 } |
| 443 | 254 |
| 444 if (pud) { | 255 if (pud) { |
| 445 pud->SetScore(score); | 256 pud->SetScore(score); |
| 446 pud->SetDuration(duration); | 257 results->push_back(pud); |
| 447 result->push_back(pud); | |
| 448 } | 258 } |
| 449 | 259 |
| 450 // Limit to the top kResultCount results. | 260 // Limit to the top kResultCount results. |
| 451 std::sort(result->begin(), result->end(), PageUsageData::Predicate); | 261 std::sort(results->begin(), results->end(), PageUsageData::Predicate); |
| 452 if (static_cast<int>(result->size()) > max_result_count) { | 262 if (static_cast<int>(results->size()) > max_result_count) { |
| 453 STLDeleteContainerPointers(result->begin() + max_result_count, | 263 STLDeleteContainerPointers(results->begin() + max_result_count, |
| 454 result->end()); | 264 results->end()); |
| 455 result->resize(max_result_count); | 265 results->resize(max_result_count); |
| 456 } | 266 } |
| 457 | 267 |
| 458 // Now fetch the details about the entries we care about. | 268 // Now fetch the details about the entries we care about. |
| 459 sql::Statement statement2(GetDB().GetCachedStatement(SQL_FROM_HERE, | 269 sql::Statement statement2(GetDB().GetCachedStatement(SQL_FROM_HERE, |
| 460 "SELECT urls.url, urls.title FROM urls " | 270 "SELECT urls.url, urls.title FROM urls " |
| 461 "JOIN segments ON segments.url_id = urls.id " | 271 "JOIN segments ON segments.url_id = urls.id " |
| 462 "WHERE segments.id = ?")); | 272 "WHERE segments.id = ?")); |
| 463 | 273 |
| 464 if (!statement2.is_valid()) | 274 if (!statement2.is_valid()) |
| 465 return; | 275 return; |
| 466 | 276 |
| 467 for (size_t i = 0; i < result->size(); ++i) { | 277 for (size_t i = 0; i < results->size(); ++i) { |
| 468 PageUsageData* pud = (*result)[i]; | 278 PageUsageData* pud = (*results)[i]; |
| 469 statement2.BindInt64(0, pud->GetID()); | 279 statement2.BindInt64(0, pud->GetID()); |
| 470 if (statement2.Step()) { | 280 if (statement2.Step()) { |
| 471 pud->SetURL(GURL(statement2.ColumnString(0))); | 281 pud->SetURL(GURL(statement2.ColumnString(0))); |
| 472 pud->SetTitle(statement2.ColumnString16(1)); | 282 pud->SetTitle(statement2.ColumnString16(1)); |
| 473 } | 283 } |
| 474 statement2.Reset(true); | 284 statement2.Reset(true); |
| 475 } | 285 } |
| 476 } | 286 } |
| 477 | 287 |
| 288 bool VisitSegmentDatabase::DeleteSegmentData(base::Time older_than) { | |
| 289 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | |
| 290 "DELETE FROM segment_usage WHERE time_slot < ?")); | |
| 291 statement.BindInt64(0, older_than.LocalMidnight().ToInternalValue()); | |
| 292 | |
| 293 return statement.Run(); | |
| 294 } | |
| 295 | |
| 296 bool VisitSegmentDatabase::DeleteSegmentForURL(URLID url_id) { | |
| 297 sql::Statement delete_usage(GetDB().GetCachedStatement(SQL_FROM_HERE, | |
| 298 "DELETE FROM segment_usage WHERE segment_id IN " | |
| 299 "(SELECT id FROM segments WHERE url_id = ?)")); | |
| 300 delete_usage.BindInt64(0, url_id); | |
| 301 | |
| 302 if (!delete_usage.Run()) | |
| 303 return false; | |
| 304 | |
| 305 sql::Statement delete_seg(GetDB().GetCachedStatement(SQL_FROM_HERE, | |
| 306 "DELETE FROM segments WHERE url_id = ?")); | |
| 307 delete_seg.BindInt64(0, url_id); | |
| 308 | |
| 309 return delete_seg.Run(); | |
| 310 } | |
| 311 | |
| 312 bool VisitSegmentDatabase::MigratePresentationIndex() { | |
| 313 sql::Transaction transaction(&GetDB()); | |
| 314 return transaction.Begin() && | |
| 315 GetDB().Execute("DROP TABLE presentation") && | |
| 316 GetDB().Execute("CREATE TABLE segments_tmp (" | |
| 317 "id INTEGER PRIMARY KEY," | |
| 318 "name VARCHAR," | |
| 319 "url_id INTEGER NON NULL)") && | |
| 320 GetDB().Execute("INSERT INTO segments_tmp SELECT " | |
| 321 "id, name, url_id FROM segments") && | |
| 322 GetDB().Execute("DROP TABLE segments") && | |
| 323 GetDB().Execute("ALTER TABLE segments_tmp RENAME TO segments") && | |
| 324 transaction.Commit(); | |
| 325 } | |
| 326 | |
| 478 } // namespace history | 327 } // namespace history |
| OLD | NEW |