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