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 "DatabaseAuthorizer.h" 35 #include "Logging.h" 36 #include "SQLError.h" 37 #include "SQLiteDatabase.h" 38 #include "SQLiteStatement.h" 39 #include "SQLResultSet.h" 40 #include "SQLStatementCallback.h" 41 #include "SQLStatementErrorCallback.h" 42 #include "SQLTransaction.h" 43 #include "SQLValue.h" 44 45 namespace WebCore { 46 47 PassRefPtr<SQLStatement> SQLStatement::create(const String& statement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> errorCallback, bool readOnly) 48 { 49 return adoptRef(new SQLStatement(statement, arguments, callback, errorCallback, readOnly)); 50 } 51 52 SQLStatement::SQLStatement(const String& statement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> errorCallback, bool readOnly) 53 : m_statement(statement.crossThreadString()) 54 , m_arguments(arguments) 55 , m_statementCallback(callback) 56 , m_statementErrorCallback(errorCallback) 57 , m_readOnly(readOnly) 58 { 59 } 60 61 bool SQLStatement::execute(Database* db) 62 { 63 ASSERT(!m_resultSet); 64 65 // If we're re-running this statement after a quota violation, we need to clear that error now 66 clearFailureDueToQuota(); 67 68 // This transaction might have been marked bad while it was being set up on the main thread, 69 // so if there is still an error, return false. 70 if (m_error) 71 return false; 72 73 if (m_readOnly) 74 db->setAuthorizerReadOnly(); 75 76 SQLiteDatabase* database = &db->m_sqliteDatabase; 77 78 SQLiteStatement statement(*database, m_statement); 79 int result = statement.prepare(); 80 81 if (result != SQLResultOk) { 82 LOG(StorageAPI, "Unable to verify correctness of statement %s - error %i (%s)", m_statement.ascii().data(), result, database->lastErrorMsg()); 83 m_error = SQLError::create(1, database->lastErrorMsg()); 84 return false; 85 } 86 87 // FIXME: If the statement uses the ?### syntax supported by sqlite, the bind parameter count is very likely off from the number of question marks. 88 // If this is the case, they might be trying to do something fishy or malicious 89 if (statement.bindParameterCount() != m_arguments.size()) { 90 LOG(StorageAPI, "Bind parameter count doesn't match number of question marks"); 91 m_error = SQLError::create(1, "number of '?'s in statement string does not match argument count"); 92 return false; 93 } 94 95 for (unsigned i = 0; i < m_arguments.size(); ++i) { 96 result = statement.bindValue(i + 1, m_arguments[i]); 97 if (result == SQLResultFull) { 98 setFailureDueToQuota(); 99 return false; 100 } 101 102 if (result != SQLResultOk) { 103 LOG(StorageAPI, "Failed to bind value index %i to statement for query '%s'", i + 1, m_statement.ascii().data()); 104 m_error = SQLError::create(1, database->lastErrorMsg()); 105 return false; 106 } 107 } 108 109 RefPtr<SQLResultSet> resultSet = SQLResultSet::create(); 110 111 // Step so we can fetch the column names. 112 result = statement.step(); 113 if (result == SQLResultRow) { 114 int columnCount = statement.columnCount(); 115 SQLResultSetRowList* rows = resultSet->rows(); 116 117 for (int i = 0; i < columnCount; i++) 118 rows->addColumn(statement.getColumnName(i)); 119 120 do { 121 for (int i = 0; i < columnCount; i++) 122 rows->addResult(statement.getColumnValue(i)); 123 124 result = statement.step(); 125 } while (result == SQLResultRow); 126 127 if (result != SQLResultDone) { 128 m_error = SQLError::create(1, database->lastErrorMsg()); 129 return false; 130 } 131 } else if (result == SQLResultDone) { 132 // Didn't find anything, or was an insert 133 if (db->m_databaseAuthorizer->lastActionWasInsert()) 134 resultSet->setInsertId(database->lastInsertRowID()); 135 } else if (result == SQLResultFull) { 136 // Return the Quota error - the delegate will be asked for more space and this statement might be re-run 137 setFailureDueToQuota(); 138 return false; 139 } else { 140 m_error = SQLError::create(1, database->lastErrorMsg()); 141 return false; 142 } 143 144 // FIXME: If the spec allows triggers, and we want to be "accurate" in a different way, we'd use 145 // sqlite3_total_changes() here instead of sqlite3_changed, because that includes rows modified from within a trigger 146 // For now, this seems sufficient 147 resultSet->setRowsAffected(database->lastChanges()); 148 149 m_resultSet = resultSet; 150 return true; 151 } 152 153 void SQLStatement::setDatabaseDeletedError() 154 { 155 ASSERT(!m_error && !m_resultSet); 156 m_error = SQLError::create(0, "unable to execute statement, because the user deleted the database"); 157 } 158 159 void SQLStatement::setVersionMismatchedError() 160 { 161 ASSERT(!m_error && !m_resultSet); 162 m_error = SQLError::create(2, "current version of the database and `oldVersion` argument do not match"); 163 } 164 165 bool SQLStatement::performCallback(SQLTransaction* transaction) 166 { 167 ASSERT(transaction); 168 169 bool callbackError = false; 170 171 // Call the appropriate statement callback and track if it resulted in an error, 172 // because then we need to jump to the transaction error callback. 173 if (m_error) { 174 ASSERT(m_statementErrorCallback); 175 callbackError = m_statementErrorCallback->handleEvent(transaction, m_error.get()); 176 } else if (m_statementCallback) 177 m_statementCallback->handleEvent(transaction, m_resultSet.get(), callbackError); 178 179 // Now release our callbacks, to break reference cycles. 180 m_statementCallback = 0; 181 m_statementErrorCallback = 0; 182 183 return callbackError; 184 } 185 186 void SQLStatement::setFailureDueToQuota() 187 { 188 ASSERT(!m_error && !m_resultSet); 189 m_error = SQLError::create(4, "there was not enough remaining storage space, or the storage quota was reached and the user declined to allow more space"); 190 } 191 192 void SQLStatement::clearFailureDueToQuota() 193 { 194 if (lastExecutionFailedDueToQuota()) 195 m_error = 0; 196 } 197 198 bool SQLStatement::lastExecutionFailedDueToQuota() const 199 { 200 return m_error && m_error->code() == 4; 201 } 202 203 } // namespace WebCore 204 205 #endif // ENABLE(DATABASE) 206