1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/indexed_db/indexed_db_leveldb_operations.h"
6
7 #include "base/json/json_reader.h"
8 #include "base/metrics/histogram_functions.h"
9 #include "base/no_destructor.h"
10 #include "base/values.h"
11 #include "components/services/storage/indexed_db/scopes/leveldb_scopes.h"
12 #include "components/services/storage/indexed_db/scopes/varint_coding.h"
13 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.h"
14 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_iterator.h"
15 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction.h"
16 #include "content/browser/indexed_db/indexed_db_data_format_version.h"
17 #include "content/browser/indexed_db/indexed_db_data_loss_info.h"
18 #include "content/browser/indexed_db/indexed_db_leveldb_env.h"
19 #include "content/browser/indexed_db/indexed_db_reporting.h"
20 #include "content/browser/indexed_db/indexed_db_tracing.h"
21 #include "storage/common/database/database_identifier.h"
22 #include "third_party/leveldatabase/env_chromium.h"
23
24 using base::StringPiece;
25 using blink::IndexedDBKeyPath;
26 using leveldb::Status;
27
28 namespace content {
29 namespace indexed_db {
30 namespace {
31 class LDBComparator : public leveldb::Comparator {
32 public:
33 LDBComparator() = default;
34 ~LDBComparator() override = default;
Compare(const leveldb::Slice & a,const leveldb::Slice & b) const35 int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const override {
36 return content::Compare(leveldb_env::MakeStringPiece(a),
37 leveldb_env::MakeStringPiece(b),
38 false /*index_keys*/);
39 }
Name() const40 const char* Name() const override { return "idb_cmp1"; }
FindShortestSeparator(std::string * start,const leveldb::Slice & limit) const41 void FindShortestSeparator(std::string* start,
42 const leveldb::Slice& limit) const override {}
FindShortSuccessor(std::string * key) const43 void FindShortSuccessor(std::string* key) const override {}
44 };
45 } // namespace
46
47 const base::FilePath::CharType kBlobExtension[] = FILE_PATH_LITERAL(".blob");
48 const base::FilePath::CharType kIndexedDBExtension[] =
49 FILE_PATH_LITERAL(".indexeddb");
50 const base::FilePath::CharType kLevelDBExtension[] =
51 FILE_PATH_LITERAL(".leveldb");
52
53 // static
GetBlobStoreFileName(const url::Origin & origin)54 base::FilePath GetBlobStoreFileName(const url::Origin& origin) {
55 std::string origin_id = storage::GetIdentifierFromOrigin(origin);
56 return base::FilePath()
57 .AppendASCII(origin_id)
58 .AddExtension(kIndexedDBExtension)
59 .AddExtension(kBlobExtension);
60 }
61
62 // static
GetLevelDBFileName(const url::Origin & origin)63 base::FilePath GetLevelDBFileName(const url::Origin& origin) {
64 std::string origin_id = storage::GetIdentifierFromOrigin(origin);
65 return base::FilePath()
66 .AppendASCII(origin_id)
67 .AddExtension(kIndexedDBExtension)
68 .AddExtension(kLevelDBExtension);
69 }
70
ComputeCorruptionFileName(const url::Origin & origin)71 base::FilePath ComputeCorruptionFileName(const url::Origin& origin) {
72 return GetLevelDBFileName(origin).Append(
73 FILE_PATH_LITERAL("corruption_info.json"));
74 }
75
IsPathTooLong(storage::FilesystemProxy * filesystem,const base::FilePath & leveldb_dir)76 bool IsPathTooLong(storage::FilesystemProxy* filesystem,
77 const base::FilePath& leveldb_dir) {
78 base::Optional<int> limit =
79 filesystem->GetMaximumPathComponentLength(leveldb_dir.DirName());
80 if (!limit.has_value()) {
81 DLOG(WARNING) << "GetMaximumPathComponentLength returned -1";
82 // In limited testing, ChromeOS returns 143, other OSes 255.
83 #if defined(OS_CHROMEOS)
84 limit = 143;
85 #else
86 limit = 255;
87 #endif
88 }
89 size_t component_length = leveldb_dir.BaseName().value().length();
90 if (component_length > static_cast<uint32_t>(*limit)) {
91 DLOG(WARNING) << "Path component length (" << component_length
92 << ") exceeds maximum (" << *limit
93 << ") allowed by this filesystem.";
94 const int min = 140;
95 const int max = 300;
96 const int num_buckets = 12;
97 base::UmaHistogramCustomCounts(
98 "WebCore.IndexedDB.BackingStore.OverlyLargeOriginLength",
99 component_length, min, max, num_buckets);
100 return true;
101 }
102 return false;
103 }
104
ReadCorruptionInfo(storage::FilesystemProxy * filesystem_proxy,const base::FilePath & path_base,const url::Origin & origin)105 std::string ReadCorruptionInfo(storage::FilesystemProxy* filesystem_proxy,
106 const base::FilePath& path_base,
107 const url::Origin& origin) {
108 const base::FilePath info_path =
109 path_base.Append(indexed_db::ComputeCorruptionFileName(origin));
110 std::string message;
111 if (IsPathTooLong(filesystem_proxy, info_path))
112 return message;
113
114 const int64_t kMaxJsonLength = 4096;
115
116 base::Optional<base::File::Info> file_info =
117 filesystem_proxy->GetFileInfo(info_path);
118 if (!file_info.has_value())
119 return message;
120 if (!file_info->size || file_info->size > kMaxJsonLength) {
121 filesystem_proxy->DeleteFile(info_path);
122 return message;
123 }
124
125 storage::FileErrorOr<base::File> file_or_error = filesystem_proxy->OpenFile(
126 info_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
127 if (!file_or_error.is_error()) {
128 auto& file = file_or_error.value();
129 if (file.IsValid()) {
130 std::string input_js(file_info->size, '\0');
131 if (file_info->size ==
132 file.Read(0, base::data(input_js), file_info->size)) {
133 base::Optional<base::Value> val = base::JSONReader::Read(input_js);
134 if (val && val->is_dict()) {
135 std::string* s = val->FindStringKey("message");
136 if (s)
137 message = *s;
138 }
139 }
140 file.Close();
141 }
142 }
143
144 filesystem_proxy->DeleteFile(info_path);
145
146 return message;
147 }
148
InternalInconsistencyStatus()149 leveldb::Status InternalInconsistencyStatus() {
150 return leveldb::Status::Corruption("Internal inconsistency");
151 }
152
InvalidDBKeyStatus()153 leveldb::Status InvalidDBKeyStatus() {
154 return leveldb::Status::InvalidArgument("Invalid database key ID");
155 }
156
IOErrorStatus()157 leveldb::Status IOErrorStatus() {
158 return leveldb::Status::IOError("IO Error");
159 }
160
PutBool(TransactionalLevelDBTransaction * transaction,const StringPiece & key,bool value)161 leveldb::Status PutBool(TransactionalLevelDBTransaction* transaction,
162 const StringPiece& key,
163 bool value) {
164 std::string buffer;
165 EncodeBool(value, &buffer);
166 return transaction->Put(key, &buffer);
167 }
168
169 template <typename DBOrTransaction>
GetVarInt(DBOrTransaction * db,const StringPiece & key,int64_t * found_int,bool * found)170 Status GetVarInt(DBOrTransaction* db,
171 const StringPiece& key,
172 int64_t* found_int,
173 bool* found) {
174 std::string result;
175 Status s = db->Get(key, &result, found);
176 if (!s.ok())
177 return s;
178 if (!*found)
179 return Status::OK();
180 StringPiece slice(result);
181 if (DecodeVarInt(&slice, found_int) && slice.empty())
182 return s;
183 return InternalInconsistencyStatus();
184 }
185 template Status GetVarInt<TransactionalLevelDBTransaction>(
186 TransactionalLevelDBTransaction* txn,
187 const StringPiece& key,
188 int64_t* found_int,
189 bool* found);
190 template Status GetVarInt<TransactionalLevelDBDatabase>(
191 TransactionalLevelDBDatabase* db,
192 const StringPiece& key,
193 int64_t* found_int,
194 bool* found);
195
196 template <typename TransactionOrWriteBatch>
PutVarInt(TransactionOrWriteBatch * transaction_or_write_batch,const StringPiece & key,int64_t value)197 leveldb::Status PutVarInt(TransactionOrWriteBatch* transaction_or_write_batch,
198 const StringPiece& key,
199 int64_t value) {
200 std::string buffer;
201 EncodeVarInt(value, &buffer);
202 return PutValue(transaction_or_write_batch, key, &buffer);
203 }
204 template leveldb::Status PutVarInt<TransactionalLevelDBTransaction>(
205 TransactionalLevelDBTransaction* transaction,
206 const StringPiece& key,
207 int64_t value);
208 template leveldb::Status PutVarInt<LevelDBDirectTransaction>(
209 LevelDBDirectTransaction* transaction,
210 const StringPiece& key,
211 int64_t value);
212 template leveldb::Status PutVarInt<LevelDBWriteBatch>(
213 LevelDBWriteBatch* transaction,
214 const StringPiece& key,
215 int64_t value);
216
217 template <typename DBOrTransaction>
GetString(DBOrTransaction * db,const StringPiece & key,base::string16 * found_string,bool * found)218 Status GetString(DBOrTransaction* db,
219 const StringPiece& key,
220 base::string16* found_string,
221 bool* found) {
222 std::string result;
223 *found = false;
224 Status s = db->Get(key, &result, found);
225 if (!s.ok())
226 return s;
227 if (!*found)
228 return Status::OK();
229 StringPiece slice(result);
230 if (DecodeString(&slice, found_string) && slice.empty())
231 return s;
232 return InternalInconsistencyStatus();
233 }
234
235 template Status GetString<TransactionalLevelDBTransaction>(
236 TransactionalLevelDBTransaction* txn,
237 const StringPiece& key,
238 base::string16* found_string,
239 bool* found);
240 template Status GetString<TransactionalLevelDBDatabase>(
241 TransactionalLevelDBDatabase* db,
242 const StringPiece& key,
243 base::string16* found_string,
244 bool* found);
245
PutString(TransactionalLevelDBTransaction * transaction,const StringPiece & key,const base::string16 & value)246 leveldb::Status PutString(TransactionalLevelDBTransaction* transaction,
247 const StringPiece& key,
248 const base::string16& value) {
249 std::string buffer;
250 EncodeString(value, &buffer);
251 return transaction->Put(key, &buffer);
252 }
253
PutIDBKeyPath(TransactionalLevelDBTransaction * transaction,const StringPiece & key,const IndexedDBKeyPath & value)254 leveldb::Status PutIDBKeyPath(TransactionalLevelDBTransaction* transaction,
255 const StringPiece& key,
256 const IndexedDBKeyPath& value) {
257 std::string buffer;
258 EncodeIDBKeyPath(value, &buffer);
259 return transaction->Put(key, &buffer);
260 }
261
262 template <typename DBOrTransaction>
GetMaxObjectStoreId(DBOrTransaction * db,int64_t database_id,int64_t * max_object_store_id)263 Status GetMaxObjectStoreId(DBOrTransaction* db,
264 int64_t database_id,
265 int64_t* max_object_store_id) {
266 const std::string max_object_store_id_key = DatabaseMetaDataKey::Encode(
267 database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
268 *max_object_store_id = -1;
269 bool found = false;
270 Status s = indexed_db::GetInt(db, max_object_store_id_key,
271 max_object_store_id, &found);
272 if (!s.ok())
273 return s;
274 if (!found)
275 *max_object_store_id = 0;
276
277 DCHECK_GE(*max_object_store_id, 0);
278 return s;
279 }
280
281 template Status GetMaxObjectStoreId<TransactionalLevelDBTransaction>(
282 TransactionalLevelDBTransaction* db,
283 int64_t database_id,
284 int64_t* max_object_store_id);
285 template Status GetMaxObjectStoreId<TransactionalLevelDBDatabase>(
286 TransactionalLevelDBDatabase* db,
287 int64_t database_id,
288 int64_t* max_object_store_id);
289
SetMaxObjectStoreId(TransactionalLevelDBTransaction * transaction,int64_t database_id,int64_t object_store_id)290 Status SetMaxObjectStoreId(TransactionalLevelDBTransaction* transaction,
291 int64_t database_id,
292 int64_t object_store_id) {
293 const std::string max_object_store_id_key = DatabaseMetaDataKey::Encode(
294 database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
295 int64_t max_object_store_id = -1;
296 bool found = false;
297 Status s = GetInt(transaction, max_object_store_id_key, &max_object_store_id,
298 &found);
299 if (!s.ok())
300 return s;
301 if (!found)
302 max_object_store_id = 0;
303
304 DCHECK_GE(max_object_store_id, 0);
305 if (!s.ok()) {
306 INTERNAL_READ_ERROR(SET_MAX_OBJECT_STORE_ID);
307 return s;
308 }
309
310 if (object_store_id <= max_object_store_id) {
311 INTERNAL_CONSISTENCY_ERROR(SET_MAX_OBJECT_STORE_ID);
312 return indexed_db::InternalInconsistencyStatus();
313 }
314 return indexed_db::PutInt(transaction, max_object_store_id_key,
315 object_store_id);
316 }
317
GetNewVersionNumber(TransactionalLevelDBTransaction * transaction,int64_t database_id,int64_t object_store_id,int64_t * new_version_number)318 Status GetNewVersionNumber(TransactionalLevelDBTransaction* transaction,
319 int64_t database_id,
320 int64_t object_store_id,
321 int64_t* new_version_number) {
322 const std::string last_version_key = ObjectStoreMetaDataKey::Encode(
323 database_id, object_store_id, ObjectStoreMetaDataKey::LAST_VERSION);
324
325 *new_version_number = -1;
326 int64_t last_version = -1;
327 bool found = false;
328 Status s = GetInt(transaction, last_version_key, &last_version, &found);
329 if (!s.ok()) {
330 INTERNAL_READ_ERROR(GET_NEW_VERSION_NUMBER);
331 return s;
332 }
333 if (!found)
334 last_version = 0;
335
336 DCHECK_GE(last_version, 0);
337
338 int64_t version = last_version + 1;
339 s = PutInt(transaction, last_version_key, version);
340 if (!s.ok()) {
341 INTERNAL_READ_ERROR(GET_NEW_VERSION_NUMBER);
342 return s;
343 }
344
345 // TODO(jsbell): Think about how we want to handle the overflow scenario.
346 DCHECK(version > last_version);
347
348 *new_version_number = version;
349 return s;
350 }
351
SetMaxIndexId(TransactionalLevelDBTransaction * transaction,int64_t database_id,int64_t object_store_id,int64_t index_id)352 Status SetMaxIndexId(TransactionalLevelDBTransaction* transaction,
353 int64_t database_id,
354 int64_t object_store_id,
355 int64_t index_id) {
356 int64_t max_index_id = -1;
357 const std::string max_index_id_key = ObjectStoreMetaDataKey::Encode(
358 database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
359 bool found = false;
360 Status s = GetInt(transaction, max_index_id_key, &max_index_id, &found);
361 if (!s.ok()) {
362 INTERNAL_READ_ERROR(SET_MAX_INDEX_ID);
363 return s;
364 }
365 if (!found)
366 max_index_id = kMinimumIndexId;
367
368 if (index_id <= max_index_id) {
369 INTERNAL_CONSISTENCY_ERROR(SET_MAX_INDEX_ID);
370 return InternalInconsistencyStatus();
371 }
372
373 return PutInt(transaction, max_index_id_key, index_id);
374 }
375
VersionExists(TransactionalLevelDBTransaction * transaction,int64_t database_id,int64_t object_store_id,int64_t version,const std::string & encoded_primary_key,bool * exists)376 Status VersionExists(TransactionalLevelDBTransaction* transaction,
377 int64_t database_id,
378 int64_t object_store_id,
379 int64_t version,
380 const std::string& encoded_primary_key,
381 bool* exists) {
382 const std::string key =
383 ExistsEntryKey::Encode(database_id, object_store_id, encoded_primary_key);
384 std::string data;
385
386 Status s = transaction->Get(key, &data, exists);
387 if (!s.ok()) {
388 INTERNAL_READ_ERROR(VERSION_EXISTS);
389 return s;
390 }
391 if (!*exists)
392 return s;
393
394 StringPiece slice(data);
395 int64_t decoded;
396 if (!DecodeInt(&slice, &decoded) || !slice.empty())
397 return InternalInconsistencyStatus();
398 *exists = (decoded == version);
399 return s;
400 }
401
402 template Status GetNewDatabaseId<LevelDBDirectTransaction>(
403 LevelDBDirectTransaction* transaction,
404 int64_t* new_id);
405
406 template Status GetNewDatabaseId<TransactionalLevelDBTransaction>(
407 TransactionalLevelDBTransaction* transaction,
408 int64_t* new_id);
409
410 template <typename Transaction>
GetNewDatabaseId(Transaction * transaction,int64_t * new_id)411 Status GetNewDatabaseId(Transaction* transaction, int64_t* new_id) {
412 *new_id = -1;
413 int64_t max_database_id = -1;
414 bool found = false;
415 Status s = indexed_db::GetInt(transaction, MaxDatabaseIdKey::Encode(),
416 &max_database_id, &found);
417 if (!s.ok()) {
418 INTERNAL_READ_ERROR(GET_NEW_DATABASE_ID);
419 return s;
420 }
421 if (!found)
422 max_database_id = 0;
423
424 DCHECK_GE(max_database_id, 0);
425
426 int64_t database_id = max_database_id + 1;
427 s = indexed_db::PutInt(transaction, MaxDatabaseIdKey::Encode(), database_id);
428 if (!s.ok()) {
429 INTERNAL_READ_ERROR(GET_NEW_DATABASE_ID);
430 return s;
431 }
432 *new_id = database_id;
433 return Status::OK();
434 }
435
CheckObjectStoreAndMetaDataType(const TransactionalLevelDBIterator * it,const std::string & stop_key,int64_t object_store_id,int64_t meta_data_type)436 bool CheckObjectStoreAndMetaDataType(const TransactionalLevelDBIterator* it,
437 const std::string& stop_key,
438 int64_t object_store_id,
439 int64_t meta_data_type) {
440 if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0)
441 return false;
442
443 StringPiece slice(it->Key());
444 ObjectStoreMetaDataKey meta_data_key;
445 bool ok =
446 ObjectStoreMetaDataKey::Decode(&slice, &meta_data_key) && slice.empty();
447 DCHECK(ok);
448 if (meta_data_key.ObjectStoreId() != object_store_id)
449 return false;
450 if (meta_data_key.MetaDataType() != meta_data_type)
451 return false;
452 return ok;
453 }
454
CheckIndexAndMetaDataKey(const TransactionalLevelDBIterator * it,const std::string & stop_key,int64_t index_id,unsigned char meta_data_type)455 bool CheckIndexAndMetaDataKey(const TransactionalLevelDBIterator* it,
456 const std::string& stop_key,
457 int64_t index_id,
458 unsigned char meta_data_type) {
459 if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0)
460 return false;
461
462 StringPiece slice(it->Key());
463 IndexMetaDataKey meta_data_key;
464 bool ok = IndexMetaDataKey::Decode(&slice, &meta_data_key);
465 DCHECK(ok);
466 if (meta_data_key.IndexId() != index_id)
467 return false;
468 if (meta_data_key.meta_data_type() != meta_data_type)
469 return false;
470 return true;
471 }
472
FindGreatestKeyLessThanOrEqual(TransactionalLevelDBTransaction * transaction,const std::string & target,std::string * found_key,Status * s)473 bool FindGreatestKeyLessThanOrEqual(
474 TransactionalLevelDBTransaction* transaction,
475 const std::string& target,
476 std::string* found_key,
477 Status* s) {
478 std::unique_ptr<TransactionalLevelDBIterator> it =
479 transaction->CreateIterator(*s);
480 if (!s->ok()) {
481 INTERNAL_WRITE_ERROR(CREATE_ITERATOR);
482 return false;
483 }
484
485 *s = it->Seek(target);
486 if (!s->ok())
487 return false;
488
489 if (!it->IsValid()) {
490 *s = it->SeekToLast();
491 if (!s->ok() || !it->IsValid())
492 return false;
493 }
494
495 while (CompareIndexKeys(it->Key(), target) > 0) {
496 *s = it->Prev();
497 if (!s->ok() || !it->IsValid())
498 return false;
499 }
500
501 do {
502 *found_key = it->Key().as_string();
503
504 // There can be several index keys that compare equal. We want the last one.
505 *s = it->Next();
506 } while (s->ok() && it->IsValid() && !CompareIndexKeys(it->Key(), target));
507
508 return true;
509 }
510
GetBlobNumberGeneratorCurrentNumber(LevelDBDirectTransaction * leveldb_transaction,int64_t database_id,int64_t * blob_number_generator_current_number)511 bool GetBlobNumberGeneratorCurrentNumber(
512 LevelDBDirectTransaction* leveldb_transaction,
513 int64_t database_id,
514 int64_t* blob_number_generator_current_number) {
515 const std::string key_gen_key = DatabaseMetaDataKey::Encode(
516 database_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER);
517
518 // Default to initial number if not found.
519 int64_t cur_number = DatabaseMetaDataKey::kBlobNumberGeneratorInitialNumber;
520 std::string data;
521
522 bool found = false;
523 bool ok = leveldb_transaction->Get(key_gen_key, &data, &found).ok();
524 if (!ok) {
525 INTERNAL_READ_ERROR(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER);
526 return false;
527 }
528 if (found) {
529 StringPiece slice(data);
530 if (!DecodeVarInt(&slice, &cur_number) || !slice.empty() ||
531 !DatabaseMetaDataKey::IsValidBlobNumber(cur_number)) {
532 INTERNAL_READ_ERROR(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER);
533 return false;
534 }
535 }
536 *blob_number_generator_current_number = cur_number;
537 return true;
538 }
539
UpdateBlobNumberGeneratorCurrentNumber(LevelDBDirectTransaction * leveldb_transaction,int64_t database_id,int64_t blob_number_generator_current_number)540 bool UpdateBlobNumberGeneratorCurrentNumber(
541 LevelDBDirectTransaction* leveldb_transaction,
542 int64_t database_id,
543 int64_t blob_number_generator_current_number) {
544 #if DCHECK_IS_ON()
545 int64_t old_number;
546 if (!GetBlobNumberGeneratorCurrentNumber(leveldb_transaction, database_id,
547 &old_number))
548 return false;
549 DCHECK_LT(old_number, blob_number_generator_current_number);
550 #endif
551 DCHECK(DatabaseMetaDataKey::IsValidBlobNumber(
552 blob_number_generator_current_number));
553 const std::string key = DatabaseMetaDataKey::Encode(
554 database_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER);
555
556 leveldb::Status s =
557 PutVarInt(leveldb_transaction, key, blob_number_generator_current_number);
558 return s.ok();
559 }
560
GetEarliestSweepTime(TransactionalLevelDBDatabase * db,base::Time * earliest_sweep)561 Status GetEarliestSweepTime(TransactionalLevelDBDatabase* db,
562 base::Time* earliest_sweep) {
563 const std::string earliest_sweep_time_key = EarliestSweepKey::Encode();
564 *earliest_sweep = base::Time();
565 bool found = false;
566 int64_t time_micros = 0;
567 Status s =
568 indexed_db::GetInt(db, earliest_sweep_time_key, &time_micros, &found);
569 if (!s.ok())
570 return s;
571 if (!found)
572 time_micros = 0;
573
574 DCHECK_GE(time_micros, 0);
575 *earliest_sweep += base::TimeDelta::FromMicroseconds(time_micros);
576
577 return s;
578 }
579
580 template leveldb::Status SetEarliestSweepTime<TransactionalLevelDBTransaction>(
581 TransactionalLevelDBTransaction* db,
582 base::Time earliest_sweep);
583 template leveldb::Status SetEarliestSweepTime<LevelDBDirectTransaction>(
584 LevelDBDirectTransaction* db,
585 base::Time earliest_sweep);
586
587 template <typename Transaction>
SetEarliestSweepTime(Transaction * txn,base::Time earliest_sweep)588 leveldb::Status SetEarliestSweepTime(Transaction* txn,
589 base::Time earliest_sweep) {
590 const std::string earliest_sweep_time_key = EarliestSweepKey::Encode();
591 int64_t time_micros = (earliest_sweep - base::Time()).InMicroseconds();
592 return indexed_db::PutInt(txn, earliest_sweep_time_key, time_micros);
593 }
594
GetDefaultLevelDBComparator()595 const leveldb::Comparator* GetDefaultLevelDBComparator() {
596 static const base::NoDestructor<LDBComparator> ldb_comparator;
597 return ldb_comparator.get();
598 }
599
600 } // namespace indexed_db
601 } // namespace content
602