~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Source/WebCore/Modules/webdatabase/SQLTransactionSync.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2010 Google 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 are
 
6
 * met:
 
7
 *
 
8
 *     * Redistributions of source code must retain the above copyright
 
9
 * notice, this list of conditions and the following disclaimer.
 
10
 *     * Redistributions in binary form must reproduce the above
 
11
 * copyright notice, this list of conditions and the following disclaimer
 
12
 * in the documentation and/or other materials provided with the
 
13
 * distribution.
 
14
 *     * Neither the name of Google Inc. nor the names of its
 
15
 * contributors may be used to endorse or promote products derived from
 
16
 * this software without specific prior written permission.
 
17
 *
 
18
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
19
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
20
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 
21
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 
22
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
23
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 
24
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
25
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
26
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
27
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 
28
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
29
 */
 
30
 
 
31
#include "config.h"
 
32
#include "SQLTransactionSync.h"
 
33
 
 
34
#if ENABLE(SQL_DATABASE)
 
35
 
 
36
#include "DatabaseAuthorizer.h"
 
37
#include "DatabaseContext.h"
 
38
#include "DatabaseSync.h"
 
39
#include "SQLException.h"
 
40
#include "SQLResultSet.h"
 
41
#include "SQLStatementSync.h"
 
42
#include "SQLTransactionClient.h"
 
43
#include "SQLTransactionSyncCallback.h"
 
44
#include "SQLValue.h"
 
45
#include "SQLiteTransaction.h"
 
46
#include "ScriptExecutionContext.h"
 
47
#include <wtf/PassRefPtr.h>
 
48
#include <wtf/RefPtr.h>
 
49
#include <wtf/text/WTFString.h>
 
50
 
 
51
namespace WebCore {
 
52
 
 
53
PassRefPtr<SQLTransactionSync> SQLTransactionSync::create(DatabaseSync* db, PassRefPtr<SQLTransactionSyncCallback> callback, bool readOnly)
 
54
{
 
55
    return adoptRef(new SQLTransactionSync(db, callback, readOnly));
 
56
}
 
57
 
 
58
SQLTransactionSync::SQLTransactionSync(DatabaseSync* db, PassRefPtr<SQLTransactionSyncCallback> callback, bool readOnly)
 
59
    : m_database(db)
 
60
    , m_callback(callback)
 
61
    , m_readOnly(readOnly)
 
62
    , m_hasVersionMismatch(false)
 
63
    , m_modifiedDatabase(false)
 
64
    , m_transactionClient(adoptPtr(new SQLTransactionClient()))
 
65
{
 
66
    ASSERT(m_database->scriptExecutionContext()->isContextThread());
 
67
}
 
68
 
 
69
SQLTransactionSync::~SQLTransactionSync()
 
70
{
 
71
    ASSERT(m_database->scriptExecutionContext()->isContextThread());
 
72
    if (m_sqliteTransaction && m_sqliteTransaction->inProgress())
 
73
        rollback();
 
74
}
 
75
 
 
76
PassRefPtr<SQLResultSet> SQLTransactionSync::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, ExceptionCode& ec)
 
77
{
 
78
    ASSERT(m_database->scriptExecutionContext()->isContextThread());
 
79
 
 
80
    m_database->setLastErrorMessage("");
 
81
 
 
82
    if (!m_database->opened()) {
 
83
        m_database->setLastErrorMessage("cannot executeSQL because the database is not open");
 
84
        ec = SQLException::UNKNOWN_ERR;
 
85
        return 0;
 
86
    }
 
87
 
 
88
    if (m_hasVersionMismatch) {
 
89
        m_database->setLastErrorMessage("cannot executeSQL because there is a version mismatch");
 
90
        ec = SQLException::VERSION_ERR;
 
91
        return 0;
 
92
    }
 
93
 
 
94
    if (sqlStatement.isEmpty())
 
95
        return 0;
 
96
 
 
97
    int permissions = DatabaseAuthorizer::ReadWriteMask;
 
98
    if (!m_database->databaseContext()->allowDatabaseAccess())
 
99
      permissions |= DatabaseAuthorizer::NoAccessMask;
 
100
    else if (m_readOnly)
 
101
      permissions |= DatabaseAuthorizer::ReadOnlyMask;
 
102
 
 
103
    SQLStatementSync statement(sqlStatement, arguments, permissions);
 
104
 
 
105
    m_database->resetAuthorizer();
 
106
    bool retryStatement = true;
 
107
    RefPtr<SQLResultSet> resultSet;
 
108
    while (retryStatement) {
 
109
        retryStatement = false;
 
110
        resultSet = statement.execute(m_database.get(), ec);
 
111
        if (!resultSet) {
 
112
            if (m_sqliteTransaction->wasRolledBackBySqlite())
 
113
                return 0;
 
114
 
 
115
            if (ec == SQLException::QUOTA_ERR) {
 
116
                if (m_transactionClient->didExceedQuota(database())) {
 
117
                    ec = 0;
 
118
                    retryStatement = true;
 
119
                } else {
 
120
                    m_database->setLastErrorMessage("there was not enough remaining storage space");
 
121
                    return 0;
 
122
                }
 
123
            }
 
124
        }
 
125
    }
 
126
 
 
127
    if (m_database->lastActionChangedDatabase()) {
 
128
        m_modifiedDatabase = true;
 
129
        m_transactionClient->didExecuteStatement(database());
 
130
    }
 
131
 
 
132
    return resultSet.release();
 
133
}
 
134
 
 
135
ExceptionCode SQLTransactionSync::begin()
 
