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