blob: c6a6c4970c56bb211df518b92090d2cff2c0a290 [file] [log] [blame]
/*
* Copyright (C) 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "SQLiteIDBCursor.h"
#if ENABLE(INDEXED_DATABASE) && ENABLE(DATABASE_PROCESS)
#include "IDBSerialization.h"
#include "Logging.h"
#include "SQLiteIDBTransaction.h"
#include <WebCore/SQLiteStatement.h>
#include <WebCore/SQLiteTransaction.h>
#include <sqlite3.h>
using namespace WebCore;
namespace WebKit {
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)
{
auto cursor = std::unique_ptr<SQLiteIDBCursor>(new SQLiteIDBCursor(transaction, cursorIdentifier, objectStoreID, indexID, cursorDirection, cursorType, taskType, keyRange));
if (!cursor->establishStatement())
return nullptr;
if (!cursor->advance(1))
return nullptr;
return cursor;
}
SQLiteIDBCursor::SQLiteIDBCursor(SQLiteIDBTransaction* transaction, const IDBIdentifier& cursorIdentifier, int64_t objectStoreID, int64_t indexID, IndexedDB::CursorDirection cursorDirection, IndexedDB::CursorType, IDBDatabaseBackend::TaskType, const IDBKeyRangeData& keyRange)
: m_transaction(transaction)
, m_cursorIdentifier(cursorIdentifier)
, m_objectStoreID(objectStoreID)
, m_indexID(indexID)
, m_cursorDirection(cursorDirection)
, m_keyRange(keyRange)
, m_completed(false)
{
ASSERT(m_objectStoreID);
}
static const String& getIndexStatement(bool lowerKey, bool lowerOpen, bool upperKey, bool upperOpen, bool descending)
{
DEFINE_STATIC_LOCAL(Vector<String>, indexStatements, ());
if (indexStatements.isEmpty()) {
indexStatements.reserveCapacity(18);
// No lower key statements (6)
indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? ORDER BY key;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? ORDER BY key DESC;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key < CAST(? AS TEXT) ORDER BY key;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key < CAST(? AS TEXT) ORDER BY key DESC;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key <= CAST(? AS TEXT) ORDER BY key;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key <= CAST(? AS TEXT) ORDER BY key DESC;"));
// Closed lower key statements (6)
indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key >= CAST(? AS TEXT) ORDER BY key;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key >= CAST(? AS TEXT) ORDER BY key DESC;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key DESC;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key DESC;"));
// Open lower key statements (6)
indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key > CAST(? AS TEXT) ORDER BY key;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key > CAST(? AS TEXT) ORDER BY key DESC;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key DESC;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM IndexRecords WHERE indexID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key DESC;"));
}
size_t i = 0;
if (lowerKey)
i += 6;
if (lowerOpen)
i += 6;
if (upperKey)
i += 2;
if (upperOpen)
i += 2;
if (descending)
i += 1;
return indexStatements[i];
}
static const String& getObjectStoreStatement(bool lowerKey, bool lowerOpen, bool upperKey, bool upperOpen, bool descending)
{
DEFINE_STATIC_LOCAL(Vector<String>, indexStatements, ());
if (indexStatements.isEmpty()) {
indexStatements.reserveCapacity(18);
// No lower key statements (6)
indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? ORDER BY key;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? ORDER BY key DESC;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key <= CAST(? AS TEXT) ORDER BY key;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key <= CAST(? AS TEXT) ORDER BY key DESC;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key < CAST(? AS TEXT) ORDER BY key;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key < CAST(? AS TEXT) ORDER BY key DESC;"));
// Closed lower key statements (6)
indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) ORDER BY key;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) ORDER BY key DESC;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key DESC;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key DESC;"));
// Open lower key statements (6)
indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) ORDER BY key;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) ORDER BY key DESC;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key DESC;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;"));
indexStatements.append(ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key DESC;"));
}
size_t i = 0;
if (lowerKey)
i += 6;
if (lowerOpen)
i += 6;
if (upperKey)
i += 2;
if (upperOpen)
i += 2;
if (descending)
i += 1;
return indexStatements[i];
}
bool SQLiteIDBCursor::establishStatement()
{
String sql;
int64_t id;
if (m_indexID != IDBIndexMetadata::InvalidId) {
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);
id = m_indexID;
} else {
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);
id = m_objectStoreID;
}
return createSQLiteStatement(sql, id);
}
bool SQLiteIDBCursor::createSQLiteStatement(const String& sql, int64_t idToBind)
{
ASSERT(m_transaction->sqliteTransaction());
SQLiteDatabase& database = m_transaction->sqliteTransaction()->database();
LOG(IDB, "Creating cursor with SQL query: \"%s\"", sql.utf8().data());
m_statement = std::make_unique<SQLiteStatement>(database, sql);
if (m_statement->prepare() != SQLResultOk
|| m_statement->bindInt64(1, idToBind) != SQLResultOk) {
LOG_ERROR("Could not create cursor statement");
return false;
}
int nextBindArgument = 2;
if (!m_keyRange.lowerKey.isNull) {
RefPtr<SharedBuffer> buffer = serializeIDBKeyData(m_keyRange.lowerKey);
if (m_statement->bindBlob(nextBindArgument++, buffer->data(), buffer->size()) != SQLResultOk) {
LOG_ERROR("Could not create cursor statement");
return false;
}
}
if (!m_keyRange.upperKey.isNull) {
RefPtr<SharedBuffer> buffer = serializeIDBKeyData(m_keyRange.upperKey);
if (m_statement->bindBlob(nextBindArgument, buffer->data(), buffer->size()) != SQLResultOk) {
LOG_ERROR("Could not create cursor statement");
return false;
}
}
return true;
}
bool SQLiteIDBCursor::advance(uint64_t count)
{
bool isUnique = m_cursorDirection == IndexedDB::CursorDirection::NextNoDuplicate || m_cursorDirection == IndexedDB::CursorDirection::PrevNoDuplicate;
for (uint64_t i = 0; i < count; ++i) {
if (!isUnique) {
if (!advanceOnce())
return false;
continue;
}
if (!advanceUnique())
return false;
}
return true;
}
bool SQLiteIDBCursor::advanceUnique()
{
IDBKeyData currentKey = m_currentKey;
while (!m_completed) {
if (!advanceOnce())
return false;
// If the new current key is different from the old current key, we're done.
if (currentKey.compare(m_currentKey))
return true;
}
return false;
}
bool SQLiteIDBCursor::advanceOnce()
{
ASSERT(m_transaction->sqliteTransaction());
ASSERT(m_statement);
if (m_completed) {
LOG_ERROR("Attempt to advance a completed cursor");
return false;
}
int result = m_statement->step();
if (result == SQLResultDone) {
m_completed = true;
return true;
}
if (result != SQLResultRow) {
LOG_ERROR("Error advancing cursor - (%i) %s", result, m_transaction->sqliteTransaction()->database().lastErrorMsg());
m_completed = true;
return false;
}
Vector<char> keyData;
m_statement->getColumnBlobAsVector(0, keyData);
IDBKeyData key;
if (!deserializeIDBKeyData(reinterpret_cast<const uint8_t*>(keyData.data()), keyData.size(), key)) {
LOG_ERROR("Unable to deserialize key data from database while advancing cursor");
m_completed = true;
return false;
}
m_statement->getColumnBlobAsVector(1, keyData);
m_currentKey = key;
m_currentPrimaryKey = key;
m_currentValueBuffer = keyData;
if (m_indexID != IDBIndexMetadata::InvalidId) {
if (!deserializeIDBKeyData(reinterpret_cast<const uint8_t*>(keyData.data()), keyData.size(), m_currentValueKey)) {
LOG_ERROR("Unable to deserialize value data from database while advancing index cursor");
m_completed = true;
return false;
}
SQLiteStatement objectStoreStatement(*m_statement->database(), "SELECT value FROM Records WHERE key = CAST(? AS TEXT) and objectStoreID = ?;");
if (objectStoreStatement.prepare() != SQLResultOk
|| objectStoreStatement.bindBlob(1, m_currentValueBuffer.data(), m_currentValueBuffer.size()) != SQLResultOk
|| objectStoreStatement.bindInt64(2, m_objectStoreID) != SQLResultOk
|| objectStoreStatement.step() != SQLResultRow) {
LOG_ERROR("Could not create index cursor statement into object store records");
return false;
}
objectStoreStatement.getColumnBlobAsVector(0, m_currentValueBuffer);
}
return true;
}
bool SQLiteIDBCursor::iterate(const WebCore::IDBKeyData& targetKey)
{
ASSERT(m_transaction->sqliteTransaction());
ASSERT(m_statement);
bool result = advance(1);
// Iterating with no key is equivalent to advancing 1 step.
if (targetKey.isNull || !result)
return result;
while (!m_completed && m_currentKey.compare(targetKey)) {
result = advance(1);
if (!result)
return false;
}
return result;
}
} // namespace WebKit
#endif // ENABLE(INDEXED_DATABASE) && ENABLE(DATABASE_PROCESS)