136
{
 
137
    ASSERT(m_database->scriptExecutionContext()->isContextThread());
 
138
    if (!m_database->opened()) {
 
139
        m_database->reportStartTransactionResult(1, SQLException::UNKNOWN_ERR, 0);
 
140
        m_database->setLastErrorMessage("cannot begin transaction because the database is not open");
 
141
        return SQLException::UNKNOWN_ERR;
 
142
    }
 
143
 
 
144
    ASSERT(!m_database->sqliteDatabase().transactionInProgress());
 
145
 
 
146
    // Set the maximum usage for this transaction if this transactions is not read-only.
 
147
    if (!m_readOnly)
 
148
        m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize());
 
149
 
 
150
    ASSERT(!m_sqliteTransaction);
 
151
    m_sqliteTransaction = adoptPtr(new SQLiteTransaction(m_database->sqliteDatabase(), m_readOnly));
 
152
 
 
153
    m_database->resetDeletes();
 
154
    m_database->disableAuthorizer();
 
155
    m_sqliteTransaction->begin();
 
156
    m_database->enableAuthorizer();
 
157
 
 
158
    // Check if begin() succeeded.
 
159
    if (!m_sqliteTransaction->inProgress()) {
 
160
        ASSERT(!m_database->sqliteDatabase().transactionInProgress());
 
161
        m_database->reportStartTransactionResult(2, SQLException::DATABASE_ERR, m_database->sqliteDatabase().lastError());
 
162
        m_database->setLastErrorMessage("unable to begin transaction",
 
163
                                        m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg());
 
164
        m_sqliteTransaction.clear();
 
165
        return SQLException::DATABASE_ERR;
 
166
    }
 
167
 
 
168
    // Note: We intentionally retrieve the actual version even with an empty expected version.
 
169
    // In multi-process browsers, we take this opportinutiy to update the cached value for
 
170
    // the actual version. In single-process browsers, this is just a map lookup.
 
171
    String actualVersion;
 
172
    if (!m_database->getActualVersionForTransaction(actualVersion)) {
 
173
        m_database->reportStartTransactionResult(3, SQLException::DATABASE_ERR, m_database->sqliteDatabase().lastError());
 
174
        m_database->setLastErrorMessage("unable to read version",
 
175
                                        m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg());
 
176
        rollback();
 
177
        return SQLException::DATABASE_ERR;
 
178
    }
 
179
    m_hasVersionMismatch = !m_database->expectedVersion().isEmpty()
 
180
                           && (m_database->expectedVersion() != actualVersion);
 
181
    m_database->reportStartTransactionResult(0, -1, 0); // OK
 
182
    return 0;
 
183
}
 
184
 
 
185
ExceptionCode SQLTransactionSync::execute()
 
186
{
 
187
    ASSERT(m_database->scriptExecutionContext()->isContextThread());
 
188
    if (!m_database->opened() || (m_callback && !m_callback->handleEvent(this))) {
 
189
        if (m_database->lastErrorMessage().isEmpty())
 
190
            m_database->setLastErrorMessage("failed to execute transaction callback");
 
191
        m_callback = 0;
 
192
        return SQLException::UNKNOWN_ERR;
 
193
    }
 
194
 
 
195
    m_callback = 0;
 
196
    return 0;
 
197
}
 
198
 
 
199
ExceptionCode SQLTransactionSync::commit()
 
200
{
 
201
    ASSERT(m_database->scriptExecutionContext()->isContextThread());
 
202
    if (!m_database->opened()) {
 
203
        m_database->reportCommitTransactionResult(1, SQLException::UNKNOWN_ERR, 0);
 
204
        m_database->setLastErrorMessage("unable to commit transaction because the database is not open.");
 
205
        return SQLException::UNKNOWN_ERR;
 
206
    }
 
207
 
 
208
    ASSERT(m_sqliteTransaction);
 
209
 
 
210
    m_database->disableAuthorizer();
 
211
    m_sqliteTransaction->commit();
 
212
    m_database->enableAuthorizer();
 
213
 
 
214
    // If the commit failed, the transaction will still be marked as "in progress"
 
215
    if (m_sqliteTransaction->inProgress()) {
 
216
        m_database->reportCommitTransactionResult(2, SQLException::DATABASE_ERR, m_database->sqliteDatabase().lastError());
 
217
        m_database->setLastErrorMessage("unable to commit transaction",
 
218
                                        m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg());
 
219
        return SQLException::DATABASE_ERR;
 
220
    }
 
221
 
 
222
    m_sqliteTransaction.clear();
 
223
 
 
224
    // Vacuum the database if anything was deleted.
 
225
    if (m_database->hadDeletes())
 
226
        m_database->incrementalVacuumIfNeeded();
 
227
 
 
228
    // The commit was successful. If the transaction modified this database, notify the delegates.
 
229
    if (m_modifiedDatabase)
 
230
        m_transactionClient->didCommitWriteTransaction(database());
 
231
 
 
232
    m_database->reportCommitTransactionResult(0, -1, 0); // OK
 
233
    return 0;
 
234
}
 
235
 
 
236
void SQLTransactionSync::rollback()
 
237
{
 
238
    ASSERT(m_database->scriptExecutionContext()->isContextThread());
 
239
    m_database->disableAuthorizer();
 
240
    if (m_sqliteTransaction) {
 
241
        m_sqliteTransaction->rollback();
 
242
        m_sqliteTransaction.clear();
 
243
    }
 
244
    m_database->enableAuthorizer();
 
245
 
 
246
    ASSERT(!m_database->sqliteDatabase().transactionInProgress());
 
247
}
 
248
 
 
249
} // namespace WebCore
 
250
 
 
251
#endif // ENABLE(SQL_DATABASE)