indexed_db_database.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
1// Copyright (c) 2013 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_database.h"
6
7#include <math.h>
8#include <vector>
9
10#include "base/auto_reset.h"
11#include "base/logging.h"
12#include "base/memory/scoped_ptr.h"
13#include "base/strings/string_number_conversions.h"
14#include "base/strings/utf_string_conversions.h"
15#include "content/browser/indexed_db/indexed_db_backing_store.h"
16#include "content/browser/indexed_db/indexed_db_cursor.h"
17#include "content/browser/indexed_db/indexed_db_factory.h"
18#include "content/browser/indexed_db/indexed_db_index_writer.h"
19#include "content/browser/indexed_db/indexed_db_tracing.h"
20#include "content/browser/indexed_db/indexed_db_transaction.h"
21#include "content/common/indexed_db/indexed_db_key_path.h"
22#include "content/common/indexed_db/indexed_db_key_range.h"
23#include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
24
25using base::Int64ToString16;
26using WebKit::WebIDBKey;
27
28namespace content {
29
30class CreateObjectStoreOperation : public IndexedDBTransaction::Operation {
31 public:
32  CreateObjectStoreOperation(
33      scoped_refptr<IndexedDBBackingStore> backing_store,
34      const IndexedDBObjectStoreMetadata& object_store_metadata)
35      : backing_store_(backing_store),
36        object_store_metadata_(object_store_metadata) {}
37  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
38
39 private:
40  const scoped_refptr<IndexedDBBackingStore> backing_store_;
41  const IndexedDBObjectStoreMetadata object_store_metadata_;
42};
43
44class DeleteObjectStoreOperation : public IndexedDBTransaction::Operation {
45 public:
46  DeleteObjectStoreOperation(
47      scoped_refptr<IndexedDBBackingStore> backing_store,
48      const IndexedDBObjectStoreMetadata& object_store_metadata)
49      : backing_store_(backing_store),
50        object_store_metadata_(object_store_metadata) {}
51  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
52
53 private:
54  const scoped_refptr<IndexedDBBackingStore> backing_store_;
55  const IndexedDBObjectStoreMetadata object_store_metadata_;
56};
57
58class IndexedDBDatabase::VersionChangeOperation
59    : public IndexedDBTransaction::Operation {
60 public:
61  VersionChangeOperation(
62      scoped_refptr<IndexedDBDatabase> database,
63      int64 transaction_id,
64      int64 version,
65      scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
66      scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks)
67      : database_(database),
68        transaction_id_(transaction_id),
69        version_(version),
70        callbacks_(callbacks),
71        database_callbacks_(database_callbacks) {}
72  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
73
74 private:
75  scoped_refptr<IndexedDBDatabase> database_;
76  int64 transaction_id_;
77  int64 version_;
78  scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
79  scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks_;
80};
81
82class CreateObjectStoreAbortOperation : public IndexedDBTransaction::Operation {
83 public:
84  CreateObjectStoreAbortOperation(scoped_refptr<IndexedDBDatabase> database,
85                                  int64 object_store_id)
86      : database_(database), object_store_id_(object_store_id) {}
87  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
88
89 private:
90  const scoped_refptr<IndexedDBDatabase> database_;
91  const int64 object_store_id_;
92};
93
94class DeleteObjectStoreAbortOperation : public IndexedDBTransaction::Operation {
95 public:
96  DeleteObjectStoreAbortOperation(
97      scoped_refptr<IndexedDBDatabase> database,
98      const IndexedDBObjectStoreMetadata& object_store_metadata)
99      : database_(database), object_store_metadata_(object_store_metadata) {}
100  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
101
102 private:
103  scoped_refptr<IndexedDBDatabase> database_;
104  IndexedDBObjectStoreMetadata object_store_metadata_;
105};
106
107class IndexedDBDatabase::VersionChangeAbortOperation
108    : public IndexedDBTransaction::Operation {
109 public:
110  VersionChangeAbortOperation(scoped_refptr<IndexedDBDatabase> database,
111                              const string16& previous_version,
112                              int64 previous_int_version)
113      : database_(database),
114        previous_version_(previous_version),
115        previous_int_version_(previous_int_version) {}
116  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
117
118 private:
119  scoped_refptr<IndexedDBDatabase> database_;
120  string16 previous_version_;
121  int64 previous_int_version_;
122};
123
124class CreateIndexOperation : public IndexedDBTransaction::Operation {
125 public:
126  CreateIndexOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
127                       int64 object_store_id,
128                       const IndexedDBIndexMetadata& index_metadata)
129      : backing_store_(backing_store),
130        object_store_id_(object_store_id),
131        index_metadata_(index_metadata) {}
132  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
133
134 private:
135  const scoped_refptr<IndexedDBBackingStore> backing_store_;
136  const int64 object_store_id_;
137  const IndexedDBIndexMetadata index_metadata_;
138};
139
140class DeleteIndexOperation : public IndexedDBTransaction::Operation {
141 public:
142  DeleteIndexOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
143                       int64 object_store_id,
144                       const IndexedDBIndexMetadata& index_metadata)
145      : backing_store_(backing_store),
146        object_store_id_(object_store_id),
147        index_metadata_(index_metadata) {}
148  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
149
150 private:
151  const scoped_refptr<IndexedDBBackingStore> backing_store_;
152  const int64 object_store_id_;
153  const IndexedDBIndexMetadata index_metadata_;
154};
155
156class CreateIndexAbortOperation : public IndexedDBTransaction::Operation {
157 public:
158  CreateIndexAbortOperation(scoped_refptr<IndexedDBDatabase> database,
159                            int64 object_store_id,
160                            int64 index_id)
161      : database_(database),
162        object_store_id_(object_store_id),
163        index_id_(index_id) {}
164  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
165
166 private:
167  const scoped_refptr<IndexedDBDatabase> database_;
168  const int64 object_store_id_;
169  const int64 index_id_;
170};
171
172class DeleteIndexAbortOperation : public IndexedDBTransaction::Operation {
173 public:
174  DeleteIndexAbortOperation(scoped_refptr<IndexedDBDatabase> database,
175                            int64 object_store_id,
176                            const IndexedDBIndexMetadata& index_metadata)
177      : database_(database),
178        object_store_id_(object_store_id),
179        index_metadata_(index_metadata) {}
180  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
181
182 private:
183  const scoped_refptr<IndexedDBDatabase> database_;
184  const int64 object_store_id_;
185  const IndexedDBIndexMetadata index_metadata_;
186};
187
188class GetOperation : public IndexedDBTransaction::Operation {
189 public:
190  GetOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
191               int64 database_id,
192               int64 object_store_id,
193               int64 index_id,
194               const IndexedDBKeyPath& key_path,
195               const bool auto_increment,
196               scoped_ptr<IndexedDBKeyRange> key_range,
197               indexed_db::CursorType cursor_type,
198               scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
199      : backing_store_(backing_store),
200        database_id_(database_id),
201        object_store_id_(object_store_id),
202        index_id_(index_id),
203        key_path_(key_path),
204        auto_increment_(auto_increment),
205        key_range_(key_range.Pass()),
206        cursor_type_(cursor_type),
207        callbacks_(callbacks) {
208  }
209  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
210
211 private:
212  const scoped_refptr<IndexedDBBackingStore> backing_store_;
213  const int64 database_id_;
214  const int64 object_store_id_;
215  const int64 index_id_;
216  const IndexedDBKeyPath key_path_;
217  const bool auto_increment_;
218  const scoped_ptr<IndexedDBKeyRange> key_range_;
219  const indexed_db::CursorType cursor_type_;
220  const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
221};
222
223class PutOperation : public IndexedDBTransaction::Operation {
224 public:
225  PutOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
226               int64 database_id,
227               const IndexedDBObjectStoreMetadata& object_store,
228               std::vector<char>* value,
229               scoped_ptr<IndexedDBKey> key,
230               IndexedDBDatabase::PutMode put_mode,
231               scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
232               const std::vector<int64>& index_ids,
233               const std::vector<IndexedDBDatabase::IndexKeys>& index_keys)
234      : backing_store_(backing_store),
235        database_id_(database_id),
236        object_store_(object_store),
237        key_(key.Pass()),
238        put_mode_(put_mode),
239        callbacks_(callbacks),
240        index_ids_(index_ids),
241        index_keys_(index_keys) {
242    value_.swap(*value);
243  }
244  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
245
246 private:
247  const scoped_refptr<IndexedDBBackingStore> backing_store_;
248  const int64 database_id_;
249  const IndexedDBObjectStoreMetadata object_store_;
250  std::vector<char> value_;
251  scoped_ptr<IndexedDBKey> key_;
252  const IndexedDBDatabase::PutMode put_mode_;
253  const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
254  const std::vector<int64> index_ids_;
255  const std::vector<IndexedDBDatabase::IndexKeys> index_keys_;
256};
257
258class SetIndexesReadyOperation : public IndexedDBTransaction::Operation {
259 public:
260  explicit SetIndexesReadyOperation(size_t index_count)
261      : index_count_(index_count) {}
262  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
263
264 private:
265  const size_t index_count_;
266};
267
268class OpenCursorOperation : public IndexedDBTransaction::Operation {
269 public:
270  OpenCursorOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
271                      int64 database_id,
272                      int64 object_store_id,
273                      int64 index_id,
274                      scoped_ptr<IndexedDBKeyRange> key_range,
275                      indexed_db::CursorDirection direction,
276                      indexed_db::CursorType cursor_type,
277                      IndexedDBDatabase::TaskType task_type,
278                      scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
279      : backing_store_(backing_store),
280        database_id_(database_id),
281        object_store_id_(object_store_id),
282        index_id_(index_id),
283        key_range_(key_range.Pass()),
284        direction_(direction),
285        cursor_type_(cursor_type),
286        task_type_(task_type),
287        callbacks_(callbacks) {}
288  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
289
290 private:
291  const scoped_refptr<IndexedDBBackingStore> backing_store_;
292  const int64 database_id_;
293  const int64 object_store_id_;
294  const int64 index_id_;
295  const scoped_ptr<IndexedDBKeyRange> key_range_;
296  const indexed_db::CursorDirection direction_;
297  const indexed_db::CursorType cursor_type_;
298  const IndexedDBDatabase::TaskType task_type_;
299  const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
300};
301
302class CountOperation : public IndexedDBTransaction::Operation {
303 public:
304  CountOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
305                 int64 database_id,
306                 int64 object_store_id,
307                 int64 index_id,
308                 scoped_ptr<IndexedDBKeyRange> key_range,
309                 scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
310      : backing_store_(backing_store),
311        database_id_(database_id),
312        object_store_id_(object_store_id),
313        index_id_(index_id),
314        key_range_(key_range.Pass()),
315        callbacks_(callbacks) {}
316  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
317
318 private:
319  const scoped_refptr<IndexedDBBackingStore> backing_store_;
320  const int64 database_id_;
321  const int64 object_store_id_;
322  const int64 index_id_;
323  const scoped_ptr<IndexedDBKeyRange> key_range_;
324  const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
325};
326
327class DeleteRangeOperation : public IndexedDBTransaction::Operation {
328 public:
329  DeleteRangeOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
330                       int64 database_id,
331                       int64 object_store_id,
332                       scoped_ptr<IndexedDBKeyRange> key_range,
333                       scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
334      : backing_store_(backing_store),
335        database_id_(database_id),
336        object_store_id_(object_store_id),
337        key_range_(key_range.Pass()),
338        callbacks_(callbacks) {}
339  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
340
341 private:
342  const scoped_refptr<IndexedDBBackingStore> backing_store_;
343  const int64 database_id_;
344  const int64 object_store_id_;
345  const scoped_ptr<IndexedDBKeyRange> key_range_;
346  const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
347};
348
349class ClearOperation : public IndexedDBTransaction::Operation {
350 public:
351  ClearOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
352                 int64 database_id,
353                 int64 object_store_id,
354                 scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
355      : backing_store_(backing_store),
356        database_id_(database_id),
357        object_store_id_(object_store_id),
358        callbacks_(callbacks) {}
359  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
360
361 private:
362  const scoped_refptr<IndexedDBBackingStore> backing_store_;
363  const int64 database_id_;
364  const int64 object_store_id_;
365  const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
366};
367
368class IndexedDBDatabase::PendingOpenCall {
369 public:
370  PendingOpenCall(
371      scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
372      scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
373      int64 transaction_id,
374      int64 version)
375      : callbacks_(callbacks),
376        database_callbacks_(database_callbacks),
377        version_(version),
378        transaction_id_(transaction_id) {}
379  scoped_refptr<IndexedDBCallbacksWrapper> Callbacks() { return callbacks_; }
380  scoped_refptr<IndexedDBDatabaseCallbacksWrapper> DatabaseCallbacks() {
381    return database_callbacks_;
382  }
383  int64 Version() { return version_; }
384  int64 TransactionId() const { return transaction_id_; }
385
386 private:
387  scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
388  scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks_;
389  int64 version_;
390  const int64 transaction_id_;
391};
392
393class IndexedDBDatabase::PendingDeleteCall {
394 public:
395  explicit PendingDeleteCall(scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
396      : callbacks_(callbacks) {}
397  scoped_refptr<IndexedDBCallbacksWrapper> Callbacks() { return callbacks_; }
398
399 private:
400  scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
401};
402
403scoped_refptr<IndexedDBDatabase> IndexedDBDatabase::Create(
404    const string16& name,
405    IndexedDBBackingStore* database,
406    IndexedDBFactory* factory,
407    const string16& unique_identifier) {
408  scoped_refptr<IndexedDBDatabase> backend =
409      new IndexedDBDatabase(name, database, factory, unique_identifier);
410  if (!backend->OpenInternal())
411    return 0;
412  return backend;
413}
414
415namespace {
416const base::string16::value_type kNoStringVersion[] = {0};
417}
418
419IndexedDBDatabase::IndexedDBDatabase(const string16& name,
420                                     IndexedDBBackingStore* backing_store,
421                                     IndexedDBFactory* factory,
422                                     const string16& unique_identifier)
423    : backing_store_(backing_store),
424      metadata_(name,
425                kInvalidId,
426                kNoStringVersion,
427                IndexedDBDatabaseMetadata::NO_INT_VERSION,
428                kInvalidId),
429      identifier_(unique_identifier),
430      factory_(factory),
431      running_version_change_transaction_(NULL),
432      closing_connection_(false) {
433  DCHECK(!metadata_.name.empty());
434}
435
436void IndexedDBDatabase::AddObjectStore(
437    const IndexedDBObjectStoreMetadata& object_store,
438    int64 new_max_object_store_id) {
439  DCHECK(metadata_.object_stores.find(object_store.id) ==
440         metadata_.object_stores.end());
441  if (new_max_object_store_id != IndexedDBObjectStoreMetadata::kInvalidId) {
442    DCHECK_LT(metadata_.max_object_store_id, new_max_object_store_id);
443    metadata_.max_object_store_id = new_max_object_store_id;
444  }
445  metadata_.object_stores[object_store.id] = object_store;
446}
447
448void IndexedDBDatabase::RemoveObjectStore(int64 object_store_id) {
449  DCHECK(metadata_.object_stores.find(object_store_id) !=
450         metadata_.object_stores.end());
451  metadata_.object_stores.erase(object_store_id);
452}
453
454void IndexedDBDatabase::AddIndex(int64 object_store_id,
455                                 const IndexedDBIndexMetadata& index,
456                                 int64 new_max_index_id) {
457  DCHECK(metadata_.object_stores.find(object_store_id) !=
458         metadata_.object_stores.end());
459  IndexedDBObjectStoreMetadata object_store =
460      metadata_.object_stores[object_store_id];
461
462  DCHECK(object_store.indexes.find(index.id) == object_store.indexes.end());
463  object_store.indexes[index.id] = index;
464  if (new_max_index_id != IndexedDBIndexMetadata::kInvalidId) {
465    DCHECK_LT(object_store.max_index_id, new_max_index_id);
466    object_store.max_index_id = new_max_index_id;
467  }
468  metadata_.object_stores[object_store_id] = object_store;
469}
470
471void IndexedDBDatabase::RemoveIndex(int64 object_store_id, int64 index_id) {
472  DCHECK(metadata_.object_stores.find(object_store_id) !=
473         metadata_.object_stores.end());
474  IndexedDBObjectStoreMetadata object_store =
475      metadata_.object_stores[object_store_id];
476
477  DCHECK(object_store.indexes.find(index_id) != object_store.indexes.end());
478  object_store.indexes.erase(index_id);
479  metadata_.object_stores[object_store_id] = object_store;
480}
481
482bool IndexedDBDatabase::OpenInternal() {
483  bool success = false;
484  bool ok = backing_store_->GetIDBDatabaseMetaData(
485      metadata_.name, &metadata_, &success);
486  DCHECK(success == (metadata_.id != kInvalidId)) << "success = " << success
487                                                  << " id_ = " << metadata_.id;
488  if (!ok)
489    return false;
490  if (success)
491    return backing_store_->GetObjectStores(metadata_.id,
492                                           &metadata_.object_stores);
493
494  return backing_store_->CreateIDBDatabaseMetaData(
495      metadata_.name, metadata_.version, metadata_.int_version, &metadata_.id);
496}
497
498IndexedDBDatabase::~IndexedDBDatabase() {
499  DCHECK(transactions_.empty());
500  DCHECK(pending_open_calls_.empty());
501  DCHECK(pending_delete_calls_.empty());
502}
503
504scoped_refptr<IndexedDBBackingStore> IndexedDBDatabase::BackingStore() const {
505  return backing_store_;
506}
507
508void IndexedDBDatabase::CreateObjectStore(int64 transaction_id,
509                                          int64 object_store_id,
510                                          const string16& name,
511                                          const IndexedDBKeyPath& key_path,
512                                          bool auto_increment) {
513  IDB_TRACE("IndexedDBDatabase::create_object_store");
514  TransactionMap::const_iterator trans_iterator =
515      transactions_.find(transaction_id);
516  if (trans_iterator == transactions_.end())
517    return;
518  IndexedDBTransaction* transaction = trans_iterator->second;
519  DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
520
521  DCHECK(metadata_.object_stores.find(object_store_id) ==
522         metadata_.object_stores.end());
523  IndexedDBObjectStoreMetadata object_store_metadata(
524      name,
525      object_store_id,
526      key_path,
527      auto_increment,
528      IndexedDBDatabase::kMinimumIndexId);
529
530  transaction->ScheduleTask(
531      new CreateObjectStoreOperation(backing_store_, object_store_metadata),
532      new CreateObjectStoreAbortOperation(this, object_store_id));
533
534  AddObjectStore(object_store_metadata, object_store_id);
535}
536
537void CreateObjectStoreOperation::Perform(IndexedDBTransaction* transaction) {
538  IDB_TRACE("CreateObjectStoreOperation");
539  if (!backing_store_->CreateObjectStore(
540          transaction->BackingStoreTransaction(),
541          transaction->database()->id(),
542          object_store_metadata_.id,
543          object_store_metadata_.name,
544          object_store_metadata_.key_path,
545          object_store_metadata_.auto_increment)) {
546    transaction->Abort(IndexedDBDatabaseError(
547        WebKit::WebIDBDatabaseExceptionUnknownError,
548        ASCIIToUTF16("Internal error creating object store '") +
549            object_store_metadata_.name + ASCIIToUTF16("'.")));
550    return;
551  }
552}
553
554void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id,
555                                          int64 object_store_id) {
556  IDB_TRACE("IndexedDBDatabase::delete_object_store");
557  TransactionMap::const_iterator trans_iterator =
558      transactions_.find(transaction_id);
559  if (trans_iterator == transactions_.end())
560    return;
561  IndexedDBTransaction* transaction = trans_iterator->second;
562  DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
563
564  DCHECK(metadata_.object_stores.find(object_store_id) !=
565         metadata_.object_stores.end());
566  const IndexedDBObjectStoreMetadata& object_store_metadata =
567      metadata_.object_stores[object_store_id];
568
569  transaction->ScheduleTask(
570      new DeleteObjectStoreOperation(backing_store_, object_store_metadata),
571      new DeleteObjectStoreAbortOperation(this, object_store_metadata));
572  RemoveObjectStore(object_store_id);
573}
574
575void IndexedDBDatabase::CreateIndex(int64 transaction_id,
576                                    int64 object_store_id,
577                                    int64 index_id,
578                                    const string16& name,
579                                    const IndexedDBKeyPath& key_path,
580                                    bool unique,
581                                    bool multi_entry) {
582  IDB_TRACE("IndexedDBDatabase::create_index");
583  TransactionMap::const_iterator trans_iterator =
584      transactions_.find(transaction_id);
585  if (trans_iterator == transactions_.end())
586    return;
587  IndexedDBTransaction* transaction = trans_iterator->second;
588  DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
589
590  DCHECK(metadata_.object_stores.find(object_store_id) !=
591         metadata_.object_stores.end());
592  const IndexedDBObjectStoreMetadata object_store =
593      metadata_.object_stores[object_store_id];
594
595  DCHECK(object_store.indexes.find(index_id) == object_store.indexes.end());
596  const IndexedDBIndexMetadata index_metadata(
597      name, index_id, key_path, unique, multi_entry);
598
599  transaction->ScheduleTask(
600      new CreateIndexOperation(backing_store_, object_store_id, index_metadata),
601      new CreateIndexAbortOperation(this, object_store_id, index_id));
602
603  AddIndex(object_store_id, index_metadata, index_id);
604}
605
606void CreateIndexOperation::Perform(IndexedDBTransaction* transaction) {
607  IDB_TRACE("CreateIndexOperation");
608  if (!backing_store_->CreateIndex(transaction->BackingStoreTransaction(),
609                                   transaction->database()->id(),
610                                   object_store_id_,
611                                   index_metadata_.id,
612                                   index_metadata_.name,
613                                   index_metadata_.key_path,
614                                   index_metadata_.unique,
615                                   index_metadata_.multi_entry)) {
616    string16 error_string = ASCIIToUTF16("Internal error creating index '") +
617                            index_metadata_.name + ASCIIToUTF16("'.");
618    transaction->Abort(IndexedDBDatabaseError(
619        WebKit::WebIDBDatabaseExceptionUnknownError, error_string));
620    return;
621  }
622}
623
624void CreateIndexAbortOperation::Perform(IndexedDBTransaction* transaction) {
625  IDB_TRACE("CreateIndexAbortOperation");
626  DCHECK(!transaction);
627  database_->RemoveIndex(object_store_id_, index_id_);
628}
629
630void IndexedDBDatabase::DeleteIndex(int64 transaction_id,
631                                    int64 object_store_id,
632                                    int64 index_id) {
633  IDB_TRACE("IndexedDBDatabase::delete_index");
634  TransactionMap::const_iterator trans_iterator =
635      transactions_.find(transaction_id);
636  if (trans_iterator == transactions_.end())
637    return;
638  IndexedDBTransaction* transaction = trans_iterator->second;
639  DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
640
641  DCHECK(metadata_.object_stores.find(object_store_id) !=
642         metadata_.object_stores.end());
643  IndexedDBObjectStoreMetadata object_store =
644      metadata_.object_stores[object_store_id];
645
646  DCHECK(object_store.indexes.find(index_id) != object_store.indexes.end());
647  const IndexedDBIndexMetadata& index_metadata = object_store.indexes[index_id];
648
649  transaction->ScheduleTask(
650      new DeleteIndexOperation(backing_store_, object_store_id, index_metadata),
651      new DeleteIndexAbortOperation(this, object_store_id, index_metadata));
652
653  RemoveIndex(object_store_id, index_id);
654}
655
656void DeleteIndexOperation::Perform(IndexedDBTransaction* transaction) {
657  IDB_TRACE("DeleteIndexOperation");
658  bool ok = backing_store_->DeleteIndex(transaction->BackingStoreTransaction(),
659                                        transaction->database()->id(),
660                                        object_store_id_,
661                                        index_metadata_.id);
662  if (!ok) {
663    string16 error_string = ASCIIToUTF16("Internal error deleting index '") +
664                            index_metadata_.name + ASCIIToUTF16("'.");
665    transaction->Abort(IndexedDBDatabaseError(
666        WebKit::WebIDBDatabaseExceptionUnknownError, error_string));
667  }
668}
669
670void DeleteIndexAbortOperation::Perform(IndexedDBTransaction* transaction) {
671  IDB_TRACE("DeleteIndexAbortOperation");
672  DCHECK(!transaction);
673  database_->AddIndex(
674      object_store_id_, index_metadata_, IndexedDBIndexMetadata::kInvalidId);
675}
676
677void IndexedDBDatabase::Commit(int64 transaction_id) {
678  // The frontend suggests that we commit, but we may have previously initiated
679  // an abort, and so have disposed of the transaction. on_abort has already
680  // been dispatched to the frontend, so it will find out about that
681  // asynchronously.
682  if (transactions_.find(transaction_id) != transactions_.end())
683    transactions_[transaction_id]->Commit();
684}
685
686void IndexedDBDatabase::Abort(int64 transaction_id) {
687  // If the transaction is unknown, then it has already been aborted by the
688  // backend before this call so it is safe to ignore it.
689  if (transactions_.find(transaction_id) != transactions_.end())
690    transactions_[transaction_id]->Abort();
691}
692
693void IndexedDBDatabase::Abort(int64 transaction_id,
694                              const IndexedDBDatabaseError& error) {
695  // If the transaction is unknown, then it has already been aborted by the
696  // backend before this call so it is safe to ignore it.
697  if (transactions_.find(transaction_id) != transactions_.end())
698    transactions_[transaction_id]->Abort(error);
699}
700
701void IndexedDBDatabase::Get(
702    int64 transaction_id,
703    int64 object_store_id,
704    int64 index_id,
705    scoped_ptr<IndexedDBKeyRange> key_range,
706    bool key_only,
707    scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
708  IDB_TRACE("IndexedDBDatabase::get");
709  TransactionMap::const_iterator trans_iterator =
710      transactions_.find(transaction_id);
711  if (trans_iterator == transactions_.end())
712    return;
713  IndexedDBDatabaseMetadata::ObjectStoreMap::const_iterator store_iterator =
714      metadata_.object_stores.find(object_store_id);
715  if (store_iterator == metadata_.object_stores.end())
716    return;
717  IndexedDBTransaction* transaction = trans_iterator->second;
718
719  transaction->ScheduleTask(new GetOperation(
720      backing_store_,
721      metadata_.id,
722      object_store_id,
723      index_id,
724      store_iterator->second.key_path,
725      store_iterator->second.auto_increment,
726      key_range.Pass(),
727      key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE,
728      callbacks));
729}
730
731void GetOperation::Perform(IndexedDBTransaction* transaction) {
732  IDB_TRACE("GetOperation");
733
734  const IndexedDBKey* key;
735
736  scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
737  if (key_range_->IsOnlyKey()) {
738    key = &key_range_->lower();
739  } else {
740    if (index_id_ == IndexedDBIndexMetadata::kInvalidId) {
741      DCHECK_NE(cursor_type_, indexed_db::CURSOR_KEY_ONLY);
742      // ObjectStore Retrieval Operation
743      backing_store_cursor = backing_store_->OpenObjectStoreCursor(
744          transaction->BackingStoreTransaction(),
745          database_id_,
746          object_store_id_,
747          *key_range_,
748          indexed_db::CURSOR_NEXT);
749    } else if (cursor_type_ == indexed_db::CURSOR_KEY_ONLY) {
750      // Index Value Retrieval Operation
751      backing_store_cursor = backing_store_->OpenIndexKeyCursor(
752          transaction->BackingStoreTransaction(),
753          database_id_,
754          object_store_id_,
755          index_id_,
756          *key_range_,
757          indexed_db::CURSOR_NEXT);
758    } else {
759      // Index Referenced Value Retrieval Operation
760      backing_store_cursor = backing_store_->OpenIndexCursor(
761          transaction->BackingStoreTransaction(),
762          database_id_,
763          object_store_id_,
764          index_id_,
765          *key_range_,
766          indexed_db::CURSOR_NEXT);
767    }
768
769    if (!backing_store_cursor) {
770      callbacks_->OnSuccess();
771      return;
772    }
773
774    key = &backing_store_cursor->key();
775  }
776
777  scoped_ptr<IndexedDBKey> primary_key;
778  bool ok;
779  if (index_id_ == IndexedDBIndexMetadata::kInvalidId) {
780    // Object Store Retrieval Operation
781    std::vector<char> value;
782    ok = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
783                                   database_id_,
784                                   object_store_id_,
785                                   *key,
786                                   &value);
787    if (!ok) {
788      callbacks_->OnError(
789          IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
790                                 "Internal error in get_record."));
791      return;
792    }
793
794    if (value.empty()) {
795      callbacks_->OnSuccess();
796      return;
797    }
798
799    if (auto_increment_ && !key_path_.IsNull()) {
800      callbacks_->OnSuccess(&value, *key, key_path_);
801      return;
802    }
803
804    callbacks_->OnSuccess(&value);
805    return;
806  }
807
808  // From here we are dealing only with indexes.
809  ok = backing_store_->GetPrimaryKeyViaIndex(
810      transaction->BackingStoreTransaction(),
811      database_id_,
812      object_store_id_,
813      index_id_,
814      *key,
815      &primary_key);
816  if (!ok) {
817    callbacks_->OnError(
818        IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
819                               "Internal error in get_primary_key_via_index."));
820    return;
821  }
822  if (!primary_key) {
823    callbacks_->OnSuccess();
824    return;
825  }
826  if (cursor_type_ == indexed_db::CURSOR_KEY_ONLY) {
827    // Index Value Retrieval Operation
828    callbacks_->OnSuccess(*primary_key);
829    return;
830  }
831
832  // Index Referenced Value Retrieval Operation
833  std::vector<char> value;
834  ok = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
835                                 database_id_,
836                                 object_store_id_,
837                                 *primary_key,
838                                 &value);
839  if (!ok) {
840    callbacks_->OnError(
841        IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
842                               "Internal error in get_record."));
843    return;
844  }
845
846  if (value.empty()) {
847    callbacks_->OnSuccess();
848    return;
849  }
850  if (auto_increment_ && !key_path_.IsNull()) {
851    callbacks_->OnSuccess(&value, *primary_key, key_path_);
852    return;
853  }
854  callbacks_->OnSuccess(&value);
855}
856
857static scoped_ptr<IndexedDBKey> GenerateKey(
858    scoped_refptr<IndexedDBBackingStore> backing_store,
859    scoped_refptr<IndexedDBTransaction> transaction,
860    int64 database_id,
861    int64 object_store_id) {
862  const int64 max_generator_value =
863      9007199254740992LL;  // Maximum integer storable as ECMAScript number.
864  int64 current_number;
865  bool ok = backing_store->GetKeyGeneratorCurrentNumber(
866      transaction->BackingStoreTransaction(),
867      database_id,
868      object_store_id,
869      &current_number);
870  if (!ok) {
871    LOG(ERROR) << "Failed to get_key_generator_current_number";
872    return make_scoped_ptr(new IndexedDBKey());
873  }
874  if (current_number < 0 || current_number > max_generator_value)
875    return make_scoped_ptr(new IndexedDBKey());
876
877  return make_scoped_ptr(
878      new IndexedDBKey(current_number, WebIDBKey::NumberType));
879}
880
881static bool UpdateKeyGenerator(
882    scoped_refptr<IndexedDBBackingStore> backing_store,
883    scoped_refptr<IndexedDBTransaction> transaction,
884    int64 database_id,
885    int64 object_store_id,
886    const IndexedDBKey* key,
887    bool check_current) {
888  DCHECK(key);
889  DCHECK_EQ(WebIDBKey::NumberType, key->type());
890  return backing_store->MaybeUpdateKeyGeneratorCurrentNumber(
891      transaction->BackingStoreTransaction(),
892      database_id,
893      object_store_id,
894      static_cast<int64>(floor(key->number())) + 1,
895      check_current);
896}
897
898void IndexedDBDatabase::Put(int64 transaction_id,
899                            int64 object_store_id,
900                            std::vector<char>* value,
901                            scoped_ptr<IndexedDBKey> key,
902                            PutMode put_mode,
903                            scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
904                            const std::vector<int64>& index_ids,
905                            const std::vector<IndexKeys>& index_keys) {
906  IDB_TRACE("IndexedDBDatabase::put");
907  TransactionMap::const_iterator trans_iterator =
908      transactions_.find(transaction_id);
909  if (trans_iterator == transactions_.end())
910    return;
911  IndexedDBTransaction* transaction = trans_iterator->second;
912  DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
913
914  const IndexedDBObjectStoreMetadata object_store_metadata =
915      metadata_.object_stores[object_store_id];
916
917  DCHECK(key);
918  DCHECK(object_store_metadata.auto_increment || key->IsValid());
919  transaction->ScheduleTask(new PutOperation(backing_store_,
920                                             id(),
921                                             object_store_metadata,
922                                             value,
923                                             key.Pass(),
924                                             put_mode,
925                                             callbacks,
926                                             index_ids,
927                                             index_keys));
928}
929
930void PutOperation::Perform(IndexedDBTransaction* transaction) {
931  IDB_TRACE("PutOperation");
932  DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
933  DCHECK_EQ(index_ids_.size(), index_keys_.size());
934  bool key_was_generated = false;
935
936  scoped_ptr<IndexedDBKey> key;
937  if (put_mode_ != IndexedDBDatabase::CURSOR_UPDATE &&
938      object_store_.auto_increment && !key_->IsValid()) {
939    scoped_ptr<IndexedDBKey> auto_inc_key = GenerateKey(
940        backing_store_, transaction, database_id_, object_store_.id);
941    key_was_generated = true;
942    if (!auto_inc_key->IsValid()) {
943      callbacks_->OnError(
944          IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionConstraintError,
945                                 "Maximum key generator value reached."));
946      return;
947    }
948    key = auto_inc_key.Pass();
949  } else {
950    key = key_.Pass();
951  }
952
953  DCHECK(key->IsValid());
954
955  IndexedDBBackingStore::RecordIdentifier record_identifier;
956  if (put_mode_ == IndexedDBDatabase::ADD_ONLY) {
957    bool found = false;
958    bool ok = backing_store_->KeyExistsInObjectStore(
959        transaction->BackingStoreTransaction(),
960        database_id_,
961        object_store_.id,
962        *key.get(),
963        &record_identifier,
964        &found);
965    if (!ok) {
966      callbacks_->OnError(
967          IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
968                                 "Internal error checking key existence."));
969      return;
970    }
971    if (found) {
972      callbacks_->OnError(
973          IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionConstraintError,
974                                 "Key already exists in the object store."));
975      return;
976    }
977  }
978
979  ScopedVector<IndexedDBObjectStoreImpl::IndexWriter> index_writers;
980  string16 error_message;
981  bool obeys_constraints = false;
982  bool backing_store_success =
983      IndexedDBObjectStoreImpl::MakeIndexWriters(transaction,
984                                                 backing_store_.get(),
985                                                 database_id_,
986                                                 object_store_,
987                                                 *key,
988                                                 key_was_generated,
989                                                 index_ids_,
990                                                 index_keys_,
991                                                 &index_writers,
992                                                 &error_message,
993                                                 &obeys_constraints);
994  if (!backing_store_success) {
995    callbacks_->OnError(IndexedDBDatabaseError(
996        WebKit::WebIDBDatabaseExceptionUnknownError,
997        "Internal error: backing store error updating index keys."));
998    return;
999  }
1000  if (!obeys_constraints) {
1001    callbacks_->OnError(IndexedDBDatabaseError(
1002        WebKit::WebIDBDatabaseExceptionConstraintError, error_message));
1003    return;
1004  }
1005
1006  // Before this point, don't do any mutation. After this point, rollback the
1007  // transaction in case of error.
1008  backing_store_success =
1009      backing_store_->PutRecord(transaction->BackingStoreTransaction(),
1010                                database_id_,
1011                                object_store_.id,
1012                                *key.get(),
1013                                value_,
1014                                &record_identifier);
1015  if (!backing_store_success) {
1016    callbacks_->OnError(IndexedDBDatabaseError(
1017        WebKit::WebIDBDatabaseExceptionUnknownError,
1018        "Internal error: backing store error performing put/add."));
1019    return;
1020  }
1021
1022  for (size_t i = 0; i < index_writers.size(); ++i) {
1023    IndexedDBObjectStoreImpl::IndexWriter* index_writer = index_writers[i];
1024    index_writer->WriteIndexKeys(record_identifier,
1025                                 backing_store_.get(),
1026                                 transaction->BackingStoreTransaction(),
1027                                 database_id_,
1028                                 object_store_.id);
1029  }
1030
1031  if (object_store_.auto_increment &&
1032      put_mode_ != IndexedDBDatabase::CURSOR_UPDATE &&
1033      key->type() == WebIDBKey::NumberType) {
1034    bool ok = UpdateKeyGenerator(backing_store_,
1035                                 transaction,
1036                                 database_id_,
1037                                 object_store_.id,
1038                                 key.get(),
1039                                 !key_was_generated);
1040    if (!ok) {
1041      callbacks_->OnError(
1042          IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
1043                                 "Internal error updating key generator."));
1044      return;
1045    }
1046  }
1047
1048  callbacks_->OnSuccess(*key);
1049}
1050
1051void IndexedDBDatabase::SetIndexKeys(int64 transaction_id,
1052                                     int64 object_store_id,
1053                                     scoped_ptr<IndexedDBKey> primary_key,
1054                                     const std::vector<int64>& index_ids,
1055                                     const std::vector<IndexKeys>& index_keys) {
1056  IDB_TRACE("IndexedDBDatabase::set_index_keys");
1057  TransactionMap::const_iterator trans_iterator =
1058      transactions_.find(transaction_id);
1059  if (trans_iterator == transactions_.end())
1060    return;
1061  IndexedDBTransaction* transaction = trans_iterator->second;
1062  DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
1063
1064  scoped_refptr<IndexedDBBackingStore> store = BackingStore();
1065  // TODO(jsbell): This method could be asynchronous, but we need to
1066  // evaluate if it's worth the extra complexity.
1067  IndexedDBBackingStore::RecordIdentifier record_identifier;
1068  bool found = false;
1069  bool ok =
1070      store->KeyExistsInObjectStore(transaction->BackingStoreTransaction(),
1071                                    metadata_.id,
1072                                    object_store_id,
1073                                    *primary_key,
1074                                    &record_identifier,
1075                                    &found);
1076  if (!ok) {
1077    transaction->Abort(
1078        IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
1079                               "Internal error setting index keys."));
1080    return;
1081  }
1082  if (!found) {
1083    transaction->Abort(IndexedDBDatabaseError(
1084        WebKit::WebIDBDatabaseExceptionUnknownError,
1085        "Internal error setting index keys for object store."));
1086    return;
1087  }
1088
1089  ScopedVector<IndexedDBObjectStoreImpl::IndexWriter> index_writers;
1090  string16 error_message;
1091  bool obeys_constraints = false;
1092  DCHECK(metadata_.object_stores.find(object_store_id) !=
1093         metadata_.object_stores.end());
1094  const IndexedDBObjectStoreMetadata& object_store_metadata =
1095      metadata_.object_stores[object_store_id];
1096  bool backing_store_success =
1097      IndexedDBObjectStoreImpl::MakeIndexWriters(transaction,
1098                                                 store.get(),
1099                                                 id(),
1100                                                 object_store_metadata,
1101                                                 *primary_key,
1102                                                 false,
1103                                                 index_ids,
1104                                                 index_keys,
1105                                                 &index_writers,
1106                                                 &error_message,
1107                                                 &obeys_constraints);
1108  if (!backing_store_success) {
1109    transaction->Abort(IndexedDBDatabaseError(
1110        WebKit::WebIDBDatabaseExceptionUnknownError,
1111        "Internal error: backing store error updating index keys."));
1112    return;
1113  }
1114  if (!obeys_constraints) {
1115    transaction->Abort(IndexedDBDatabaseError(
1116        WebKit::WebIDBDatabaseExceptionConstraintError, error_message));
1117    return;
1118  }
1119
1120  for (size_t i = 0; i < index_writers.size(); ++i) {
1121    IndexedDBObjectStoreImpl::IndexWriter* index_writer = index_writers[i];
1122    index_writer->WriteIndexKeys(record_identifier,
1123                                 store.get(),
1124                                 transaction->BackingStoreTransaction(),
1125                                 id(),
1126                                 object_store_id);
1127  }
1128}
1129
1130void IndexedDBDatabase::SetIndexesReady(int64 transaction_id,
1131                                        int64,
1132                                        const std::vector<int64>& index_ids) {
1133  IDB_TRACE("IndexedDBObjectStoreImpl::set_indexes_ready");
1134
1135  TransactionMap::const_iterator trans_iterator =
1136      transactions_.find(transaction_id);
1137  if (trans_iterator == transactions_.end())
1138    return;
1139  IndexedDBTransaction* transaction = trans_iterator->second;
1140
1141  transaction->ScheduleTask(IndexedDBDatabase::PREEMPTIVE_TASK,
1142                            new SetIndexesReadyOperation(index_ids.size()));
1143}
1144
1145void SetIndexesReadyOperation::Perform(IndexedDBTransaction* transaction) {
1146  IDB_TRACE("SetIndexesReadyOperation");
1147  for (size_t i = 0; i < index_count_; ++i)
1148    transaction->DidCompletePreemptiveEvent();
1149}
1150
1151void IndexedDBDatabase::OpenCursor(
1152    int64 transaction_id,
1153    int64 object_store_id,
1154    int64 index_id,
1155    scoped_ptr<IndexedDBKeyRange> key_range,
1156    indexed_db::CursorDirection direction,
1157    bool key_only,
1158    TaskType task_type,
1159    scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
1160  IDB_TRACE("IndexedDBDatabase::open_cursor");
1161  TransactionMap::const_iterator trans_iterator =
1162      transactions_.find(transaction_id);
1163  if (trans_iterator == transactions_.end())
1164    return;
1165  IndexedDBTransaction* transaction = trans_iterator->second;
1166
1167  transaction->ScheduleTask(new OpenCursorOperation(
1168      backing_store_,
1169      id(),
1170      object_store_id,
1171      index_id,
1172      key_range.Pass(),
1173      direction,
1174      key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE,
1175      task_type,
1176      callbacks));
1177}
1178
1179void OpenCursorOperation::Perform(IndexedDBTransaction* transaction) {
1180  IDB_TRACE("OpenCursorOperation");
1181
1182  // The frontend has begun indexing, so this pauses the transaction
1183  // until the indexing is complete. This can't happen any earlier
1184  // because we don't want to switch to early mode in case multiple
1185  // indexes are being created in a row, with Put()'s in between.
1186  if (task_type_ == IndexedDBDatabase::PREEMPTIVE_TASK)
1187    transaction->AddPreemptiveEvent();
1188
1189  scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1190  if (index_id_ == IndexedDBIndexMetadata::kInvalidId) {
1191    DCHECK_NE(cursor_type_, indexed_db::CURSOR_KEY_ONLY);
1192    backing_store_cursor = backing_store_->OpenObjectStoreCursor(
1193        transaction->BackingStoreTransaction(),
1194        database_id_,
1195        object_store_id_,
1196        *key_range_,
1197        direction_);
1198  } else {
1199    DCHECK_EQ(task_type_, IndexedDBDatabase::NORMAL_TASK);
1200    if (cursor_type_ == indexed_db::CURSOR_KEY_ONLY) {
1201      backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1202          transaction->BackingStoreTransaction(),
1203          database_id_,
1204          object_store_id_,
1205          index_id_,
1206          *key_range_,
1207          direction_);
1208    } else {
1209      backing_store_cursor = backing_store_->OpenIndexCursor(
1210          transaction->BackingStoreTransaction(),
1211          database_id_,
1212          object_store_id_,
1213          index_id_,
1214          *key_range_,
1215          direction_);
1216    }
1217  }
1218
1219  if (!backing_store_cursor) {
1220    callbacks_->OnSuccess(static_cast<std::vector<char>*>(NULL));
1221    return;
1222  }
1223
1224  IndexedDBDatabase::TaskType task_type(
1225      static_cast<IndexedDBDatabase::TaskType>(task_type_));
1226  scoped_refptr<IndexedDBCursor> cursor = IndexedDBCursor::Create(
1227      backing_store_cursor.Pass(), cursor_type_, task_type, transaction);
1228  callbacks_->OnSuccess(
1229      cursor, cursor->key(), cursor->primary_key(), cursor->Value());
1230}
1231
1232void IndexedDBDatabase::Count(
1233    int64 transaction_id,
1234    int64 object_store_id,
1235    int64 index_id,
1236    scoped_ptr<IndexedDBKeyRange> key_range,
1237    scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
1238  IDB_TRACE("IndexedDBDatabase::count");
1239  TransactionMap::const_iterator trans_iterator =
1240      transactions_.find(transaction_id);
1241  if (trans_iterator == transactions_.end())
1242    return;
1243  IndexedDBTransaction* transaction = trans_iterator->second;
1244
1245  DCHECK(metadata_.object_stores.find(object_store_id) !=
1246         metadata_.object_stores.end());
1247  transaction->ScheduleTask(new CountOperation(backing_store_,
1248                                               id(),
1249                                               object_store_id,
1250                                               index_id,
1251                                               key_range.Pass(),
1252                                               callbacks));
1253}
1254
1255void CountOperation::Perform(IndexedDBTransaction* transaction) {
1256  IDB_TRACE("CountOperation");
1257  uint32 count = 0;
1258  scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1259
1260  if (index_id_ == IndexedDBIndexMetadata::kInvalidId) {
1261    backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
1262        transaction->BackingStoreTransaction(),
1263        database_id_,
1264        object_store_id_,
1265        *key_range_,
1266        indexed_db::CURSOR_NEXT);
1267  } else {
1268    backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1269        transaction->BackingStoreTransaction(),
1270        database_id_,
1271        object_store_id_,
1272        index_id_,
1273        *key_range_,
1274        indexed_db::CURSOR_NEXT);
1275  }
1276  if (!backing_store_cursor) {
1277    callbacks_->OnSuccess(count);
1278    return;
1279  }
1280
1281  do {
1282    ++count;
1283  } while (backing_store_cursor->ContinueFunction(0));
1284
1285  callbacks_->OnSuccess(count);
1286}
1287
1288void IndexedDBDatabase::DeleteRange(
1289    int64 transaction_id,
1290    int64 object_store_id,
1291    scoped_ptr<IndexedDBKeyRange> key_range,
1292    scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
1293  IDB_TRACE("IndexedDBDatabase::delete_range");
1294  TransactionMap::const_iterator trans_iterator =
1295      transactions_.find(transaction_id);
1296  if (trans_iterator == transactions_.end())
1297    return;
1298  IndexedDBTransaction* transaction = trans_iterator->second;
1299
1300  transaction->ScheduleTask(new DeleteRangeOperation(
1301      backing_store_, id(), object_store_id, key_range.Pass(), callbacks));
1302}
1303
1304void DeleteRangeOperation::Perform(IndexedDBTransaction* transaction) {
1305  IDB_TRACE("DeleteRangeOperation");
1306  scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor =
1307      backing_store_->OpenObjectStoreCursor(
1308          transaction->BackingStoreTransaction(),
1309          database_id_,
1310          object_store_id_,
1311          *key_range_,
1312          indexed_db::CURSOR_NEXT);
1313  if (backing_store_cursor) {
1314    do {
1315      if (!backing_store_->DeleteRecord(
1316              transaction->BackingStoreTransaction(),
1317              database_id_,
1318              object_store_id_,
1319              backing_store_cursor->record_identifier())) {
1320        callbacks_->OnError(
1321            IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
1322                                   "Internal error deleting data in range"));
1323        return;
1324      }
1325    } while (backing_store_cursor->ContinueFunction(0));
1326  }
1327
1328  callbacks_->OnSuccess();
1329}
1330
1331void IndexedDBDatabase::Clear(
1332    int64 transaction_id,
1333    int64 object_store_id,
1334    scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
1335  IDB_TRACE("IndexedDBDatabase::clear");
1336  TransactionMap::const_iterator trans_iterator =
1337      transactions_.find(transaction_id);
1338  if (trans_iterator == transactions_.end())
1339    return;
1340  IndexedDBTransaction* transaction = trans_iterator->second;
1341  DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
1342
1343  transaction->ScheduleTask(
1344      new ClearOperation(backing_store_, id(), object_store_id, callbacks));
1345}
1346
1347void ClearOperation::Perform(IndexedDBTransaction* transaction) {
1348  IDB_TRACE("ObjectStoreClearOperation");
1349  if (!backing_store_->ClearObjectStore(transaction->BackingStoreTransaction(),
1350                                        database_id_,
1351                                        object_store_id_)) {
1352    callbacks_->OnError(
1353        IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
1354                               "Internal error clearing object store"));
1355    return;
1356  }
1357  callbacks_->OnSuccess();
1358}
1359
1360void DeleteObjectStoreOperation::Perform(IndexedDBTransaction* transaction) {
1361  IDB_TRACE("DeleteObjectStoreOperation");
1362  bool ok =
1363      backing_store_->DeleteObjectStore(transaction->BackingStoreTransaction(),
1364                                        transaction->database()->id(),
1365                                        object_store_metadata_.id);
1366  if (!ok) {
1367    string16 error_string =
1368        ASCIIToUTF16("Internal error deleting object store '") +
1369        object_store_metadata_.name + ASCIIToUTF16("'.");
1370    transaction->Abort(IndexedDBDatabaseError(
1371        WebKit::WebIDBDatabaseExceptionUnknownError, error_string));
1372  }
1373}
1374
1375void IndexedDBDatabase::VersionChangeOperation::Perform(
1376    IndexedDBTransaction* transaction) {
1377  IDB_TRACE("VersionChangeOperation");
1378  int64 database_id = database_->id();
1379  int64 old_version = database_->metadata_.int_version;
1380  DCHECK_GT(version_, old_version);
1381  database_->metadata_.int_version = version_;
1382  if (!database_->backing_store_->UpdateIDBDatabaseIntVersion(
1383          transaction->BackingStoreTransaction(),
1384          database_id,
1385          database_->metadata_.int_version)) {
1386    IndexedDBDatabaseError error(
1387        WebKit::WebIDBDatabaseExceptionUnknownError,
1388        ASCIIToUTF16("Internal error writing data to stable storage when "
1389                     "updating version."));
1390    callbacks_->OnError(error);
1391    transaction->Abort(error);
1392    return;
1393  }
1394  DCHECK(!database_->pending_second_half_open_);
1395  database_->pending_second_half_open_.reset(new PendingOpenCall(
1396      callbacks_, database_callbacks_, transaction_id_, version_));
1397  callbacks_->OnUpgradeNeeded(old_version, database_, database_->metadata());
1398}
1399
1400void IndexedDBDatabase::TransactionStarted(IndexedDBTransaction* transaction) {
1401
1402  if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
1403    DCHECK(!running_version_change_transaction_);
1404    running_version_change_transaction_ = transaction;
1405  }
1406}
1407
1408void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction) {
1409
1410  DCHECK(transactions_.find(transaction->id()) != transactions_.end());
1411  DCHECK_EQ(transactions_[transaction->id()], transaction);
1412  transactions_.erase(transaction->id());
1413  if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
1414    DCHECK_EQ(transaction, running_version_change_transaction_);
1415    running_version_change_transaction_ = NULL;
1416  }
1417}
1418
1419void IndexedDBDatabase::TransactionFinishedAndAbortFired(
1420    IndexedDBTransaction* transaction) {
1421  if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
1422    if (pending_second_half_open_) {
1423      pending_second_half_open_->Callbacks()->OnError(
1424          IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionAbortError,
1425                                 "Version change transaction was aborted in "
1426                                 "upgradeneeded event handler."));
1427      pending_second_half_open_.reset();
1428    }
1429    ProcessPendingCalls();
1430  }
1431}
1432
1433void IndexedDBDatabase::TransactionFinishedAndCompleteFired(
1434    IndexedDBTransaction* transaction) {
1435  if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
1436    DCHECK(pending_second_half_open_);
1437    if (pending_second_half_open_) {
1438      DCHECK_EQ(pending_second_half_open_->Version(), metadata_.int_version);
1439      DCHECK(metadata_.id != kInvalidId);
1440      pending_second_half_open_->Callbacks()->OnSuccess(this, this->metadata());
1441      pending_second_half_open_.reset();
1442    }
1443    ProcessPendingCalls();
1444  }
1445}
1446
1447size_t IndexedDBDatabase::ConnectionCount() const {
1448  // This does not include pending open calls, as those should not block version
1449  // changes and deletes.
1450  return database_callbacks_set_.size();
1451}
1452
1453void IndexedDBDatabase::ProcessPendingCalls() {
1454  if (pending_second_half_open_) {
1455    DCHECK_EQ(pending_second_half_open_->Version(), metadata_.int_version);
1456    DCHECK(metadata_.id != kInvalidId);
1457    scoped_ptr<PendingOpenCall> pending_call = pending_second_half_open_.Pass();
1458    pending_call->Callbacks()->OnSuccess(this, this->metadata());
1459    // Fall through when complete, as pending opens may be unblocked.
1460  }
1461
1462  if (pending_run_version_change_transaction_call_ && ConnectionCount() == 1) {
1463    DCHECK(pending_run_version_change_transaction_call_->Version() >
1464           metadata_.int_version);
1465    scoped_ptr<PendingOpenCall> pending_call =
1466        pending_run_version_change_transaction_call_.Pass();
1467    RunVersionChangeTransactionFinal(pending_call->Callbacks(),
1468                                     pending_call->DatabaseCallbacks(),
1469                                     pending_call->TransactionId(),
1470                                     pending_call->Version());
1471    DCHECK_EQ(static_cast<size_t>(1), ConnectionCount());
1472    // Fall through would be a no-op, since transaction must complete
1473    // asynchronously.
1474    DCHECK(IsDeleteDatabaseBlocked());
1475    DCHECK(IsOpenConnectionBlocked());
1476    return;
1477  }
1478
1479  if (!IsDeleteDatabaseBlocked()) {
1480    PendingDeleteCallList pending_delete_calls;
1481    pending_delete_calls_.swap(pending_delete_calls);
1482    while (!pending_delete_calls.empty()) {
1483      // Only the first delete call will delete the database, but each must fire
1484      // callbacks.
1485      scoped_ptr<PendingDeleteCall> pending_delete_call(
1486          pending_delete_calls.front());
1487      pending_delete_calls.pop_front();
1488      DeleteDatabaseFinal(pending_delete_call->Callbacks());
1489    }
1490    // delete_database_final should never re-queue calls.
1491    DCHECK(pending_delete_calls_.empty());
1492    // Fall through when complete, as pending opens may be unblocked.
1493  }
1494
1495  if (!IsOpenConnectionBlocked()) {
1496    PendingOpenCallList pending_open_calls;
1497    pending_open_calls_.swap(pending_open_calls);
1498    while (!pending_open_calls.empty()) {
1499      scoped_ptr<PendingOpenCall> pending_open_call(pending_open_calls.front());
1500      pending_open_calls.pop_front();
1501      OpenConnection(pending_open_call->Callbacks(),
1502                     pending_open_call->DatabaseCallbacks(),
1503                     pending_open_call->TransactionId(),
1504                     pending_open_call->Version());
1505    }
1506  }
1507}
1508
1509void IndexedDBDatabase::CreateTransaction(
1510    int64 transaction_id,
1511    scoped_refptr<IndexedDBDatabaseCallbacksWrapper> callbacks,
1512    const std::vector<int64>& object_store_ids,
1513    uint16 mode) {
1514
1515  DCHECK(database_callbacks_set_.has(callbacks));
1516
1517  scoped_refptr<IndexedDBTransaction> transaction =
1518      IndexedDBTransaction::Create(
1519          transaction_id,
1520          callbacks,
1521          object_store_ids,
1522          static_cast<indexed_db::TransactionMode>(mode),
1523          this);
1524  DCHECK(transactions_.find(transaction_id) == transactions_.end());
1525  transactions_[transaction_id] = transaction.get();
1526}
1527
1528bool IndexedDBDatabase::IsOpenConnectionBlocked() const {
1529  return !pending_delete_calls_.empty() ||
1530         running_version_change_transaction_ ||
1531         pending_run_version_change_transaction_call_;
1532}
1533
1534void IndexedDBDatabase::OpenConnection(
1535    scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
1536    scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
1537    int64 transaction_id,
1538    int64 version) {
1539  DCHECK(backing_store_.get());
1540
1541  // TODO(jsbell): Should have a priority queue so that higher version
1542  // requests are processed first. http://crbug.com/225850
1543  if (IsOpenConnectionBlocked()) {
1544    pending_open_calls_.push_back(new PendingOpenCall(
1545        callbacks, database_callbacks, transaction_id, version));
1546    return;
1547  }
1548
1549  if (metadata_.id == kInvalidId) {
1550    // The database was deleted then immediately re-opened; OpenInternal()
1551    // recreates it in the backing store.
1552    if (OpenInternal()) {
1553      DCHECK_EQ(metadata_.int_version,
1554                IndexedDBDatabaseMetadata::NO_INT_VERSION);
1555    } else {
1556      string16 message;
1557      if (version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
1558        message = ASCIIToUTF16(
1559            "Internal error opening database with no version specified.");
1560      else
1561        message =
1562            ASCIIToUTF16("Internal error opening database with version ") +
1563            Int64ToString16(version);
1564      callbacks->OnError(IndexedDBDatabaseError(
1565          WebKit::WebIDBDatabaseExceptionUnknownError, message));
1566      return;
1567    }
1568  }
1569
1570  // We infer that the database didn't exist from its lack of either type of
1571  // version.
1572  bool is_new_database =
1573      metadata_.version == kNoStringVersion &&
1574      metadata_.int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION;
1575
1576  if (version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION) {
1577    // For unit tests only - skip upgrade steps. Calling from script with
1578    // DEFAULT_INT_VERSION throws exception.
1579    // TODO(jsbell): Assert that we're executing a unit test.
1580    DCHECK(is_new_database);
1581    database_callbacks_set_.insert(database_callbacks);
1582    callbacks->OnSuccess(this, this->metadata());
1583    return;
1584  }
1585
1586  if (version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
1587    if (!is_new_database) {
1588      database_callbacks_set_.insert(database_callbacks);
1589      callbacks->OnSuccess(this, this->metadata());
1590      return;
1591    }
1592    // Spec says: If no version is specified and no database exists, set
1593    // database version to 1.
1594    version = 1;
1595  }
1596
1597  if (version > metadata_.int_version) {
1598    database_callbacks_set_.insert(database_callbacks);
1599    RunVersionChangeTransaction(
1600        callbacks, database_callbacks, transaction_id, version);
1601    return;
1602  }
1603  if (version < metadata_.int_version) {
1604    callbacks->OnError(IndexedDBDatabaseError(
1605        WebKit::WebIDBDatabaseExceptionVersionError,
1606        ASCIIToUTF16("The requested version (") + Int64ToString16(version) +
1607            ASCIIToUTF16(") is less than the existing version (") +
1608            Int64ToString16(metadata_.int_version) + ASCIIToUTF16(").")));
1609    return;
1610  }
1611  DCHECK_EQ(version, metadata_.int_version);
1612  database_callbacks_set_.insert(database_callbacks);
1613  callbacks->OnSuccess(this, this->metadata());
1614}
1615
1616void IndexedDBDatabase::RunVersionChangeTransaction(
1617    scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
1618    scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
1619    int64 transaction_id,
1620    int64 requested_version) {
1621
1622  DCHECK(callbacks.get());
1623  DCHECK(database_callbacks_set_.has(database_callbacks));
1624  if (ConnectionCount() > 1) {
1625    // Front end ensures the event is not fired at connections that have
1626    // close_pending set.
1627    for (DatabaseCallbacksSet::const_iterator it =
1628             database_callbacks_set_.begin();
1629         it != database_callbacks_set_.end();
1630         ++it) {
1631      if (it->get() != database_callbacks.get())
1632        (*it)->OnVersionChange(metadata_.int_version, requested_version);
1633    }
1634    // TODO(jsbell): Remove the call to on_blocked and instead wait
1635    // until the frontend tells us that all the "versionchange" events
1636    // have been delivered.  http://crbug.com/100123
1637    callbacks->OnBlocked(metadata_.int_version);
1638
1639    DCHECK(!pending_run_version_change_transaction_call_);
1640    pending_run_version_change_transaction_call_.reset(new PendingOpenCall(
1641        callbacks, database_callbacks, transaction_id, requested_version));
1642    return;
1643  }
1644  RunVersionChangeTransactionFinal(
1645      callbacks, database_callbacks, transaction_id, requested_version);
1646}
1647
1648void IndexedDBDatabase::RunVersionChangeTransactionFinal(
1649    scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
1650    scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
1651    int64 transaction_id,
1652    int64 requested_version) {
1653
1654  std::vector<int64> object_store_ids;
1655  CreateTransaction(transaction_id,
1656                    database_callbacks,
1657                    object_store_ids,
1658                    indexed_db::TRANSACTION_VERSION_CHANGE);
1659  scoped_refptr<IndexedDBTransaction> transaction =
1660      transactions_[transaction_id];
1661
1662  transaction->ScheduleTask(
1663      new VersionChangeOperation(this,
1664                                 transaction_id,
1665                                 requested_version,
1666                                 callbacks,
1667                                 database_callbacks),
1668      new VersionChangeAbortOperation(
1669          this, metadata_.version, metadata_.int_version));
1670
1671  DCHECK(!pending_second_half_open_);
1672}
1673
1674void IndexedDBDatabase::DeleteDatabase(
1675    scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
1676
1677  if (IsDeleteDatabaseBlocked()) {
1678    for (DatabaseCallbacksSet::const_iterator it =
1679             database_callbacks_set_.begin();
1680         it != database_callbacks_set_.end();
1681         ++it) {
1682      // Front end ensures the event is not fired at connections that have
1683      // close_pending set.
1684      (*it)->OnVersionChange(metadata_.int_version,
1685                             IndexedDBDatabaseMetadata::NO_INT_VERSION);
1686    }
1687    // TODO(jsbell): Only fire on_blocked if there are open
1688    // connections after the VersionChangeEvents are received, not
1689    // just set up to fire.  http://crbug.com/100123
1690    callbacks->OnBlocked(metadata_.int_version);
1691    pending_delete_calls_.push_back(new PendingDeleteCall(callbacks));
1692    return;
1693  }
1694  DeleteDatabaseFinal(callbacks);
1695}
1696
1697bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const {
1698  return !!ConnectionCount();
1699}
1700
1701void IndexedDBDatabase::DeleteDatabaseFinal(
1702    scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
1703  DCHECK(!IsDeleteDatabaseBlocked());
1704  DCHECK(backing_store_.get());
1705  if (!backing_store_->DeleteDatabase(metadata_.name)) {
1706    callbacks->OnError(
1707        IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
1708                               "Internal error deleting database."));
1709    return;
1710  }
1711  metadata_.version = kNoStringVersion;
1712  metadata_.id = kInvalidId;
1713  metadata_.int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
1714  metadata_.object_stores.clear();
1715  callbacks->OnSuccess();
1716}
1717
1718void IndexedDBDatabase::Close(
1719    scoped_refptr<IndexedDBDatabaseCallbacksWrapper> callbacks) {
1720  DCHECK(callbacks.get());
1721  DCHECK(database_callbacks_set_.has(callbacks));
1722
1723  // Close outstanding transactions from the closing connection. This
1724  // can not happen if the close is requested by the connection itself
1725  // as the front-end defers the close until all transactions are
1726  // complete, so something unusual has happened e.g. unexpected
1727  // process termination.
1728  {
1729    TransactionMap transactions(transactions_);
1730    for (TransactionMap::const_iterator it = transactions.begin(),
1731                                        end = transactions.end();
1732         it != end;
1733         ++it) {
1734      if (it->second->connection() == callbacks.get())
1735        it->second->Abort(
1736            IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
1737                                   "Connection is closing."));
1738    }
1739  }
1740
1741  database_callbacks_set_.erase(callbacks);
1742  if (pending_second_half_open_ &&
1743      pending_second_half_open_->DatabaseCallbacks().get() == callbacks.get()) {
1744    pending_second_half_open_->Callbacks()->OnError(
1745        IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionAbortError,
1746                               "The connection was closed."));
1747    pending_second_half_open_.reset();
1748  }
1749
1750  // process_pending_calls allows the inspector to process a pending open call
1751  // and call close, reentering IndexedDBDatabase::close. Then the
1752  // backend would be removed both by the inspector closing its connection, and
1753  // by the connection that first called close.
1754  // To avoid that situation, don't proceed in case of reentrancy.
1755  if (closing_connection_)
1756    return;
1757  base::AutoReset<bool> ClosingConnection(&closing_connection_, true);
1758  ProcessPendingCalls();
1759
1760  // TODO(jsbell): Add a test for the pending_open_calls_ cases below.
1761  if (!ConnectionCount() && !pending_open_calls_.size() &&
1762      !pending_delete_calls_.size()) {
1763    DCHECK(transactions_.empty());
1764
1765    backing_store_ = NULL;
1766
1767    // This check should only be false in unit tests.
1768    // TODO(jsbell): Assert factory_ || we're executing a unit test.
1769    if (factory_.get())
1770      factory_->RemoveIDBDatabaseBackend(identifier_);
1771  }
1772}
1773
1774void CreateObjectStoreAbortOperation::Perform(
1775    IndexedDBTransaction* transaction) {
1776  IDB_TRACE("CreateObjectStoreAbortOperation");
1777  DCHECK(!transaction);
1778  database_->RemoveObjectStore(object_store_id_);
1779}
1780
1781void DeleteObjectStoreAbortOperation::Perform(
1782    IndexedDBTransaction* transaction) {
1783  IDB_TRACE("DeleteObjectStoreAbortOperation");
1784  DCHECK(!transaction);
1785  database_->AddObjectStore(object_store_metadata_,
1786                            IndexedDBObjectStoreMetadata::kInvalidId);
1787}
1788
1789void IndexedDBDatabase::VersionChangeAbortOperation::Perform(
1790    IndexedDBTransaction* transaction) {
1791  IDB_TRACE("VersionChangeAbortOperation");
1792  DCHECK(!transaction);
1793  database_->metadata_.version = previous_version_;
1794  database_->metadata_.int_version = previous_int_version_;
1795}
1796
1797}  // namespace content
1798