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 | |
42 | namespace WebCore { |
43 | namespace IDBServer { |
44 | |
45 | static const size_t prefetchLimit = 8; |
46 | |
47 | std::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 | |
60 | std::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 | |
73 | SQLiteIDBCursor::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 | |
85 | SQLiteIDBCursor::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 | |
98 | SQLiteIDBCursor::~SQLiteIDBCursor() |
99 | { |
100 | if (m_backingStoreCursor) |
101 | m_transaction->closeCursor(*this); |
102 | } |
103 | |
104 | void 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 | |
118 | static 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 | |
147 | static 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 | |
175 | bool 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 | |
194 | bool 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 | |
212 | void 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 | |
253 | void 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 | |
275 | bool 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 | |
306 | bool 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 | |
319 | bool 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 | |
371 | bool 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 | |
395 | bool 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 | |
408 | void SQLiteIDBCursor::markAsErrored(SQLiteCursorRecord& record) |
409 | { |
410 | record.record = { }; |
411 | record.completed = true; |
412 | record.errored = true; |
413 | record.rowID = 0; |
414 | } |
415 | |
416 | SQLiteIDBCursor::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 | |
506 | bool 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 | |
551 | const IDBKeyData& SQLiteIDBCursor::currentKey() const |
552 | { |
553 | ASSERT(!m_fetchedRecords.isEmpty()); |
554 | return m_fetchedRecords.first().record.key; |
555 | } |
556 | |
557 | const IDBKeyData& SQLiteIDBCursor::currentPrimaryKey() const |
558 | { |
559 | ASSERT(!m_fetchedRecords.isEmpty()); |
560 | return m_fetchedRecords.first().record.primaryKey; |
561 | } |
562 | |
563 | IDBValue* SQLiteIDBCursor::currentValue() const |
564 | { |
565 | ASSERT(!m_fetchedRecords.isEmpty()); |
566 | return m_fetchedRecords.first().record.value.get(); |
567 | } |
568 | |
569 | bool SQLiteIDBCursor::didComplete() const |
570 | { |
571 | ASSERT(!m_fetchedRecords.isEmpty()); |
572 | return m_fetchedRecords.first().completed; |
573 | } |
574 | |
575 | bool SQLiteIDBCursor::didError() const |
576 | { |
577 | ASSERT(!m_fetchedRecords.isEmpty()); |
578 | return m_fetchedRecords.first().errored; |
579 | } |
580 | |
581 | int64_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 | |