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_index_writer.h"
6
7#include "base/logging.h"
8#include "base/strings/utf_string_conversions.h"
9#include "content/browser/indexed_db/indexed_db_backing_store.h"
10#include "content/browser/indexed_db/indexed_db_tracing.h"
11#include "content/browser/indexed_db/indexed_db_transaction.h"
12#include "content/common/indexed_db/indexed_db_key.h"
13#include "content/common/indexed_db/indexed_db_key_path.h"
14#include "content/common/indexed_db/indexed_db_key_range.h"
15
16using base::ASCIIToUTF16;
17
18namespace content {
19
20IndexWriter::IndexWriter(
21    const IndexedDBIndexMetadata& index_metadata)
22    : index_metadata_(index_metadata) {}
23
24IndexWriter::IndexWriter(
25    const IndexedDBIndexMetadata& index_metadata,
26    const IndexedDBDatabase::IndexKeys& index_keys)
27    : index_metadata_(index_metadata), index_keys_(index_keys) {}
28
29IndexWriter::~IndexWriter() {}
30
31bool IndexWriter::VerifyIndexKeys(
32    IndexedDBBackingStore* backing_store,
33    IndexedDBBackingStore::Transaction* transaction,
34    int64 database_id,
35    int64 object_store_id,
36    int64 index_id,
37    bool* can_add_keys,
38    const IndexedDBKey& primary_key,
39    base::string16* error_message) const {
40  *can_add_keys = false;
41  DCHECK_EQ(index_id, index_keys_.first);
42  for (size_t i = 0; i < index_keys_.second.size(); ++i) {
43    bool ok = AddingKeyAllowed(backing_store,
44                               transaction,
45                               database_id,
46                               object_store_id,
47                               index_id,
48                               (index_keys_.second)[i],
49                               primary_key,
50                               can_add_keys);
51    if (!ok)
52      return false;
53    if (!*can_add_keys) {
54      if (error_message) {
55        *error_message = ASCIIToUTF16("Unable to add key to index '") +
56                         index_metadata_.name +
57                         ASCIIToUTF16("': at least one key does not satisfy "
58                                      "the uniqueness requirements.");
59      }
60      return true;
61    }
62  }
63  *can_add_keys = true;
64  return true;
65}
66
67void IndexWriter::WriteIndexKeys(
68    const IndexedDBBackingStore::RecordIdentifier& record_identifier,
69    IndexedDBBackingStore* backing_store,
70    IndexedDBBackingStore::Transaction* transaction,
71    int64 database_id,
72    int64 object_store_id) const {
73  int64 index_id = index_metadata_.id;
74  DCHECK_EQ(index_id, index_keys_.first);
75  for (size_t i = 0; i < index_keys_.second.size(); ++i) {
76    leveldb::Status s =
77        backing_store->PutIndexDataForRecord(transaction,
78                                             database_id,
79                                             object_store_id,
80                                             index_id,
81                                             index_keys_.second[i],
82                                             record_identifier);
83    // This should have already been verified as a valid write during
84    // verify_index_keys.
85    DCHECK(s.ok());
86  }
87}
88
89bool IndexWriter::AddingKeyAllowed(
90    IndexedDBBackingStore* backing_store,
91    IndexedDBBackingStore::Transaction* transaction,
92    int64 database_id,
93    int64 object_store_id,
94    int64 index_id,
95    const IndexedDBKey& index_key,
96    const IndexedDBKey& primary_key,
97    bool* allowed) const {
98  *allowed = false;
99  if (!index_metadata_.unique) {
100    *allowed = true;
101    return true;
102  }
103
104  scoped_ptr<IndexedDBKey> found_primary_key;
105  bool found = false;
106  leveldb::Status s = backing_store->KeyExistsInIndex(transaction,
107                                                      database_id,
108                                                      object_store_id,
109                                                      index_id,
110                                                      index_key,
111                                                      &found_primary_key,
112                                                      &found);
113  if (!s.ok())
114    return false;
115  if (!found ||
116      (primary_key.IsValid() && found_primary_key->Equals(primary_key)))
117    *allowed = true;
118  return true;
119}
120
121bool MakeIndexWriters(
122    IndexedDBTransaction* transaction,
123    IndexedDBBackingStore* backing_store,
124    int64 database_id,
125    const IndexedDBObjectStoreMetadata& object_store,
126    const IndexedDBKey& primary_key,  // makes a copy
127    bool key_was_generated,
128    const std::vector<IndexedDBDatabase::IndexKeys>& index_keys,
129    ScopedVector<IndexWriter>* index_writers,
130    base::string16* error_message,
131    bool* completed) {
132  *completed = false;
133
134  for (std::vector<IndexedDBDatabase::IndexKeys>::const_iterator it =
135           index_keys.begin();
136       it != index_keys.end();
137       ++it) {
138    IndexedDBObjectStoreMetadata::IndexMap::const_iterator found =
139        object_store.indexes.find(it->first);
140    if (found == object_store.indexes.end())
141      continue;
142    const IndexedDBIndexMetadata& index = found->second;
143    IndexedDBDatabase::IndexKeys keys = *it;
144
145    // If the object_store is using auto_increment, then any indexes with an
146    // identical key_path need to also use the primary (generated) key as a key.
147    if (key_was_generated && (index.key_path == object_store.key_path))
148      keys.second.push_back(primary_key);
149
150    scoped_ptr<IndexWriter> index_writer(new IndexWriter(index, keys));
151    bool can_add_keys = false;
152    bool backing_store_success =
153        index_writer->VerifyIndexKeys(backing_store,
154                                      transaction->BackingStoreTransaction(),
155                                      database_id,
156                                      object_store.id,
157                                      index.id,
158                                      &can_add_keys,
159                                      primary_key,
160                                      error_message);
161    if (!backing_store_success)
162      return false;
163    if (!can_add_keys)
164      return true;
165
166    index_writers->push_back(index_writer.release());
167  }
168
169  *completed = true;
170  return true;
171}
172
173}  // namespace content
174