d212de2b6b88affe0a35b0fd0fd33fc867cecbd1
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / server / SQLiteIDBTransaction.cpp
1 /*
2  * Copyright (C) 2013, 2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 #include "config.h"
26 #include "SQLiteIDBTransaction.h"
27
28 #if ENABLE(INDEXED_DATABASE)
29
30 #include "IDBCursorInfo.h"
31 #include "IndexedDB.h"
32 #include "Logging.h"
33 #include "SQLiteIDBBackingStore.h"
34 #include "SQLiteIDBCursor.h"
35 #include "SQLiteTransaction.h"
36 #include <wtf/FileSystem.h>
37
38 namespace WebCore {
39 namespace IDBServer {
40
41 SQLiteIDBTransaction::SQLiteIDBTransaction(SQLiteIDBBackingStore& backingStore, const IDBTransactionInfo& info)
42     : m_info(info)
43     , m_backingStore(backingStore)
44 {
45 }
46
47 SQLiteIDBTransaction::~SQLiteIDBTransaction()
48 {
49     if (inProgress())
50         m_sqliteTransaction->rollback();
51
52     // Explicitly clear cursors, as that also unregisters them from the backing store.
53     clearCursors();
54 }
55
56
57 IDBError SQLiteIDBTransaction::begin(SQLiteDatabase& database)
58 {
59     ASSERT(!m_sqliteTransaction);
60
61     m_sqliteTransaction = makeUnique<SQLiteTransaction>(database, m_info.mode() == IDBTransactionMode::Readonly);
62     m_sqliteTransaction->begin();
63
64     if (m_sqliteTransaction->inProgress())
65         return IDBError { };
66
67     return IDBError { UnknownError, "Could not start SQLite transaction in database backend"_s };
68 }
69
70 IDBError SQLiteIDBTransaction::commit()
71 {
72     LOG(IndexedDB, "SQLiteIDBTransaction::commit");
73     if (!m_sqliteTransaction || !m_sqliteTransaction->inProgress())
74         return IDBError { UnknownError, "No SQLite transaction in progress to commit"_s };
75
76     m_sqliteTransaction->commit();
77
78     if (m_sqliteTransaction->inProgress())
79         return IDBError { UnknownError, "Unable to commit SQLite transaction in database backend"_s };
80
81     deleteBlobFilesIfNecessary();
82     moveBlobFilesIfNecessary();
83
84     reset();
85     return IDBError { };
86 }
87
88 void SQLiteIDBTransaction::moveBlobFilesIfNecessary()
89 {
90     String databaseDirectory = m_backingStore.databaseDirectory();
91     for (auto& entry : m_blobTemporaryAndStoredFilenames) {
92         if (!FileSystem::hardLinkOrCopyFile(entry.first, FileSystem::pathByAppendingComponent(databaseDirectory, entry.second)))
93             LOG_ERROR("Failed to link/copy temporary blob file '%s' to location '%s'", entry.first.utf8().data(), FileSystem::pathByAppendingComponent(databaseDirectory, entry.second).utf8().data());
94
95         m_backingStore.temporaryFileHandler().accessToTemporaryFileComplete(entry.first);
96     }
97
98     m_blobTemporaryAndStoredFilenames.clear();
99 }
100
101 void SQLiteIDBTransaction::deleteBlobFilesIfNecessary()
102 {
103     if (m_blobRemovedFilenames.isEmpty())
104         return;
105
106     String databaseDirectory = m_backingStore.databaseDirectory();
107     for (auto& entry : m_blobRemovedFilenames) {
108         String fullPath = FileSystem::pathByAppendingComponent(databaseDirectory, entry);
109         m_backingStore.temporaryFileHandler().accessToTemporaryFileComplete(fullPath);
110     }
111
112     m_blobRemovedFilenames.clear();
113 }
114
115 IDBError SQLiteIDBTransaction::abort()
116 {
117     for (auto& entry : m_blobTemporaryAndStoredFilenames)
118         m_backingStore.temporaryFileHandler().accessToTemporaryFileComplete(entry.first);
119
120     m_blobTemporaryAndStoredFilenames.clear();
121
122     if (!m_sqliteTransaction || !m_sqliteTransaction->inProgress())
123         return IDBError { UnknownError, "No SQLite transaction in progress to abort"_s };
124
125     m_sqliteTransaction->rollback();
126
127     if (m_sqliteTransaction->inProgress())
128         return IDBError { UnknownError, "Unable to abort SQLite transaction in database backend"_s };
129
130     reset();
131     return IDBError { };
132 }
133
134 void SQLiteIDBTransaction::reset()
135 {
136     m_sqliteTransaction = nullptr;
137     clearCursors();
138     ASSERT(m_blobTemporaryAndStoredFilenames.isEmpty());
139 }
140
141 std::unique_ptr<SQLiteIDBCursor> SQLiteIDBTransaction::maybeOpenBackingStoreCursor(uint64_t objectStoreID, uint64_t indexID, const IDBKeyRangeData& range)
142 {
143     ASSERT(m_sqliteTransaction);
144     ASSERT(m_sqliteTransaction->inProgress());
145
146     auto cursor = SQLiteIDBCursor::maybeCreateBackingStoreCursor(*this, objectStoreID, indexID, range);
147
148     if (cursor)
149         m_backingStoreCursors.add(cursor.get());
150
151     return cursor;
152 }
153
154 SQLiteIDBCursor* SQLiteIDBTransaction::maybeOpenCursor(const IDBCursorInfo& info)
155 {
156     ASSERT(m_sqliteTransaction);
157     if (!m_sqliteTransaction->inProgress())
158         return nullptr;
159
160     auto addResult = m_cursors.add(info.identifier(), SQLiteIDBCursor::maybeCreate(*this, info));
161
162     ASSERT(addResult.isNewEntry);
163
164     // It is possible the cursor failed to create and we just stored a null value.
165     if (!addResult.iterator->value) {
166         m_cursors.remove(addResult.iterator);
167         return nullptr;
168     }
169
170     return addResult.iterator->value.get();
171 }
172
173 void SQLiteIDBTransaction::closeCursor(SQLiteIDBCursor& cursor)
174 {
175     auto backingStoreTake = m_backingStoreCursors.take(&cursor);
176     if (backingStoreTake) {
177         ASSERT(!m_cursors.contains(cursor.identifier()));
178         return;
179     }
180
181     ASSERT(m_cursors.contains(cursor.identifier()));
182
183     m_backingStore.unregisterCursor(cursor);
184     m_cursors.remove(cursor.identifier());
185 }
186
187 void SQLiteIDBTransaction::notifyCursorsOfChanges(int64_t objectStoreID)
188 {
189     for (auto& i : m_cursors) {
190         if (i.value->objectStoreID() == objectStoreID)
191             i.value->objectStoreRecordsChanged();
192     }
193
194     for (auto* cursor : m_backingStoreCursors) {
195         if (cursor->objectStoreID() == objectStoreID)
196             cursor->objectStoreRecordsChanged();
197     }
198 }
199
200 void SQLiteIDBTransaction::clearCursors()
201 {
202     for (auto& cursor : m_cursors.values())
203         m_backingStore.unregisterCursor(*cursor);
204
205     m_cursors.clear();
206 }
207
208 bool SQLiteIDBTransaction::inProgress() const
209 {
210     return m_sqliteTransaction && m_sqliteTransaction->inProgress();
211 }
212
213 void SQLiteIDBTransaction::addBlobFile(const String& temporaryPath, const String& storedFilename)
214 {
215     m_blobTemporaryAndStoredFilenames.append({ temporaryPath, storedFilename });
216 }
217
218 void SQLiteIDBTransaction::addRemovedBlobFile(const String& removedFilename)
219 {
220     ASSERT(!m_blobRemovedFilenames.contains(removedFilename));
221     m_blobRemovedFilenames.add(removedFilename);
222 }
223
224
225 } // namespace IDBServer
226 } // namespace WebCore
227
228 #endif // ENABLE(INDEXED_DATABASE)