Remove IDBBackingStoreTemporaryFileHandler
[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         FileSystem::deleteFile(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
110         FileSystem::deleteFile(fullPath);
111     }
112
113     m_blobRemovedFilenames.clear();
114 }
115
116 IDBError SQLiteIDBTransaction::abort()
117 {
118     for (auto& entry : m_blobTemporaryAndStoredFilenames)
119         FileSystem::deleteFile(entry.first);
120
121     m_blobTemporaryAndStoredFilenames.clear();
122
123     if (!m_sqliteTransaction || !m_sqliteTransaction->inProgress())
124         return IDBError { UnknownError, "No SQLite transaction in progress to abort"_s };
125
126     m_sqliteTransaction->rollback();
127
128     if (m_sqliteTransaction->inProgress())
129         return IDBError { UnknownError, "Unable to abort SQLite transaction in database backend"_s };
130
131     reset();
132     return IDBError { };
133 }
134
135 void SQLiteIDBTransaction::reset()
136 {
137     m_sqliteTransaction = nullptr;
138     clearCursors();
139     ASSERT(m_blobTemporaryAndStoredFilenames.isEmpty());
140 }
141
142 std::unique_ptr<SQLiteIDBCursor> SQLiteIDBTransaction::maybeOpenBackingStoreCursor(uint64_t objectStoreID, uint64_t indexID, const IDBKeyRangeData& range)
143 {
144     ASSERT(m_sqliteTransaction);
145     ASSERT(m_sqliteTransaction->inProgress());
146
147     auto cursor = SQLiteIDBCursor::maybeCreateBackingStoreCursor(*this, objectStoreID, indexID, range);
148
149     if (cursor)
150         m_backingStoreCursors.add(cursor.get());
151
152     return cursor;
153 }
154
155 SQLiteIDBCursor* SQLiteIDBTransaction::maybeOpenCursor(const IDBCursorInfo& info)
156 {
157     ASSERT(m_sqliteTransaction);
158     if (!m_sqliteTransaction->inProgress())
159         return nullptr;
160
161     auto addResult = m_cursors.add(info.identifier(), SQLiteIDBCursor::maybeCreate(*this, info));
162
163     ASSERT(addResult.isNewEntry);
164
165     // It is possible the cursor failed to create and we just stored a null value.
166     if (!addResult.iterator->value) {
167         m_cursors.remove(addResult.iterator);
168         return nullptr;
169     }
170
171     return addResult.iterator->value.get();
172 }
173
174 void SQLiteIDBTransaction::closeCursor(SQLiteIDBCursor& cursor)
175 {
176     auto backingStoreTake = m_backingStoreCursors.take(&cursor);
177     if (backingStoreTake) {
178         ASSERT(!m_cursors.contains(cursor.identifier()));
179         return;
180     }
181
182     ASSERT(m_cursors.contains(cursor.identifier()));
183
184     m_backingStore.unregisterCursor(cursor);
185     m_cursors.remove(cursor.identifier());
186 }
187
188 void SQLiteIDBTransaction::notifyCursorsOfChanges(int64_t objectStoreID)
189 {
190     for (auto& i : m_cursors) {
191         if (i.value->objectStoreID() == objectStoreID)
192             i.value->objectStoreRecordsChanged();
193     }
194
195     for (auto* cursor : m_backingStoreCursors) {
196         if (cursor->objectStoreID() == objectStoreID)
197             cursor->objectStoreRecordsChanged();
198     }
199 }
200
201 void SQLiteIDBTransaction::clearCursors()
202 {
203     for (auto& cursor : m_cursors.values())
204         m_backingStore.unregisterCursor(*cursor);
205
206     m_cursors.clear();
207 }
208
209 bool SQLiteIDBTransaction::inProgress() const
210 {
211     return m_sqliteTransaction && m_sqliteTransaction->inProgress();
212 }
213
214 void SQLiteIDBTransaction::addBlobFile(const String& temporaryPath, const String& storedFilename)
215 {
216     m_blobTemporaryAndStoredFilenames.append({ temporaryPath, storedFilename });
217 }
218
219 void SQLiteIDBTransaction::addRemovedBlobFile(const String& removedFilename)
220 {
221     ASSERT(!m_blobRemovedFilenames.contains(removedFilename));
222     m_blobRemovedFilenames.add(removedFilename);
223 }
224
225
226 } // namespace IDBServer
227 } // namespace WebCore
228
229 #endif // ENABLE(INDEXED_DATABASE)