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