IDB: Index cursor complete advance() and iterate() support
[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_completed(false)
62 {
63     ASSERT(m_objectStoreID);
64 }
65
66 static const String& getIndexStatement(bool lowerKey, bool lowerOpen, bool upperKey, bool upperOpen, bool descending)
67 {
68     DEFINE_STATIC_LOCAL(Vector<String>, indexStatements, ());
69
70     if (indexStatements.isEmpty()) {
71         indexStatements.reserveCapacity(18);
72
73         // No lower key statements (6)
74         indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? ORDER BY key;"));
75         indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? ORDER BY key DESC;"));
76         indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key < CAST(? AS TEXT) ORDER BY key;"));
77         indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key < CAST(? AS TEXT) ORDER BY key DESC;"));
78         indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key <= CAST(? AS TEXT) ORDER BY key;"));
79         indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key <= CAST(? AS TEXT) ORDER BY key DESC;"));
80
81         // Closed lower key statements (6)
82         indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key >= CAST(? AS TEXT) ORDER BY key;"));
83         indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key >= CAST(? AS TEXT) ORDER BY key DESC;"));
84         indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;"));
85         indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key DESC;"));
86         indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;"));
87         indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key DESC;"));
88
89         // Open lower key statements (6)
90         indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key > CAST(? AS TEXT) ORDER BY key;"));
91         indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key > CAST(? AS TEXT) ORDER BY key DESC;"));
92         indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;"));
93         indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key DESC;"));
94         indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;"));
95         indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key DESC;"));
96     }
97
98     size_t i = 0;
99     if (lowerKey)
100         i += 6;
101     if (lowerOpen)
102         i += 6;
103     if (upperKey)
104         i += 2;
105     if (upperOpen)
106         i += 2;
107     if (descending)
108         i += 1;
109
110     return indexStatements[i];
111 }
112
113 static const String& getObjectStoreStatement(bool lowerKey, bool lowerOpen, bool upperKey, bool upperOpen, bool descending)
114 {
115     DEFINE_STATIC_LOCAL(Vector<String>, indexStatements, ());
116
117     if (indexStatements.isEmpty()) {
118         indexStatements.reserveCapacity(18);
119
120         // No lower key statements (6)
121         indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? ORDER BY key;"));
122         indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? ORDER BY key DESC;"));
123         indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key <= CAST(? AS TEXT) ORDER BY key;"));
124         indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key <= CAST(? AS TEXT) ORDER BY key DESC;"));
125         indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key < CAST(? AS TEXT) ORDER BY key;"));
126         indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key < CAST(? AS TEXT) ORDER BY key DESC;"));
127
128         // Closed lower key statements (6)
129         indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) ORDER BY key;"));
130         indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) ORDER BY key DESC;"));
131         indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;"));
132         indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key DESC;"));
133         indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;"));
134         indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key DESC;"));
135
136         // Open lower key statements (6)
137         indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) ORDER BY key;"));
138         indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) ORDER BY key DESC;"));
139         indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;"));
140         indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key DESC;"));
141         indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;"));
142         indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key DESC;"));
143     }
144
145     size_t i = 0;
146     if (lowerKey)
147         i += 6;
148     if (lowerOpen)
149         i += 6;
150     if (upperKey)
151         i += 2;
152     if (upperOpen)
153         i += 2;
154     if (descending)
155         i += 1;
156
157     return indexStatements[i];
158 }
159
160 bool SQLiteIDBCursor::establishStatement()
161 {
162     String sql;
163     int64_t id;
164
165     if (m_indexID != IDBIndexMetadata::InvalidId) {
166         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);
167         id = m_indexID;
168     } else {
169         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);
170         id = m_objectStoreID;
171     }
172
173     return createSQLiteStatement(sql, id);
174 }
175
176 bool SQLiteIDBCursor::createSQLiteStatement(const String& sql, int64_t idToBind)
177 {
178     ASSERT(m_transaction->sqliteTransaction());
179     SQLiteDatabase& database = m_transaction->sqliteTransaction()->database();
180
181     LOG(IDB, "Creating cursor with SQL query: \"%s\"", sql.utf8().data());
182
183     m_statement = std::make_unique<SQLiteStatement>(database, sql);
184
185     if (m_statement->prepare() != SQLResultOk
186         || m_statement->bindInt64(1, idToBind) != SQLResultOk) {
187         LOG_ERROR("Could not create cursor statement");
188         return false;
189     }
190
191     int nextBindArgument = 2;
192
193     if (!m_keyRange.lowerKey.isNull) {
194         RefPtr<SharedBuffer> buffer = serializeIDBKeyData(m_keyRange.lowerKey);
195         if (m_statement->bindBlob(nextBindArgument++, buffer->data(), buffer->size()) != SQLResultOk) {
196             LOG_ERROR("Could not create cursor statement");
197             return false;
198         }
199     }
200     if (!m_keyRange.upperKey.isNull) {
201         RefPtr<SharedBuffer> buffer = serializeIDBKeyData(m_keyRange.upperKey);
202         if (m_statement->bindBlob(nextBindArgument, buffer->data(), buffer->size()) != SQLResultOk) {
203             LOG_ERROR("Could not create cursor statement");
204             return false;
205         }
206     }
207
208     return true;
209 }
210
211 bool SQLiteIDBCursor::advance(uint64_t count)
212 {
213     bool isUnique = m_cursorDirection == IndexedDB::CursorDirection::NextNoDuplicate || m_cursorDirection == IndexedDB::CursorDirection::PrevNoDuplicate;
214
215     for (uint64_t i = 0; i < count; ++i) {
216         if (!isUnique) {
217             if (!advanceOnce())
218                 return false;
219             continue;
220         }
221
222         if (!advanceUnique())
223             return false;
224     }
225
226     return true;
227 }
228
229 bool SQLiteIDBCursor::advanceUnique()
230 {
231     IDBKeyData currentKey = m_currentKey;
232
233     while (!m_completed) {
234         if (!advanceOnce())
235             return false;
236
237         // If the new current key is different from the old current key, we're done.
238         if (currentKey.compare(m_currentKey))
239             return true;
240     }
241
242     return false;
243 }
244
245
246 bool SQLiteIDBCursor::advanceOnce()
247 {
248     ASSERT(m_transaction->sqliteTransaction());
249     ASSERT(m_statement);
250
251     if (m_completed) {
252         LOG_ERROR("Attempt to advance a completed cursor");
253         return false;
254     }
255
256     int result = m_statement->step();
257     if (result == SQLResultDone) {
258         m_completed = true;
259         return true;
260     }
261
262     if (result != SQLResultRow) {
263         LOG_ERROR("Error advancing cursor - (%i) %s", result, m_transaction->sqliteTransaction()->database().lastErrorMsg());
264         m_completed = true;
265         return false;
266     }
267
268     Vector<char> keyData;
269     m_statement->getColumnBlobAsVector(0, keyData);
270
271     IDBKeyData key;
272     if (!deserializeIDBKeyData(reinterpret_cast<const uint8_t*>(keyData.data()), keyData.size(), key)) {
273         LOG_ERROR("Unable to deserialize key data from database while advancing cursor");
274         m_completed = true;
275         return false;
276     }
277
278     m_statement->getColumnBlobAsVector(1, keyData);
279
280     m_currentKey = key;
281     m_currentPrimaryKey = key;
282     m_currentValueBuffer = keyData;
283
284     if (m_indexID != IDBIndexMetadata::InvalidId) {
285         if (!deserializeIDBKeyData(reinterpret_cast<const uint8_t*>(keyData.data()), keyData.size(), m_currentValueKey)) {
286             LOG_ERROR("Unable to deserialize value data from database while advancing index cursor");
287             m_completed = true;
288             return false;
289         }
290
291         SQLiteStatement objectStoreStatement(*m_statement->database(), "SELECT value FROM Records WHERE key = CAST(? AS TEXT) and objectStoreID = ?;");
292
293         if (objectStoreStatement.prepare() != SQLResultOk
294             || objectStoreStatement.bindBlob(1, m_currentValueBuffer.data(), m_currentValueBuffer.size()) != SQLResultOk
295             || objectStoreStatement.bindInt64(2, m_objectStoreID) != SQLResultOk
296             || objectStoreStatement.step() != SQLResultRow) {
297             LOG_ERROR("Could not create index cursor statement into object store records");
298             return false;
299         }
300
301         objectStoreStatement.getColumnBlobAsVector(0, m_currentValueBuffer);
302     }
303
304     return true;
305 }
306
307 bool SQLiteIDBCursor::iterate(const WebCore::IDBKeyData& targetKey)
308 {
309     ASSERT(m_transaction->sqliteTransaction());
310     ASSERT(m_statement);
311
312     bool result = advance(1);
313
314     // Iterating with no key is equivalent to advancing 1 step.
315     if (targetKey.isNull || !result)
316         return result;
317
318     while (!m_completed && m_currentKey.compare(targetKey)) {
319         result = advance(1);
320         if (!result)
321             return false;
322     }
323
324     return result;
325 }
326
327 } // namespace WebKit
328
329 #endif // ENABLE(INDEXED_DATABASE) && ENABLE(DATABASE_PROCESS)