WebCore/storage/SQLStatement.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  * Copyright (C) 2007 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  *
       
     8  * 1.  Redistributions of source code must retain the above copyright
       
     9  *     notice, this list of conditions and the following disclaimer.
       
    10  * 2.  Redistributions in binary form must reproduce the above copyright
       
    11  *     notice, this list of conditions and the following disclaimer in the
       
    12  *     documentation and/or other materials provided with the distribution.
       
    13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
       
    14  *     its contributors may be used to endorse or promote products derived
       
    15  *     from this software without specific prior written permission.
       
    16  *
       
    17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
       
    18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
       
    19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
       
    20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
       
    21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
       
    22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
       
    23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
       
    24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
       
    26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    27  */
       
    28 #include "config.h"
       
    29 #include "SQLStatement.h"
       
    30 
       
    31 #if ENABLE(DATABASE)
       
    32 
       
    33 #include "Database.h"
       
    34 #include "Logging.h"
       
    35 #include "SQLError.h"
       
    36 #include "SQLiteDatabase.h"
       
    37 #include "SQLiteStatement.h"
       
    38 #include "SQLStatementCallback.h"
       
    39 #include "SQLStatementErrorCallback.h"
       
    40 #include "SQLTransaction.h"
       
    41 #include "SQLValue.h"
       
    42 
       
    43 namespace WebCore {
       
    44 
       
    45 PassRefPtr<SQLStatement> SQLStatement::create(const String& statement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> errorCallback, bool readOnly)
       
    46 {
       
    47     return adoptRef(new SQLStatement(statement, arguments, callback, errorCallback, readOnly));
       
    48 }
       
    49 
       
    50 SQLStatement::SQLStatement(const String& statement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> errorCallback, bool readOnly)
       
    51     : m_statement(statement.crossThreadString())
       
    52     , m_arguments(arguments)
       
    53     , m_statementCallback(callback)
       
    54     , m_statementErrorCallback(errorCallback)
       
    55     , m_readOnly(readOnly)
       
    56 {
       
    57 }
       
    58 
       
    59 bool SQLStatement::execute(Database* db)
       
    60 {
       
    61     ASSERT(!m_resultSet);
       
    62 
       
    63     // If we're re-running this statement after a quota violation, we need to clear that error now
       
    64     clearFailureDueToQuota();
       
    65 
       
    66     // This transaction might have been marked bad while it was being set up on the main thread,
       
    67     // so if there is still an error, return false.
       
    68     if (m_error)
       
    69         return false;
       
    70 
       
    71     if (m_readOnly)
       
    72         db->setAuthorizerReadOnly();
       
    73 
       
    74     SQLiteDatabase* database = &db->sqliteDatabase();
       
    75 
       
    76     SQLiteStatement statement(*database, m_statement);
       
    77     int result = statement.prepare();
       
    78 
       
    79     if (result != SQLResultOk) {
       
    80         LOG(StorageAPI, "Unable to verify correctness of statement %s - error %i (%s)", m_statement.ascii().data(), result, database->lastErrorMsg());
       
    81         m_error = SQLError::create(SQLError::SYNTAX_ERR, database->lastErrorMsg());
       
    82         return false;
       
    83     }
       
    84 
       
    85     // FIXME:  If the statement uses the ?### syntax supported by sqlite, the bind parameter count is very likely off from the number of question marks.
       
    86     // If this is the case, they might be trying to do something fishy or malicious
       
    87     if (statement.bindParameterCount() != m_arguments.size()) {
       
    88         LOG(StorageAPI, "Bind parameter count doesn't match number of question marks");
       
    89         m_error = SQLError::create(SQLError::SYNTAX_ERR, "number of '?'s in statement string does not match argument count");
       
    90         return false;
       
    91     }
       
    92 
       
    93     for (unsigned i = 0; i < m_arguments.size(); ++i) {
       
    94         result = statement.bindValue(i + 1, m_arguments[i]);
       
    95         if (result == SQLResultFull) {
       
    96             setFailureDueToQuota();
       
    97             return false;
       
    98         }
       
    99 
       
   100         if (result != SQLResultOk) {
       
   101             LOG(StorageAPI, "Failed to bind value index %i to statement for query '%s'", i + 1, m_statement.ascii().data());
       
   102             m_error = SQLError::create(SQLError::DATABASE_ERR, database->lastErrorMsg());
       
   103             return false;
       
   104         }
       
   105     }
       
   106 
       
   107     RefPtr<SQLResultSet> resultSet = SQLResultSet::create();
       
   108 
       
   109     // Step so we can fetch the column names.
       
   110     result = statement.step();
       
   111     if (result == SQLResultRow) {
       
   112         int columnCount = statement.columnCount();
       
   113         SQLResultSetRowList* rows = resultSet->rows();
       
   114 
       
   115         for (int i = 0; i < columnCount; i++)
       
   116             rows->addColumn(statement.getColumnName(i));
       
   117 
       
   118         do {
       
   119             for (int i = 0; i < columnCount; i++)
       
   120                 rows->addResult(statement.getColumnValue(i));
       
   121 
       
   122             result = statement.step();
       
   123         } while (result == SQLResultRow);
       
   124 
       
   125         if (result != SQLResultDone) {
       
   126             m_error = SQLError::create(SQLError::DATABASE_ERR, database->lastErrorMsg());
       
   127             return false;
       
   128         }
       
   129     } else if (result == SQLResultDone) {
       
   130         // Didn't find anything, or was an insert
       
   131         if (db->lastActionWasInsert())
       
   132             resultSet->setInsertId(database->lastInsertRowID());
       
   133     } else if (result == SQLResultFull) {
       
   134         // Return the Quota error - the delegate will be asked for more space and this statement might be re-run
       
   135         setFailureDueToQuota();
       
   136         return false;
       
   137     } else {
       
   138         m_error = SQLError::create(SQLError::DATABASE_ERR, database->lastErrorMsg());
       
   139         return false;
       
   140     }
       
   141 
       
   142     // FIXME: If the spec allows triggers, and we want to be "accurate" in a different way, we'd use
       
   143     // sqlite3_total_changes() here instead of sqlite3_changed, because that includes rows modified from within a trigger
       
   144     // For now, this seems sufficient
       
   145     resultSet->setRowsAffected(database->lastChanges());
       
   146 
       
   147     m_resultSet = resultSet;
       
   148     return true;
       
   149 }
       
   150 
       
   151 void SQLStatement::setDatabaseDeletedError()
       
   152 {
       
   153     ASSERT(!m_error && !m_resultSet);
       
   154     m_error = SQLError::create(SQLError::UNKNOWN_ERR, "unable to execute statement, because the user deleted the database");
       
   155 }
       
   156 
       
   157 void SQLStatement::setVersionMismatchedError()
       
   158 {
       
   159     ASSERT(!m_error && !m_resultSet);
       
   160     m_error = SQLError::create(SQLError::VERSION_ERR, "current version of the database and `oldVersion` argument do not match");
       
   161 }
       
   162 
       
   163 bool SQLStatement::performCallback(SQLTransaction* transaction)
       
   164 {
       
   165     ASSERT(transaction);
       
   166 
       
   167     bool callbackError = false;
       
   168 
       
   169     // Call the appropriate statement callback and track if it resulted in an error,
       
   170     // because then we need to jump to the transaction error callback.
       
   171     if (m_error) {
       
   172         ASSERT(m_statementErrorCallback);
       
   173         callbackError = m_statementErrorCallback->handleEvent(transaction->database()->scriptExecutionContext(), transaction, m_error.get());
       
   174     } else if (m_statementCallback)
       
   175         callbackError = !m_statementCallback->handleEvent(transaction->database()->scriptExecutionContext(), transaction, m_resultSet.get());
       
   176 
       
   177     // Now release our callbacks, to break reference cycles.
       
   178     m_statementCallback = 0;
       
   179     m_statementErrorCallback = 0;
       
   180 
       
   181     return callbackError;
       
   182 }
       
   183 
       
   184 void SQLStatement::setFailureDueToQuota()
       
   185 {
       
   186     ASSERT(!m_error && !m_resultSet);
       
   187     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");
       
   188 }
       
   189 
       
   190 void SQLStatement::clearFailureDueToQuota()
       
   191 {
       
   192     if (lastExecutionFailedDueToQuota())
       
   193         m_error = 0;
       
   194 }
       
   195 
       
   196 bool SQLStatement::lastExecutionFailedDueToQuota() const
       
   197 {
       
   198     return m_error && m_error->code() == SQLError::QUOTA_ERR;
       
   199 }
       
   200 
       
   201 } // namespace WebCore
       
   202 
       
   203 #endif // ENABLE(DATABASE)