/* * Copyright (C) 2007, 2013 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. * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE 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 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 "SQLStatement.h" #include "Database.h" #include "Logging.h" #include "SQLError.h" #include "SQLResultSet.h" #include "SQLStatementCallback.h" #include "SQLStatementErrorCallback.h" #include "SQLValue.h" #include "SQLiteDatabase.h" #include "SQLiteStatement.h" #include // The Life-Cycle of a SQLStatement i.e. Who's keeping the SQLStatement alive? // ========================================================================== // The RefPtr chain goes something like this: // // At birth (in SQLTransactionBackend::executeSQL()): // ================================================= // SQLTransactionBackend // Deque> m_statementQueue points to ... // --> SQLStatement // std::unique_ptr m_frontend points to ... // --> SQLStatement // // After grabbing the statement for execution (in SQLTransactionBackend::getNextStatement()): // ========================================================================================= // SQLTransactionBackend // RefPtr m_currentStatementBackend points to ... // --> SQLStatement // std::unique_ptr m_frontend points to ... // --> SQLStatement // // Then we execute the statement in SQLTransactionBackend::runCurrentStatementAndGetNextState(). // And we callback to the script in SQLTransaction::deliverStatementCallback() if // necessary. // - Inside SQLTransaction::deliverStatementCallback(), we operate on a raw SQLStatement*. // This pointer is valid because it is owned by SQLTransactionBackend's // SQLTransactionBackend::m_currentStatementBackend. // // After we're done executing the statement (in SQLTransactionBackend::getNextStatement()): // ======================================================================================= // When we're done executing, we'll grab the next statement. But before we // do that, getNextStatement() nullify SQLTransactionBackend::m_currentStatementBackend. // This will trigger the deletion of the SQLStatement and SQLStatement. // // Note: unlike with SQLTransaction, there is no JS representation of SQLStatement. // Hence, there is no GC dependency at play here. namespace WebCore { SQLStatement::SQLStatement(Database& database, const String& statement, const Vector& arguments, PassRefPtr callback, PassRefPtr errorCallback, int permissions) : m_statement(statement.isolatedCopy()) , m_arguments(arguments) , m_statementCallbackWrapper(callback, database.scriptExecutionContext()) , m_statementErrorCallbackWrapper(errorCallback, database.scriptExecutionContext()) , m_permissions(permissions) { } SQLStatement::~SQLStatement() { } PassRefPtr SQLStatement::sqlError() const { return m_error; } PassRefPtr SQLStatement::sqlResultSet() const { return m_resultSet; } bool SQLStatement::execute(Database& db) { ASSERT(!m_resultSet); // If we're re-running this statement after a quota violation, we need to clear that error now clearFailureDueToQuota(); // This transaction might have been marked bad while it was being set up on the main thread, // so if there is still an error, return false. if (m_error) return false; db.setAuthorizerPermissions(m_permissions); SQLiteDatabase& database = db.sqliteDatabase(); SQLiteStatement statement(database, m_statement); int result = statement.prepare(); if (result != SQLITE_OK) { LOG(StorageAPI, "Unable to verify correctness of statement %s - error %i (%s)", m_statement.ascii().data(), result, database.lastErrorMsg()); if (result == SQLITE_INTERRUPT) m_error = SQLError::create(SQLError::DATABASE_ERR, "could not prepare statement", result, "interrupted"); else m_error = SQLError::create(SQLError::SYNTAX_ERR, "could not prepare statement", result, database.lastErrorMsg()); return false; } // FIXME: If the statement uses the ?### syntax supported by sqlite, the bind parameter count is very likely off from the number of question marks. // If this is the case, they might be trying to do something fishy or malicious if (statement.bindParameterCount() != m_arguments.size()) { LOG(StorageAPI, "Bind parameter count doesn't match number of question marks"); m_error = SQLError::create(SQLError::SYNTAX_ERR, "number of '?'s in statement string does not match argument count"); return false; } for (unsigned i = 0; i < m_arguments.size(); ++i) { result = statement.bindValue(i + 1, m_arguments[i]); if (result == SQLITE_FULL) { setFailureDueToQuota(); return false; } if (result != SQLITE_OK) { LOG(StorageAPI, "Failed to bind value index %i to statement for query '%s'", i + 1, m_statement.ascii().data()); m_error = SQLError::create(SQLError::DATABASE_ERR, "could not bind value", result, database.lastErrorMsg()); return false; } } RefPtr resultSet = SQLResultSet::create(); // Step so we can fetch the column names. result = statement.step(); switch (result) { case SQLITE_ROW: { int columnCount = statement.columnCount(); SQLResultSetRowList* rows = resultSet->rows(); for (int i = 0; i < columnCount; i++) rows->addColumn(statement.getColumnName(i)); do { for (int i = 0; i < columnCount; i++) rows->addResult(statement.getColumnValue(i)); result = statement.step(); } while (result == SQLITE_ROW); if (result != SQLITE_DONE) { m_error = SQLError::create(SQLError::DATABASE_ERR, "could not iterate results", result, database.lastErrorMsg()); return false; } break; } case SQLITE_DONE: { // Didn't find anything, or was an insert if (db.lastActionWasInsert()) resultSet->setInsertId(database.lastInsertRowID()); break; } case SQLITE_FULL: // Return the Quota error - the delegate will be asked for more space and this statement might be re-run setFailureDueToQuota(); return false; case SQLITE_CONSTRAINT: m_error = SQLError::create(SQLError::CONSTRAINT_ERR, "could not execute statement due to a constaint failure", result, database.lastErrorMsg()); return false; default: m_error = SQLError::create(SQLError::DATABASE_ERR, "could not execute statement", result, database.lastErrorMsg()); return false; } // FIXME: If the spec allows triggers, and we want to be "accurate" in a different way, we'd use // sqlite3_total_changes() here instead of sqlite3_changed, because that includes rows modified from within a trigger // For now, this seems sufficient resultSet->setRowsAffected(database.lastChanges()); m_resultSet = resultSet; return true; } bool SQLStatement::performCallback(SQLTransaction* transaction) { ASSERT(transaction); bool callbackError = false; RefPtr callback = m_statementCallbackWrapper.unwrap(); RefPtr errorCallback = m_statementErrorCallbackWrapper.unwrap(); RefPtr error = sqlError(); // Call the appropriate statement callback and track if it resulted in an error, // because then we need to jump to the transaction error callback. if (error) { if (errorCallback) callbackError = errorCallback->handleEvent(transaction, error.get()); } else if (callback) { RefPtr resultSet = sqlResultSet(); callbackError = !callback->handleEvent(transaction, resultSet.get()); } return callbackError; } void SQLStatement::setDatabaseDeletedError() { ASSERT(!m_error && !m_resultSet); m_error = SQLError::create(SQLError::UNKNOWN_ERR, "unable to execute statement, because the user deleted the database"); } void SQLStatement::setVersionMismatchedError() { ASSERT(!m_error && !m_resultSet); m_error = SQLError::create(SQLError::VERSION_ERR, "current version of the database and `oldVersion` argument do not match"); } void SQLStatement::setFailureDueToQuota() { ASSERT(!m_error && !m_resultSet); m_error = SQLError::create(SQLError::QUOTA_ERR, "there was not enough remaining storage space, or the storage quota was reached and the user declined to allow more space"); } void SQLStatement::clearFailureDueToQuota() { if (lastExecutionFailedDueToQuota()) m_error = nullptr; } bool SQLStatement::lastExecutionFailedDueToQuota() const { return m_error && m_error->code() == SQLError::QUOTA_ERR; } } // namespace WebCore