1/*
2 * Copyright (C) 2014, 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
26#include "config.h"
27#include "SQLiteIDBCursor.h"
28
29#if ENABLE(INDEXED_DATABASE)
30
31#include "IDBCursorInfo.h"
32#include "IDBGetResult.h"
33#include "IDBSerialization.h"
34#include "Logging.h"
35#include "SQLiteIDBBackingStore.h"
36#include "SQLiteIDBTransaction.h"
37#include "SQLiteStatement.h"
38#include "SQLiteTransaction.h"
39#include <sqlite3.h>
40#include <wtf/text/StringBuilder.h>
41
42namespace WebCore {
43namespace IDBServer {
44
45static const size_t prefetchLimit = 8;
46
47std::unique_ptr<SQLiteIDBCursor> SQLiteIDBCursor::maybeCreate(SQLiteIDBTransaction& transaction, const IDBCursorInfo& info)
48{
49 auto cursor = std::make_unique<SQLiteIDBCursor>(transaction, info);
50
51 if (!cursor->establishStatement())
52 return nullptr;
53
54 if (!cursor->advance(1))
55 return nullptr;
56
57 return cursor;
58}
59
60std::unique_ptr<SQLiteIDBCursor> SQLiteIDBCursor::maybeCreateBackingStoreCursor(SQLiteIDBTransaction& transaction, const uint64_t objectStoreID, const uint64_t indexID, const IDBKeyRangeData& range)
61{
62 auto cursor = std::make_unique<SQLiteIDBCursor>(transaction, objectStoreID, indexID, range);
63
64 if (!cursor->establishStatement())
65 return nullptr;
66
67 if (!cursor->advance(1))
68 return nullptr;
69
70 return cursor;
71}
72
73SQLiteIDBCursor::SQLiteIDBCursor(SQLiteIDBTransaction& transaction, const IDBCursorInfo& info)
74 : m_transaction(&transaction)
75 , m_cursorIdentifier(info.identifier())
76 , m_objectStoreID(info.objectStoreIdentifier())
77 , m_indexID(info.cursorSource() == IndexedDB::CursorSource::Index ? info.sourceIdentifier() : IDBIndexInfo::InvalidId)
78 , m_cursorDirection(info.cursorDirection())
79 , m_cursorType(info.cursorType())
80 , m_keyRange(info.range())
81{
82 ASSERT(m_objectStoreID);
83}
84
85SQLiteIDBCursor::SQLiteIDBCursor(SQLiteIDBTransaction& transaction, const uint64_t objectStoreID, const uint64_t indexID, const IDBKeyRangeData& range)
86 : m_transaction(&transaction)
87 , m_cursorIdentifier(transaction.transactionIdentifier())
88 , m_objectStoreID(objectStoreID)
89 , m_indexID(indexID ? indexID : IDBIndexInfo::InvalidId)
90 , m_cursorDirection(IndexedDB::CursorDirection::Next)
91 , m_cursorType(IndexedDB::CursorType::KeyAndValue)
92 , m_keyRange(range)
93 , m_backingStoreCursor(true)
94{
95 ASSERT(m_objectStoreID);
96}
97
98SQLiteIDBCursor::~SQLiteIDBCursor()
99{
100 if (m_backingStoreCursor)
101 m_transaction->closeCursor(*this);
102}
103
104void SQLiteIDBCursor::currentData(IDBGetResult& result, const Optional<IDBKeyPath>& keyPath)
105{
106 ASSERT(!m_fetchedRecords.isEmpty());
107
108 auto& currentRecord = m_fetchedRecords.first();
109 if (currentRecord.completed) {
110 ASSERT(!currentRecord.errored);
111 result = { };
112 return;
113 }
114
115 result = { currentRecord.record.key, currentRecord.record.primaryKey, currentRecord.record.value ? *currentRecord.record.value : IDBValue(), keyPath};
116}
117
118static String buildIndexStatement(const IDBKeyRangeData& keyRange, IndexedDB::CursorDirection cursorDirection)
119{
120 StringBuilder builder;
121
122 builder.appendLiteral("SELECT rowid, key, value FROM IndexRecords WHERE indexID = ? AND objectStoreID = ? AND key ");
123 if (!keyRange.lowerKey.isNull() && !keyRange.lowerOpen)
124 builder.appendLiteral(">=");
125 else
126 builder.append('>');
127
128 builder.appendLiteral(" CAST(? AS TEXT) AND key ");
129 if (!keyRange.upperKey.isNull() && !keyRange.upperOpen)
130 builder.appendLiteral("<=");
131 else
132 builder.append('<');
133
134 builder.appendLiteral(" CAST(? AS TEXT) ORDER BY key");
135 if (cursorDirection == IndexedDB::CursorDirection::Prev || cursorDirection == IndexedDB::CursorDirection::Prevunique)
136 builder.appendLiteral(" DESC");
137
138 builder.appendLiteral(", value");
139 if (cursorDirection == IndexedDB::CursorDirection::Prev)
140 builder.appendLiteral(" DESC");
141
142 builder.append(';');
143
144 return builder.toString();
145}
146
147static String buildObjectStoreStatement(const IDBKeyRangeData& keyRange, IndexedDB::CursorDirection cursorDirection)
148{
149 StringBuilder builder;
150
151 builder.appendLiteral("SELECT rowid, key, value FROM Records WHERE objectStoreID = ? AND key ");
152
153 if (!keyRange.lowerKey.isNull() && !keyRange.lowerOpen)
154 builder.appendLiteral(">=");
155 else
156 builder.append('>');
157
158 builder.appendLiteral(" CAST(? AS TEXT) AND key ");
159
160 if (!keyRange.upperKey.isNull() && !keyRange.upperOpen)
161 builder.appendLiteral("<=");
162 else
163 builder.append('<');
164
165 builder.appendLiteral(" CAST(? AS TEXT) ORDER BY key");
166
167 if (cursorDirection == IndexedDB::CursorDirection::Prev || cursorDirection == IndexedDB::CursorDirection::Prevunique)
168 builder.appendLiteral(" DESC");
169
170 builder.append(';');
171
172 return builder.toString();
173}
174
175bool SQLiteIDBCursor::establishStatement()
176{
177 ASSERT(!m_statement);
178 String sql;
179
180 if (m_indexID != IDBIndexInfo::InvalidId) {
181 sql = buildIndexStatement(m_keyRange, m_cursorDirection);
182 m_boundID = m_indexID;
183 } else {
184 sql = buildObjectStoreStatement(m_keyRange, m_cursorDirection);
185 m_boundID = m_objectStoreID;
186 }
187
188 m_currentLowerKey = m_keyRange.lowerKey.isNull() ? IDBKeyData::minimum() : m_keyRange.lowerKey;
189 m_currentUpperKey = m_keyRange.upperKey.isNull() ? IDBKeyData::maximum() : m_keyRange.upperKey;
190
191 return createSQLiteStatement(sql);
192}
193
194bool SQLiteIDBCursor::createSQLiteStatement(const String& sql)
195{
196 LOG(IndexedDB, "Creating cursor with SQL query: \"%s\"", sql.utf8().data());
197
198 ASSERT(!m_currentLowerKey.isNull());
199 ASSERT(!m_currentUpperKey.isNull());
200 ASSERT(m_transaction->sqliteTransaction());
201
202 m_statement = std::make_unique<SQLiteStatement>(m_transaction->sqliteTransaction()->database(), sql);
203
204 if (m_statement->prepare() != SQLITE_OK) {
205 LOG_ERROR("Could not create cursor statement (prepare/id) - '%s'", m_transaction->sqliteTransaction()->database().lastErrorMsg());
206 return false;
207 }
208
209 return bindArguments();
210}
211
212void SQLiteIDBCursor::objectStoreRecordsChanged()
213{
214 if (m_statementNeedsReset)
215 return;
216
217 ASSERT(!m_fetchedRecords.isEmpty());
218
219 m_currentKeyForUniqueness = m_fetchedRecords.first().record.key;
220
221 if (m_cursorDirection != IndexedDB::CursorDirection::Nextunique && m_cursorDirection != IndexedDB::CursorDirection::Prevunique) {
222 if (!m_fetchedRecords.last().isTerminalRecord())
223 fetch(ShouldFetchForSameKey::Yes);
224
225 while (m_fetchedRecords.last().record.key != m_fetchedRecords.first().record.key)
226 m_fetchedRecords.removeLast();
227 } else
228 m_fetchedRecords.clear();
229
230 // If ObjectStore or Index contents changed, we need to reset the statement and bind new parameters to it.
231 // This is to pick up any changes that might exist.
232 // We also need to throw away any fetched records as they may no longer be valid.
233
234 m_statementNeedsReset = true;
235
236 if (m_cursorDirection == IndexedDB::CursorDirection::Next || m_cursorDirection == IndexedDB::CursorDirection::Nextunique) {
237 m_currentLowerKey = m_currentKeyForUniqueness;
238 if (!m_keyRange.lowerOpen) {
239 m_keyRange.lowerOpen = true;
240 m_keyRange.lowerKey = m_currentLowerKey;
241 m_statement = nullptr;
242 }
243 } else {
244 m_currentUpperKey = m_currentKeyForUniqueness;
245 if (!m_keyRange.upperOpen) {
246 m_keyRange.upperOpen = true;
247 m_keyRange.upperKey = m_currentUpperKey;
248 m_statement = nullptr;
249 }
250 }
251}
252
253void SQLiteIDBCursor::resetAndRebindStatement()
254{
255 ASSERT(!m_currentLowerKey.isNull());
256 ASSERT(!m_currentUpperKey.isNull());
257 ASSERT(m_transaction->sqliteTransaction());
258 ASSERT(m_statementNeedsReset);
259
260 m_statementNeedsReset = false;
261
262 if (!m_statement && !establishStatement()) {
263 LOG_ERROR("Unable to establish new statement for cursor iteration");
264 return;
265 }
266
267 if (m_statement->reset() != SQLITE_OK) {
268 LOG_ERROR("Could not reset cursor statement to respond to object store changes");
269 return;
270 }
271
272 bindArguments();
273}
274
275bool SQLiteIDBCursor::bindArguments()
276{
277 LOG(IndexedDB, "Cursor is binding lower key '%s' and upper key '%s'", m_currentLowerKey.loggingString().utf8().data(), m_currentUpperKey.loggingString().utf8().data());
278
279 int currentBindArgument = 1;
280
281 if (m_statement->bindInt64(currentBindArgument++, m_boundID) != SQLITE_OK) {
282 LOG_ERROR("Could not bind id argument (bound ID)");
283 return false;
284 }
285
286 if (m_indexID != IDBIndexInfo::InvalidId && m_statement->bindInt64(currentBindArgument++, m_objectStoreID) != SQLITE_OK) {
287 LOG_ERROR("Could not bind object store id argument for an index cursor");
288 return false;
289 }
290
291 RefPtr<SharedBuffer> buffer = serializeIDBKeyData(m_currentLowerKey);
292 if (m_statement->bindBlob(currentBindArgument++, buffer->data(), buffer->size()) != SQLITE_OK) {
293 LOG_ERROR("Could not create cursor statement (lower key)");
294 return false;
295 }
296
297 buffer = serializeIDBKeyData(m_currentUpperKey);
298 if (m_statement->bindBlob(currentBindArgument++, buffer->data(), buffer->size()) != SQLITE_OK) {
299 LOG_ERROR("Could not create cursor statement (upper key)");
300 return false;
301 }
302
303 return true;
304}
305
306bool SQLiteIDBCursor::prefetch()
307{
308 LOG(IndexedDB, "SQLiteIDBCursor::prefetch() - Cursor already has %zu fetched records", m_fetchedRecords.size());
309
310 if (m_fetchedRecords.isEmpty() || m_fetchedRecords.size() >= prefetchLimit || m_fetchedRecords.last().isTerminalRecord())
311 return false;
312
313 m_currentKeyForUniqueness = m_fetchedRecords.last().record.key;
314 fetch();
315
316 return m_fetchedRecords.size() < prefetchLimit;
317}
318
319bool SQLiteIDBCursor::advance(uint64_t count)
320{
321 LOG(IndexedDB, "SQLiteIDBCursor::advance() - Count %" PRIu64 ", %zu fetched records", count, m_fetchedRecords.size());
322 ASSERT(count);
323
324 if (!m_fetchedRecords.isEmpty() && m_fetchedRecords.first().isTerminalRecord()) {
325 LOG_ERROR("Attempt to advance a completed cursor");
326 return false;
327 }
328
329 if (!m_fetchedRecords.isEmpty())
330 m_currentKeyForUniqueness = m_fetchedRecords.last().record.key;
331
332 // Drop already-fetched records up to `count` to see if we've already fetched the record we're looking for.
333 bool hadCurrentRecord = !m_fetchedRecords.isEmpty();
334 for (; count && !m_fetchedRecords.isEmpty(); --count) {
335 if (m_fetchedRecords.first().isTerminalRecord())
336 break;
337
338 m_fetchedRecords.removeFirst();
339 }
340
341 // If we still have any records left, the first record is our new current record.
342 if (!m_fetchedRecords.isEmpty())
343 return true;
344
345 ASSERT(m_fetchedRecords.isEmpty());
346
347 // If we started out with a current record, we burnt a count on removing it.
348 // Replace that count now.
349 if (hadCurrentRecord)
350 ++count;
351
352 for (; count; --count) {
353 if (!m_fetchedRecords.isEmpty()) {
354 ASSERT(m_fetchedRecords.size() == 1);
355 m_currentKeyForUniqueness = m_fetchedRecords.first().record.key;
356 m_fetchedRecords.removeFirst();
357 }
358
359 if (!fetch())
360 return false;
361
362 ASSERT(!m_fetchedRecords.isEmpty());
363 ASSERT(!m_fetchedRecords.first().errored);
364 if (m_fetchedRecords.first().completed)
365 break;
366 }
367
368 return true;
369}
370
371bool SQLiteIDBCursor::fetch(ShouldFetchForSameKey shouldFetchForSameKey)
372{
373 ASSERT(m_fetchedRecords.isEmpty() || !m_fetchedRecords.last().isTerminalRecord());
374
375 m_fetchedRecords.append({ });
376
377 bool isUnique = m_cursorDirection == IndexedDB::CursorDirection::Nextunique || m_cursorDirection == IndexedDB::CursorDirection::Prevunique || shouldFetchForSameKey == ShouldFetchForSameKey::Yes;
378 if (!isUnique)
379 return fetchNextRecord(m_fetchedRecords.last());
380
381 while (fetchNextRecord(m_fetchedRecords.last())) {
382 if (m_currentKeyForUniqueness.compare(m_fetchedRecords.last().record.key))
383 return true;
384
385 if (m_fetchedRecords.last().completed)
386 return false;
387
388 if (shouldFetchForSameKey == ShouldFetchForSameKey::Yes)
389 m_fetchedRecords.append({ });
390 }
391
392 return false;
393}
394
395bool SQLiteIDBCursor::fetchNextRecord(SQLiteCursorRecord& record)
396{
397 if (m_statementNeedsReset)
398 resetAndRebindStatement();
399
400 FetchResult result;
401 do {
402 result = internalFetchNextRecord(record);
403 } while (result == FetchResult::ShouldFetchAgain);
404
405 return result == FetchResult::Success;
406}
407
408void SQLiteIDBCursor::markAsErrored(SQLiteCursorRecord& record)
409{
410 record.record = { };
411 record.completed = true;
412 record.errored = true;
413 record.rowID = 0;
414}
415
416SQLiteIDBCursor::FetchResult SQLiteIDBCursor::internalFetchNextRecord(SQLiteCursorRecord& record)
417{
418 ASSERT(m_transaction->sqliteTransaction());
419 ASSERT(m_statement);
420 ASSERT(!m_fetchedRecords.isEmpty());
421 ASSERT(!m_fetchedRecords.last().isTerminalRecord());
422
423 record.record.value = nullptr;
424
425 int result = m_statement->step();
426 if (result == SQLITE_DONE) {
427 // When a cursor reaches its end, that is indicated by having undefined keys/values
428 record = { };
429 record.completed = true;
430
431 return FetchResult::Success;
432 }
433
434 if (result != SQLITE_ROW) {
435 LOG_ERROR("Error advancing cursor - (%i) %s", result, m_transaction->sqliteTransaction()->database().lastErrorMsg());
436 markAsErrored(record);
437 return FetchResult::Failure;
438 }
439
440 record.rowID = m_statement->getColumnInt64(0);
441 ASSERT(record.rowID);
442
443 Vector<uint8_t> keyData;
444 m_statement->getColumnBlobAsVector(1, keyData);
445
446 if (!deserializeIDBKeyData(keyData.data(), keyData.size(), record.record.key)) {
447 LOG_ERROR("Unable to deserialize key data from database while advancing cursor");
448 markAsErrored(record);
449 return FetchResult::Failure;
450 }
451
452 m_statement->getColumnBlobAsVector(2, keyData);
453
454 // The primaryKey of an ObjectStore cursor is the same as its key.
455 if (m_indexID == IDBIndexInfo::InvalidId) {
456 record.record.primaryKey = record.record.key;
457
458 Vector<String> blobURLs, blobFilePaths;
459 PAL::SessionID sessionID;
460 auto error = m_transaction->backingStore().getBlobRecordsForObjectStoreRecord(record.rowID, blobURLs, sessionID, blobFilePaths);
461 if (!error.isNull()) {
462 LOG_ERROR("Unable to fetch blob records from database while advancing cursor");
463 markAsErrored(record);
464 return FetchResult::Failure;
465 }
466
467 if (m_cursorType == IndexedDB::CursorType::KeyAndValue)
468 record.record.value = std::make_unique<IDBValue>(ThreadSafeDataBuffer::create(WTFMove(keyData)), blobURLs, sessionID, blobFilePaths);
469 } else {
470 if (!deserializeIDBKeyData(keyData.data(), keyData.size(), record.record.primaryKey)) {
471 LOG_ERROR("Unable to deserialize value data from database while advancing index cursor");
472 markAsErrored(record);
473 return FetchResult::Failure;
474 }
475
476 SQLiteStatement objectStoreStatement(m_statement->database(), "SELECT value FROM Records WHERE key = CAST(? AS TEXT) and objectStoreID = ?;");
477
478 if (objectStoreStatement.prepare() != SQLITE_OK
479 || objectStoreStatement.bindBlob(1, keyData.data(), keyData.size()) != SQLITE_OK
480 || objectStoreStatement.bindInt64(2, m_objectStoreID) != SQLITE_OK) {
481 LOG_ERROR("Could not create index cursor statement into object store records (%i) '%s'", m_statement->database().lastError(), m_statement->database().lastErrorMsg());
482 markAsErrored(record);
483 return FetchResult::Failure;
484 }
485
486 int result = objectStoreStatement.step();
487
488 if (result == SQLITE_ROW) {
489 objectStoreStatement.getColumnBlobAsVector(0, keyData);
490 record.record.value = std::make_unique<IDBValue>(ThreadSafeDataBuffer::create(WTFMove(keyData)));
491 } else if (result == SQLITE_DONE) {
492 // This indicates that the record we're trying to retrieve has been removed from the object store.
493 // Skip over it.
494 return FetchResult::ShouldFetchAgain;
495 } else {
496 LOG_ERROR("Could not step index cursor statement into object store records (%i) '%s'", m_statement->database().lastError(), m_statement->database().lastErrorMsg());
497 markAsErrored(record);
498 return FetchResult::Failure;
499
500 }
501 }
502
503 return FetchResult::Success;
504}
505
506bool SQLiteIDBCursor::iterate(const IDBKeyData& targetKey, const IDBKeyData& targetPrimaryKey)
507{
508 ASSERT(m_transaction->sqliteTransaction());
509 ASSERT(m_statement);
510
511 bool result = advance(1);
512 ASSERT(!m_fetchedRecords.isEmpty());
513
514 // Iterating with no key is equivalent to advancing 1 step.
515 if (targetKey.isNull() || !result)
516 return result;
517
518 while (!m_fetchedRecords.first().isTerminalRecord()) {
519 if (!result)
520 return false;
521
522 // 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.
523 if (m_cursorDirection == IndexedDB::CursorDirection::Next || m_cursorDirection == IndexedDB::CursorDirection::Nextunique) {
524 if (m_fetchedRecords.first().record.key.compare(targetKey) >= 0)
525 break;
526 } else if (m_fetchedRecords.first().record.key.compare(targetKey) <= 0)
527 break;
528
529 result = advance(1);
530 }
531
532 if (targetPrimaryKey.isValid()) {
533 while (!m_fetchedRecords.first().isTerminalRecord() && !m_fetchedRecords.first().record.key.compare(targetKey)) {
534 if (!result)
535 return false;
536
537 // Search for the next primary key >= the primary target if the cursor is a Next cursor, or the next key <= if the cursor is a Previous cursor.
538 if (m_cursorDirection == IndexedDB::CursorDirection::Next || m_cursorDirection == IndexedDB::CursorDirection::Nextunique) {
539 if (m_fetchedRecords.first().record.primaryKey.compare(targetPrimaryKey) >= 0)
540 break;
541 } else if (m_fetchedRecords.first().record.primaryKey.compare(targetPrimaryKey) <= 0)
542 break;
543
544 result = advance(1);
545 }
546 }
547
548 return result;
549}
550
551const IDBKeyData& SQLiteIDBCursor::currentKey() const
552{
553 ASSERT(!m_fetchedRecords.isEmpty());
554 return m_fetchedRecords.first().record.key;
555}
556
557const IDBKeyData& SQLiteIDBCursor::currentPrimaryKey() const
558{
559 ASSERT(!m_fetchedRecords.isEmpty());
560 return m_fetchedRecords.first().record.primaryKey;
561}
562
563IDBValue* SQLiteIDBCursor::currentValue() const
564{
565 ASSERT(!m_fetchedRecords.isEmpty());
566 return m_fetchedRecords.first().record.value.get();
567}
568
569bool SQLiteIDBCursor::didComplete() const
570{
571 ASSERT(!m_fetchedRecords.isEmpty());
572 return m_fetchedRecords.first().completed;
573}
574
575bool SQLiteIDBCursor::didError() const
576{
577 ASSERT(!m_fetchedRecords.isEmpty());
578 return m_fetchedRecords.first().errored;
579}
580
581int64_t SQLiteIDBCursor::currentRecordRowID() const
582{
583 ASSERT(!m_fetchedRecords.isEmpty());
584 return m_fetchedRecords.first().rowID;
585}
586
587
588} // namespace IDBServer
589} // namespace WebCore
590
591#endif // ENABLE(INDEXED_DATABASE)
592