Unreviewed test gardening.
[WebKit-https.git] / Source / WebKit2 / DatabaseProcess / IndexedDB / sqlite / SQLiteIDBCursor.cpp
1 /*
2  * Copyright (C) 2014 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 "SQLiteIDBCursor.h"
27
28 #if ENABLE(INDEXED_DATABASE) && ENABLE(DATABASE_PROCESS)
29
30 #include "IDBSerialization.h"
31 #include "Logging.h"
32 #include "SQLiteIDBTransaction.h"
33 #include <WebCore/SQLiteStatement.h>
34 #include <WebCore/SQLiteTransaction.h>
35 #include <sqlite3.h>
36
37 using namespace WebCore;
38
39 namespace WebKit {
40
41 std::unique_ptr<SQLiteIDBCursor> SQLiteIDBCursor::maybeCreate(SQLiteIDBTransaction* transaction, const IDBIdentifier& cursorIdentifier, int64_t objectStoreID, int64_t indexID, IndexedDB::CursorDirection cursorDirection, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, const IDBKeyRangeData& keyRange)
42 {
43     auto cursor = std::unique_ptr<SQLiteIDBCursor>(new SQLiteIDBCursor(transaction, cursorIdentifier, objectStoreID, indexID, cursorDirection, cursorType, taskType, keyRange));
44
45     if (!cursor->establishStatement())
46         return nullptr;
47
48     if (!cursor->advance(1))
49         return nullptr;
50
51     return cursor;
52 }
53
54 SQLiteIDBCursor::SQLiteIDBCursor(SQLiteIDBTransaction* transaction, const IDBIdentifier& cursorIdentifier, int64_t objectStoreID, int64_t indexID, IndexedDB::CursorDirection cursorDirection, IndexedDB::CursorType, IDBDatabaseBackend::TaskType, const IDBKeyRangeData& keyRange)
55     : m_transaction(transaction)
56     , m_cursorIdentifier(cursorIdentifier)
57     , m_objectStoreID(objectStoreID)
58     , m_indexID(indexID)
59     , m_cursorDirection(cursorDirection)
60     , m_keyRange(keyRange)
61     , m_currentRecordID(-1)
62     , m_statementNeedsReset(false)
63     , m_boundID(0)
64     , m_completed(false)
65     , m_errored(false)
66 {
67     ASSERT(m_objectStoreID);
68 }
69
70 static String buildIndexStatement(const IDBKeyRangeData& keyRange, IndexedDB::CursorDirection cursorDirection)
71 {
72     StringBuilder builder;
73
74     builder.appendLiteral("SELECT rowid, key, value FROM IndexRecords WHERE indexID = ? AND key ");
75     if (!keyRange.lowerKey.isNull() && !keyRange.lowerOpen)
76         builder.appendLiteral(">=");
77     else
78         builder.append('>');
79
80     builder.appendLiteral(" CAST(? AS TEXT) AND key ");
81     if (!keyRange.upperKey.isNull() && !keyRange.upperOpen)
82         builder.appendLiteral("<=");
83     else
84         builder.append('<');
85
86     builder.appendLiteral(" CAST(? AS TEXT) ORDER BY key");
87     if (cursorDirection == IndexedDB::CursorDirection::Prev || cursorDirection == IndexedDB::CursorDirection::PrevNoDuplicate)
88         builder.appendLiteral(" DESC");
89
90     builder.appendLiteral(", value");
91     if (cursorDirection == IndexedDB::CursorDirection::Prev)
92         builder.appendLiteral(" DESC");
93
94     builder.append(';');
95
96     return builder.toString();
97 }
98
99 static String buildObjectStoreStatement(const IDBKeyRangeData& keyRange, IndexedDB::CursorDirection cursorDirection)
100 {
101     StringBuilder builder;
102
103     builder.appendLiteral("SELECT rowid, key, value FROM Records WHERE objectStoreID = ? AND key ");
104
105     if (!keyRange.lowerKey.isNull() && !keyRange.lowerOpen)
106         builder.appendLiteral(">=");
107     else
108         builder.append('>');
109
110     builder.appendLiteral(" CAST(? AS TEXT) AND key ");
111
112     if (!keyRange.upperKey.isNull() && !keyRange.upperOpen)
113         builder.appendLiteral("<=");
114     else
115         builder.append('<');
116
117     builder.appendLiteral(" CAST(? AS TEXT) ORDER BY key");
118
119     if (cursorDirection == IndexedDB::CursorDirection::Prev || cursorDirection == IndexedDB::CursorDirection::PrevNoDuplicate)
120         builder.appendLiteral(" DESC");
121
122     builder.append(';');
123
124     return builder.toString();
125 }
126
127 bool SQLiteIDBCursor::establishStatement()
128 {
129     ASSERT(!m_statement);
130     String sql;
131
132     if (m_indexID != IDBIndexMetadata::InvalidId) {
133         sql = buildIndexStatement(m_keyRange, m_cursorDirection);
134         m_boundID = m_indexID;
135     } else {
136         sql = buildObjectStoreStatement(m_keyRange, m_cursorDirection);
137         m_boundID = m_objectStoreID;
138     }
139
140     m_currentLowerKey = m_keyRange.lowerKey.isNull() ? IDBKeyData::minimum() : m_keyRange.lowerKey;
141     m_currentUpperKey = m_keyRange.upperKey.isNull() ? IDBKeyData::maximum() : m_keyRange.upperKey;
142
143     return createSQLiteStatement(sql);
144 }
145
146 bool SQLiteIDBCursor::createSQLiteStatement(const String& sql)
147 {
148     LOG(IDB, "Creating cursor with SQL query: \"%s\"", sql.utf8().data());
149
150     ASSERT(!m_currentLowerKey.isNull());
151     ASSERT(!m_currentUpperKey.isNull());
152     ASSERT(m_transaction->sqliteTransaction());
153
154     m_statement = std::make_unique<SQLiteStatement>(m_transaction->sqliteTransaction()->database(), sql);
155
156     if (m_statement->prepare() != SQLITE_OK) {
157         LOG_ERROR("Could not create cursor statement (prepare/id) - '%s'", m_transaction->sqliteTransaction()->database().lastErrorMsg());
158         return false;
159     }
160
161     return bindArguments();
162 }
163
164 void SQLiteIDBCursor::objectStoreRecordsChanged()
165 {
166     // If ObjectStore or Index contents changed, we need to reset the statement and bind new parameters to it.
167     // This is to pick up any changes that might exist.
168
169     m_statementNeedsReset = true;
170 }
171
172 void SQLiteIDBCursor::resetAndRebindStatement()
173 {
174     ASSERT(!m_currentLowerKey.isNull());
175     ASSERT(!m_currentUpperKey.isNull());
176     ASSERT(m_transaction->sqliteTransaction());
177     ASSERT(m_statement);
178     ASSERT(m_statementNeedsReset);
179
180     m_statementNeedsReset = false;
181
182     // If this cursor never fetched any records, we don't need to reset the statement.
183     if (m_currentKey.isNull())
184         return;
185
186     // Otherwise update the lower key or upper key used for the cursor range.
187     // This is so the cursor can pick up where we left off.
188     if (m_cursorDirection == IndexedDB::CursorDirection::Next || m_cursorDirection == IndexedDB::CursorDirection::NextNoDuplicate)
189         m_currentLowerKey = m_currentKey;
190     else
191         m_currentUpperKey = m_currentKey;
192
193     if (m_statement->reset() != SQLITE_OK) {
194         LOG_ERROR("Could not reset cursor statement to respond to object store changes");
195         return;
196     }
197
198     bindArguments();
199 }
200
201 bool SQLiteIDBCursor::bindArguments()
202 {
203     if (m_statement->bindInt64(1, m_boundID) != SQLITE_OK) {
204         LOG_ERROR("Could not bind id argument (bound ID)");
205         return false;
206     }
207
208     RefPtr<SharedBuffer> buffer = serializeIDBKeyData(m_currentLowerKey);
209     if (m_statement->bindBlob(2, buffer->data(), buffer->size()) != SQLITE_OK) {
210         LOG_ERROR("Could not create cursor statement (lower key)");
211         return false;
212     }
213
214     buffer = serializeIDBKeyData(m_currentUpperKey);
215     if (m_statement->bindBlob(3, buffer->data(), buffer->size()) != SQLITE_OK) {
216         LOG_ERROR("Could not create cursor statement (upper key)");
217         return false;
218     }
219
220     return true;
221 }
222
223 bool SQLiteIDBCursor::advance(uint64_t count)
224 {
225     bool isUnique = m_cursorDirection == IndexedDB::CursorDirection::NextNoDuplicate || m_cursorDirection == IndexedDB::CursorDirection::PrevNoDuplicate;
226
227     for (uint64_t i = 0; i < count; ++i) {
228         if (!isUnique) {
229             if (!advanceOnce())
230                 return false;
231         } else {
232             if (!advanceUnique())
233                 return false;
234         }
235     }
236
237     return true;
238 }
239
240 bool SQLiteIDBCursor::advanceUnique()
241 {
242     IDBKeyData currentKey = m_currentKey;
243
244     while (!m_completed) {
245         if (!advanceOnce())
246             return false;
247
248         // If the new current key is different from the old current key, we're done.
249         if (currentKey.compare(m_currentKey))
250             return true;
251     }
252
253     return false;
254 }
255
256 bool SQLiteIDBCursor::advanceOnce()
257 {
258     if (m_statementNeedsReset)
259         resetAndRebindStatement();
260
261     AdvanceResult result;
262     do {
263         result = internalAdvanceOnce();
264     } while (result == AdvanceResult::ShouldAdvanceAgain);
265
266     return result == AdvanceResult::Success;
267 }
268
269 SQLiteIDBCursor::AdvanceResult SQLiteIDBCursor::internalAdvanceOnce()
270 {
271     ASSERT(m_transaction->sqliteTransaction());
272     ASSERT(m_statement);
273
274     if (m_completed) {
275         LOG_ERROR("Attempt to advance a completed cursor");
276         return AdvanceResult::Failure;
277     }
278
279     int result = m_statement->step();
280     if (result == SQLITE_DONE) {
281         m_completed = true;
282
283         // When a cursor reaches its end, that is indicated by having undefined keys/values
284         m_currentKey = IDBKeyData();
285         m_currentPrimaryKey = IDBKeyData();
286         m_currentValueBuffer.clear();
287
288         return AdvanceResult::Success;
289     }
290
291     if (result != SQLITE_ROW) {
292         LOG_ERROR("Error advancing cursor - (%i) %s", result, m_transaction->sqliteTransaction()->database().lastErrorMsg());
293         m_completed = true;
294         m_errored = true;
295         return AdvanceResult::Failure;
296     }
297
298     int64_t recordID = m_statement->getColumnInt64(0);
299
300     // If the recordID of the record just fetched is the same as the current record ID
301     // then this statement must have been re-prepared in response to an object store change.
302     // We don't want to re-use the current record so we'll move on to the next one.
303     if (recordID == m_currentRecordID)
304         return AdvanceResult::ShouldAdvanceAgain;
305
306     m_currentRecordID = recordID;
307
308     Vector<uint8_t> keyData;
309     m_statement->getColumnBlobAsVector(1, keyData);
310
311     if (!deserializeIDBKeyData(keyData.data(), keyData.size(), m_currentKey)) {
312         LOG_ERROR("Unable to deserialize key data from database while advancing cursor");
313         m_completed = true;
314         m_errored = true;
315         return AdvanceResult::Failure;
316     }
317
318     m_statement->getColumnBlobAsVector(2, keyData);
319     m_currentValueBuffer = keyData;
320
321     // The primaryKey of an ObjectStore cursor is the same as its key.
322     if (m_indexID == IDBIndexMetadata::InvalidId)
323         m_currentPrimaryKey = m_currentKey;
324     else {
325         if (!deserializeIDBKeyData(keyData.data(), keyData.size(), m_currentPrimaryKey)) {
326             LOG_ERROR("Unable to deserialize value data from database while advancing index cursor");
327             m_completed = true;
328             m_errored = true;
329             return AdvanceResult::Failure;
330         }
331
332         SQLiteStatement objectStoreStatement(m_statement->database(), "SELECT value FROM Records WHERE key = CAST(? AS TEXT) and objectStoreID = ?;");
333
334         if (objectStoreStatement.prepare() != SQLITE_OK
335             || objectStoreStatement.bindBlob(1, m_currentValueBuffer.data(), m_currentValueBuffer.size()) != SQLITE_OK
336             || objectStoreStatement.bindInt64(2, m_objectStoreID) != SQLITE_OK) {
337             LOG_ERROR("Could not create index cursor statement into object store records (%i) '%s'", m_statement->database().lastError(), m_statement->database().lastErrorMsg());
338             m_completed = true;
339             m_errored = true;
340             return AdvanceResult::Failure;
341         }
342
343         int result = objectStoreStatement.step();
344
345         if (result == SQLITE_ROW)
346             objectStoreStatement.getColumnBlobAsVector(0, m_currentValueBuffer);
347         else if (result == SQLITE_DONE) {
348             // This indicates that the record we're trying to retrieve has been removed from the object store.
349             // Skip over it.
350             return AdvanceResult::ShouldAdvanceAgain;
351         } else {
352             LOG_ERROR("Could not step index cursor statement into object store records (%i) '%s'", m_statement->database().lastError(), m_statement->database().lastErrorMsg());
353             m_completed = true;
354             m_errored = true;
355             return AdvanceResult::Failure;
356
357         }
358     }
359
360     return AdvanceResult::Success;
361 }
362
363 bool SQLiteIDBCursor::iterate(const WebCore::IDBKeyData& targetKey)
364 {
365     ASSERT(m_transaction->sqliteTransaction());
366     ASSERT(m_statement);
367
368     bool result = advance(1);
369
370     // Iterating with no key is equivalent to advancing 1 step.
371     if (targetKey.isNull() || !result)
372         return result;
373
374     while (!m_completed) {
375         if (!result)
376             return false;
377
378         // Search for the next key >= the target if the cursor is a Next cursor, or the next key <= if the cursor is a Previous cursor.
379         if (m_cursorDirection == IndexedDB::CursorDirection::Next || m_cursorDirection == IndexedDB::CursorDirection::NextNoDuplicate) {
380             if (m_currentKey.compare(targetKey) >= 0)
381                 break;
382         } else if (m_currentKey.compare(targetKey) <= 0)
383             break;
384
385         result = advance(1);
386     }
387
388     return result;
389 }
390
391 } // namespace WebKit
392
393 #endif // ENABLE(INDEXED_DATABASE) && ENABLE(DATABASE_PROCESS)