1 /* 2 * Copyright (C) 2007, 2013 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 "modules/webdatabase/SQLStatementBackend.h" 30 31 #include "core/platform/Logging.h" 32 #include "core/platform/sql/SQLValue.h" 33 #include "core/platform/sql/SQLiteDatabase.h" 34 #include "core/platform/sql/SQLiteStatement.h" 35 #include "modules/webdatabase/AbstractSQLStatement.h" 36 #include "modules/webdatabase/DatabaseBackend.h" 37 #include "modules/webdatabase/SQLError.h" 38 #include "wtf/text/CString.h" 39 40 41 // The Life-Cycle of a SQLStatement i.e. Who's keeping the SQLStatement alive? 42 // ========================================================================== 43 // The RefPtr chain goes something like this: 44 // 45 // At birth (in SQLTransactionBackend::executeSQL()): 46 // ================================================= 47 // SQLTransactionBackend // Deque<RefPtr<SQLStatementBackend> > m_statementQueue points to ... 48 // --> SQLStatementBackend // OwnPtr<SQLStatement> m_frontend points to ... 49 // --> SQLStatement 50 // 51 // After grabbing the statement for execution (in SQLTransactionBackend::getNextStatement()): 52 // ========================================================================================= 53 // SQLTransactionBackend // RefPtr<SQLStatementBackend> m_currentStatementBackend points to ... 54 // --> SQLStatementBackend // OwnPtr<SQLStatement> m_frontend points to ... 55 // --> SQLStatement 56 // 57 // Then we execute the statement in SQLTransactionBackend::runCurrentStatementAndGetNextState(). 58 // And we callback to the script in SQLTransaction::deliverStatementCallback() if 59 // necessary. 60 // - Inside SQLTransaction::deliverStatementCallback(), we operate on a raw SQLStatement*. 61 // This pointer is valid because it is owned by SQLTransactionBackend's 62 // SQLTransactionBackend::m_currentStatementBackend. 63 // 64 // After we're done executing the statement (in SQLTransactionBackend::getNextStatement()): 65 // ======================================================================================= 66 // When we're done executing, we'll grab the next statement. But before we 67 // do that, getNextStatement() nullify SQLTransactionBackend::m_currentStatementBackend. 68 // This will trigger the deletion of the SQLStatementBackend and SQLStatement. 69 // 70 // Note: unlike with SQLTransaction, there is no JS representation of SQLStatement. 71 // Hence, there is no GC dependency at play here. 72 73 namespace WebCore { 74 75 PassRefPtr<SQLStatementBackend> SQLStatementBackend::create(PassOwnPtr<AbstractSQLStatement> frontend, 76 const String& statement, const Vector<SQLValue>& arguments, int permissions) 77 { 78 return adoptRef(new SQLStatementBackend(frontend, statement, arguments, permissions)); 79 } 80 81 SQLStatementBackend::SQLStatementBackend(PassOwnPtr<AbstractSQLStatement> frontend, 82 const String& statement, const Vector<SQLValue>& arguments, int permissions) 83 : m_frontend(frontend) 84 , m_statement(statement.isolatedCopy()) 85 , m_arguments(arguments) 86 , m_hasCallback(m_frontend->hasCallback()) 87 , m_hasErrorCallback(m_frontend->hasErrorCallback()) 88 , m_permissions(permissions) 89 { 90 m_frontend->setBackend(this); 91 } 92 93 AbstractSQLStatement* SQLStatementBackend::frontend() 94 { 95 return m_frontend.get(); 96 } 97 98 PassRefPtr<SQLError> SQLStatementBackend::sqlError() const 99 { 100 return m_error; 101 } 102 103 PassRefPtr<SQLResultSet> SQLStatementBackend::sqlResultSet() const 104 { 105 return m_resultSet; 106 } 107 108 bool SQLStatementBackend::execute(DatabaseBackend* db) 109 { 110 ASSERT(!m_resultSet); 111 112 // If we're re-running this statement after a quota violation, we need to clear that error now 113 clearFailureDueToQuota(); 114 115 // This transaction might have been marked bad while it was being set up on the main thread, 116 // so if there is still an error, return false. 117 if (m_error) 118 return false; 119 120 db->setAuthorizerPermissions(m_permissions); 121 122 SQLiteDatabase* database = &db->sqliteDatabase(); 123 124 SQLiteStatement statement(*database, m_statement); 125 int result = statement.prepare(); 126 127 if (result != SQLResultOk) { 128 LOG(StorageAPI, "Unable to verify correctness of statement %s - error %i (%s)", m_statement.ascii().data(), result, database->lastErrorMsg()); 129 if (result == SQLResultInterrupt) 130 m_error = SQLError::create(SQLError::DATABASE_ERR, "could not prepare statement", result, "interrupted"); 131 else 132 m_error = SQLError::create(SQLError::SYNTAX_ERR, "could not prepare statement", result, database->lastErrorMsg()); 133 db->reportExecuteStatementResult(1, m_error->code(), result); 134 return false; 135 } 136 137 // FIXME: If the statement uses the ?### syntax supported by sqlite, the bind parameter count is very likely off from the number of question marks. 138 // If this is the case, they might be trying to do something fishy or malicious 139 if (statement.bindParameterCount() != m_arguments.size()) { 140 LOG(StorageAPI, "Bind parameter count doesn't match number of question marks"); 141 m_error = SQLError::create(db->isInterrupted() ? SQLError::DATABASE_ERR : SQLError::SYNTAX_ERR, "number of '?'s in statement string does not match argument count"); 142 db->reportExecuteStatementResult(2, m_error->code(), 0); 143 return false; 144 } 145 146 for (unsigned i = 0; i < m_arguments.size(); ++i) { 147 result = statement.bindValue(i + 1, m_arguments[i]); 148 if (result == SQLResultFull) { 149 setFailureDueToQuota(db); 150 return false; 151 } 152 153 if (result != SQLResultOk) { 154 LOG(StorageAPI, "Failed to bind value index %i to statement for query '%s'", i + 1, m_statement.ascii().data()); 155 db->reportExecuteStatementResult(3, SQLError::DATABASE_ERR, result); 156 m_error = SQLError::create(SQLError::DATABASE_ERR, "could not bind value", result, database->lastErrorMsg()); 157 return false; 158 } 159 } 160 161 RefPtr<SQLResultSet> resultSet = SQLResultSet::create(); 162 163 // Step so we can fetch the column names. 164 result = statement.step(); 165 if (result == SQLResultRow) { 166 int columnCount = statement.columnCount(); 167 SQLResultSetRowList* rows = resultSet->rows(); 168 169 for (int i = 0; i < columnCount; i++) 170 rows->addColumn(statement.getColumnName(i)); 171 172 do { 173 for (int i = 0; i < columnCount; i++) 174 rows->addResult(statement.getColumnValue(i)); 175 176 result = statement.step(); 177 } while (result == SQLResultRow); 178 179 if (result != SQLResultDone) { 180 db->reportExecuteStatementResult(4, SQLError::DATABASE_ERR, result); 181 m_error = SQLError::create(SQLError::DATABASE_ERR, "could not iterate results", result, database->lastErrorMsg()); 182 return false; 183 } 184 } else if (result == SQLResultDone) { 185 // Didn't find anything, or was an insert 186 if (db->lastActionWasInsert()) 187 resultSet->setInsertId(database->lastInsertRowID()); 188 } else if (result == SQLResultFull) { 189 // Return the Quota error - the delegate will be asked for more space and this statement might be re-run 190 setFailureDueToQuota(db); 191 return false; 192 } else if (result == SQLResultConstraint) { 193 db->reportExecuteStatementResult(6, SQLError::CONSTRAINT_ERR, result); 194 m_error = SQLError::create(SQLError::CONSTRAINT_ERR, "could not execute statement due to a constaint failure", result, database->lastErrorMsg()); 195 return false; 196 } else { 197 db->reportExecuteStatementResult(5, SQLError::DATABASE_ERR, result); 198 m_error = SQLError::create(SQLError::DATABASE_ERR, "could not execute statement", result, database->lastErrorMsg()); 199 return false; 200 } 201 202 // FIXME: If the spec allows triggers, and we want to be "accurate" in a different way, we'd use 203 // sqlite3_total_changes() here instead of sqlite3_changed, because that includes rows modified from within a trigger 204 // For now, this seems sufficient 205 resultSet->setRowsAffected(database->lastChanges()); 206 207 m_resultSet = resultSet; 208 db->reportExecuteStatementResult(0, -1, 0); // OK 209 return true; 210 } 211 212 void SQLStatementBackend::setDatabaseDeletedError(DatabaseBackend* database) 213 { 214 ASSERT(!m_error && !m_resultSet); 215 database->reportExecuteStatementResult(6, SQLError::UNKNOWN_ERR, 0); 216 m_error = SQLError::create(SQLError::UNKNOWN_ERR, "unable to execute statement, because the user deleted the database"); 217 } 218 219 void SQLStatementBackend::setVersionMismatchedError(DatabaseBackend* database) 220 { 221 ASSERT(!m_error && !m_resultSet); 222 database->reportExecuteStatementResult(7, SQLError::VERSION_ERR, 0); 223 m_error = SQLError::create(SQLError::VERSION_ERR, "current version of the database and `oldVersion` argument do not match"); 224 } 225 226 void SQLStatementBackend::setFailureDueToQuota(DatabaseBackend* database) 227 { 228 ASSERT(!m_error && !m_resultSet); 229 database->reportExecuteStatementResult(8, SQLError::QUOTA_ERR, 0); 230 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"); 231 } 232 233 void SQLStatementBackend::clearFailureDueToQuota() 234 { 235 if (lastExecutionFailedDueToQuota()) 236 m_error = 0; 237 } 238 239 bool SQLStatementBackend::lastExecutionFailedDueToQuota() const 240 { 241 return m_error && m_error->code() == SQLError::QUOTA_ERR; 242 } 243 244 } // namespace WebCore 245