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 <stddef.h>
8 #include <utility>
9 
10 #include "base/logging.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "content/browser/indexed_db/indexed_db_backing_store.h"
13 #include "content/browser/indexed_db/indexed_db_tracing.h"
14 #include "content/browser/indexed_db/indexed_db_transaction.h"
15 #include "third_party/blink/public/common/indexeddb/indexeddb_metadata.h"
16 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
17 
18 using base::ASCIIToUTF16;
19 using blink::IndexedDBIndexKeys;
20 using blink::IndexedDBIndexMetadata;
21 using blink::IndexedDBKey;
22 using blink::IndexedDBObjectStoreMetadata;
23 
24 namespace content {
25 
IndexWriter(const IndexedDBIndexMetadata & index_metadata)26 IndexWriter::IndexWriter(
27     const IndexedDBIndexMetadata& index_metadata)
28     : index_metadata_(index_metadata) {}
29 
IndexWriter(const IndexedDBIndexMetadata & index_metadata,const std::vector<IndexedDBKey> & keys)30 IndexWriter::IndexWriter(const IndexedDBIndexMetadata& index_metadata,
31                          const std::vector<IndexedDBKey>& keys)
32     : index_metadata_(index_metadata), keys_(keys) {}
33 
~IndexWriter()34 IndexWriter::~IndexWriter() {}
35 
VerifyIndexKeys(IndexedDBBackingStore * backing_store,IndexedDBBackingStore::Transaction * transaction,int64_t database_id,int64_t object_store_id,int64_t index_id,bool * can_add_keys,const IndexedDBKey & primary_key,base::string16 * error_message) const36 bool IndexWriter::VerifyIndexKeys(
37     IndexedDBBackingStore* backing_store,
38     IndexedDBBackingStore::Transaction* transaction,
39     int64_t database_id,
40     int64_t object_store_id,
41     int64_t index_id,
42     bool* can_add_keys,
43     const IndexedDBKey& primary_key,
44     base::string16* error_message) const {
45   *can_add_keys = false;
46   for (const auto& key : keys_) {
47     bool ok = AddingKeyAllowed(backing_store, transaction, database_id,
48                                object_store_id, index_id, key, primary_key,
49                                can_add_keys);
50     if (!ok)
51       return false;
52     if (!*can_add_keys) {
53       if (error_message) {
54         *error_message = ASCIIToUTF16("Unable to add key to index '") +
55                          index_metadata_.name +
56                          ASCIIToUTF16("': at least one key does not satisfy "
57                                       "the uniqueness requirements.");
58       }
59       return true;
60     }
61   }
62   *can_add_keys = true;
63   return true;
64 }
65 
WriteIndexKeys(const IndexedDBBackingStore::RecordIdentifier & record_identifier,IndexedDBBackingStore * backing_store,IndexedDBBackingStore::Transaction * transaction,int64_t database_id,int64_t object_store_id) const66 leveldb::Status IndexWriter::WriteIndexKeys(
67     const IndexedDBBackingStore::RecordIdentifier& record_identifier,
68     IndexedDBBackingStore* backing_store,
69     IndexedDBBackingStore::Transaction* transaction,
70     int64_t database_id,
71     int64_t object_store_id) const {
72   int64_t index_id = index_metadata_.id;
73   for (const auto& key : keys_) {
74     leveldb::Status s = backing_store->PutIndexDataForRecord(
75         transaction, database_id, object_store_id, index_id, key,
76         record_identifier);
77     if (!s.ok())
78       return s;
79   }
80   return leveldb::Status::OK();
81 }
82 
AddingKeyAllowed(IndexedDBBackingStore * backing_store,IndexedDBBackingStore::Transaction * transaction,int64_t database_id,int64_t object_store_id,int64_t index_id,const IndexedDBKey & index_key,const IndexedDBKey & primary_key,bool * allowed) const83 bool IndexWriter::AddingKeyAllowed(
84     IndexedDBBackingStore* backing_store,
85     IndexedDBBackingStore::Transaction* transaction,
86     int64_t database_id,
87     int64_t object_store_id,
88     int64_t 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   std::unique_ptr<IndexedDBKey> found_primary_key;
99   bool found = false;
100   leveldb::Status s = 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 (!s.ok())
108     return false;
109   if (!found ||
110       (primary_key.IsValid() && found_primary_key->Equals(primary_key)))
111     *allowed = true;
112   return true;
113 }
114 
MakeIndexWriters(IndexedDBTransaction * transaction,IndexedDBBackingStore * backing_store,int64_t database_id,const IndexedDBObjectStoreMetadata & object_store,const IndexedDBKey & primary_key,bool key_was_generated,const std::vector<IndexedDBIndexKeys> & index_keys,std::vector<std::unique_ptr<IndexWriter>> * index_writers,base::string16 * error_message,bool * completed)115 bool MakeIndexWriters(IndexedDBTransaction* transaction,
116                       IndexedDBBackingStore* backing_store,
117                       int64_t database_id,
118                       const IndexedDBObjectStoreMetadata& object_store,
119                       const IndexedDBKey& primary_key,  // makes a copy
120                       bool key_was_generated,
121                       const std::vector<IndexedDBIndexKeys>& index_keys,
122                       std::vector<std::unique_ptr<IndexWriter>>* index_writers,
123                       base::string16* error_message,
124                       bool* completed) {
125   *completed = false;
126 
127   for (const auto& it : index_keys) {
128     const auto& found = object_store.indexes.find(it.id);
129     if (found == object_store.indexes.end())
130       continue;
131     const IndexedDBIndexMetadata& index = found->second;
132     // A copy is made because additional keys may be added.
133     std::vector<IndexedDBKey> keys = it.keys;
134 
135     // If the object_store is using a key generator to produce the primary key,
136     // and the store uses in-line keys, index key paths may reference it.
137     if (key_was_generated && !object_store.key_path.IsNull()) {
138       if (index.key_path == object_store.key_path) {
139         // The index key path is the same as the store's key path - no index key
140         // will have been sent by the front end, so synthesize one here.
141         keys.push_back(primary_key);
142 
143       } else if (index.key_path.type() == blink::mojom::IDBKeyPathType::Array) {
144         // An index with compound keys for a store with a key generator and
145         // in-line keys may need subkeys filled in. These are represented as
146         // "holes", which are not otherwise allowed.
147         for (size_t i = 0; i < keys.size(); ++i) {
148           if (keys[i].HasHoles())
149             keys[i] = keys[i].FillHoles(primary_key);
150         }
151       }
152     }
153 
154     std::unique_ptr<IndexWriter> index_writer(
155         std::make_unique<IndexWriter>(index, std::move(keys)));
156     bool can_add_keys = false;
157     bool backing_store_success =
158         index_writer->VerifyIndexKeys(backing_store,
159                                       transaction->BackingStoreTransaction(),
160                                       database_id,
161                                       object_store.id,
162                                       index.id,
163                                       &can_add_keys,
164                                       primary_key,
165                                       error_message);
166     if (!backing_store_success)
167       return false;
168     if (!can_add_keys)
169       return true;
170 
171     index_writers->push_back(std::move(index_writer));
172   }
173 
174   *completed = true;
175   return true;
176 }
177 
178 }  // namespace content
179