1/*
2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com)
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "modules/webdatabase/sqlite/SQLiteDatabase.h"
29
30#include <sqlite3.h>
31#include "platform/Logging.h"
32#include "modules/webdatabase/sqlite/SQLiteFileSystem.h"
33#include "modules/webdatabase/sqlite/SQLiteStatement.h"
34#include "modules/webdatabase/DatabaseAuthorizer.h"
35
36namespace WebCore {
37
38const int SQLResultDone = SQLITE_DONE;
39const int SQLResultError = SQLITE_ERROR;
40const int SQLResultOk = SQLITE_OK;
41const int SQLResultRow = SQLITE_ROW;
42const int SQLResultSchema = SQLITE_SCHEMA;
43const int SQLResultFull = SQLITE_FULL;
44const int SQLResultInterrupt = SQLITE_INTERRUPT;
45const int SQLResultConstraint = SQLITE_CONSTRAINT;
46
47static const char notOpenErrorMessage[] = "database is not open";
48
49SQLiteDatabase::SQLiteDatabase()
50    : m_db(0)
51    , m_pageSize(-1)
52    , m_transactionInProgress(false)
53    , m_sharable(false)
54    , m_openingThread(0)
55    , m_interrupted(false)
56    , m_openError(SQLITE_ERROR)
57    , m_openErrorMessage()
58    , m_lastChangesCount(0)
59{
60}
61
62SQLiteDatabase::~SQLiteDatabase()
63{
64    close();
65}
66
67bool SQLiteDatabase::open(const String& filename, bool forWebSQLDatabase)
68{
69    close();
70
71    m_openError = SQLiteFileSystem::openDatabase(filename, &m_db, forWebSQLDatabase);
72    if (m_openError != SQLITE_OK) {
73        m_openErrorMessage = m_db ? sqlite3_errmsg(m_db) : "sqlite_open returned null";
74        WTF_LOG_ERROR("SQLite database failed to load from %s\nCause - %s", filename.ascii().data(),
75            m_openErrorMessage.data());
76        sqlite3_close(m_db);
77        m_db = 0;
78        return false;
79    }
80
81    m_openError = sqlite3_extended_result_codes(m_db, 1);
82    if (m_openError != SQLITE_OK) {
83        m_openErrorMessage = sqlite3_errmsg(m_db);
84        WTF_LOG_ERROR("SQLite database error when enabling extended errors - %s", m_openErrorMessage.data());
85        sqlite3_close(m_db);
86        m_db = 0;
87        return false;
88    }
89
90    if (isOpen())
91        m_openingThread = currentThread();
92    else
93        m_openErrorMessage = "sqlite_open returned null";
94
95    if (!SQLiteStatement(*this, "PRAGMA temp_store = MEMORY;").executeCommand())
96        WTF_LOG_ERROR("SQLite database could not set temp_store to memory");
97
98    return isOpen();
99}
100
101void SQLiteDatabase::close()
102{
103    if (m_db) {
104        // FIXME: This is being called on the main thread during JS GC. <rdar://problem/5739818>
105        // ASSERT(currentThread() == m_openingThread);
106        sqlite3* db = m_db;
107        {
108            MutexLocker locker(m_databaseClosingMutex);
109            m_db = 0;
110        }
111        sqlite3_close(db);
112    }
113
114    m_openingThread = 0;
115    m_openError = SQLITE_ERROR;
116    m_openErrorMessage = CString();
117}
118
119void SQLiteDatabase::interrupt()
120{
121    m_interrupted = true;
122    while (!m_lockingMutex.tryLock()) {
123        MutexLocker locker(m_databaseClosingMutex);
124        if (!m_db)
125            return;
126        sqlite3_interrupt(m_db);
127        yield();
128    }
129
130    m_lockingMutex.unlock();
131}
132
133bool SQLiteDatabase::isInterrupted()
134{
135    ASSERT(!m_lockingMutex.tryLock());
136    return m_interrupted;
137}
138
139void SQLiteDatabase::setFullsync(bool fsync)
140{
141    if (fsync)
142        executeCommand("PRAGMA fullfsync = 1;");
143    else
144        executeCommand("PRAGMA fullfsync = 0;");
145}
146
147int64_t SQLiteDatabase::maximumSize()
148{
149    int64_t maxPageCount = 0;
150
151    {
152        MutexLocker locker(m_authorizerLock);
153        enableAuthorizer(false);
154        SQLiteStatement statement(*this, "PRAGMA max_page_count");
155        maxPageCount = statement.getColumnInt64(0);
156        enableAuthorizer(true);
157    }
158
159    return maxPageCount * pageSize();
160}
161
162void SQLiteDatabase::setMaximumSize(int64_t size)
163{
164    if (size < 0)
165        size = 0;
166
167    int currentPageSize = pageSize();
168
169    ASSERT(currentPageSize || !m_db);
170    int64_t newMaxPageCount = currentPageSize ? size / currentPageSize : 0;
171
172    MutexLocker locker(m_authorizerLock);
173    enableAuthorizer(false);
174
175    SQLiteStatement statement(*this, "PRAGMA max_page_count = " + String::number(newMaxPageCount));
176    statement.prepare();
177    if (statement.step() != SQLResultRow)
178#if OS(WIN)
179        WTF_LOG_ERROR("Failed to set maximum size of database to %I64i bytes", static_cast<long long>(size));
180#else
181        WTF_LOG_ERROR("Failed to set maximum size of database to %lli bytes", static_cast<long long>(size));
182#endif
183
184    enableAuthorizer(true);
185
186}
187
188int SQLiteDatabase::pageSize()
189{
190    // Since the page size of a database is locked in at creation and therefore cannot be dynamic,
191    // we can cache the value for future use
192    if (m_pageSize == -1) {
193        MutexLocker locker(m_authorizerLock);
194        enableAuthorizer(false);
195
196        SQLiteStatement statement(*this, "PRAGMA page_size");
197        m_pageSize = statement.getColumnInt(0);
198
199        enableAuthorizer(true);
200    }
201
202    return m_pageSize;
203}
204
205int64_t SQLiteDatabase::freeSpaceSize()
206{
207    int64_t freelistCount = 0;
208
209    {
210        MutexLocker locker(m_authorizerLock);
211        enableAuthorizer(false);
212        // Note: freelist_count was added in SQLite 3.4.1.
213        SQLiteStatement statement(*this, "PRAGMA freelist_count");
214        freelistCount = statement.getColumnInt64(0);
215        enableAuthorizer(true);
216    }
217
218    return freelistCount * pageSize();
219}
220
221int64_t SQLiteDatabase::totalSize()
222{
223    int64_t pageCount = 0;
224
225    {
226        MutexLocker locker(m_authorizerLock);
227        enableAuthorizer(false);
228        SQLiteStatement statement(*this, "PRAGMA page_count");
229        pageCount = statement.getColumnInt64(0);
230        enableAuthorizer(true);
231    }
232
233    return pageCount * pageSize();
234}
235
236void SQLiteDatabase::setSynchronous(SynchronousPragma sync)
237{
238    executeCommand("PRAGMA synchronous = " + String::number(sync));
239}
240
241void SQLiteDatabase::setBusyTimeout(int ms)
242{
243    if (m_db)
244        sqlite3_busy_timeout(m_db, ms);
245    else
246        WTF_LOG(SQLDatabase, "BusyTimeout set on non-open database");
247}
248
249void SQLiteDatabase::setBusyHandler(int(*handler)(void*, int))
250{
251    if (m_db)
252        sqlite3_busy_handler(m_db, handler, NULL);
253    else
254        WTF_LOG(SQLDatabase, "Busy handler set on non-open database");
255}
256
257bool SQLiteDatabase::executeCommand(const String& sql)
258{
259    return SQLiteStatement(*this, sql).executeCommand();
260}
261
262bool SQLiteDatabase::returnsAtLeastOneResult(const String& sql)
263{
264    return SQLiteStatement(*this, sql).returnsAtLeastOneResult();
265}
266
267bool SQLiteDatabase::tableExists(const String& tablename)
268{
269    if (!isOpen())
270        return false;
271
272    String statement = "SELECT name FROM sqlite_master WHERE type = 'table' AND name = '" + tablename + "';";
273
274    SQLiteStatement sql(*this, statement);
275    sql.prepare();
276    return sql.step() == SQLITE_ROW;
277}
278
279void SQLiteDatabase::clearAllTables()
280{
281    String query = "SELECT name FROM sqlite_master WHERE type='table';";
282    Vector<String> tables;
283    if (!SQLiteStatement(*this, query).returnTextResults(0, tables)) {
284        WTF_LOG(SQLDatabase, "Unable to retrieve list of tables from database");
285        return;
286    }
287
288    for (Vector<String>::iterator table = tables.begin(); table != tables.end(); ++table ) {
289        if (*table == "sqlite_sequence")
290            continue;
291        if (!executeCommand("DROP TABLE " + *table))
292            WTF_LOG(SQLDatabase, "Unable to drop table %s", (*table).ascii().data());
293    }
294}
295
296int SQLiteDatabase::runVacuumCommand()
297{
298    if (!executeCommand("VACUUM;"))
299        WTF_LOG(SQLDatabase, "Unable to vacuum database - %s", lastErrorMsg());
300    return lastError();
301}
302
303int SQLiteDatabase::runIncrementalVacuumCommand()
304{
305    MutexLocker locker(m_authorizerLock);
306    enableAuthorizer(false);
307
308    if (!executeCommand("PRAGMA incremental_vacuum"))
309        WTF_LOG(SQLDatabase, "Unable to run incremental vacuum - %s", lastErrorMsg());
310
311    enableAuthorizer(true);
312    return lastError();
313}
314
315int64_t SQLiteDatabase::lastInsertRowID()
316{
317    if (!m_db)
318        return 0;
319    return sqlite3_last_insert_rowid(m_db);
320}
321
322void SQLiteDatabase::updateLastChangesCount()
323{
324    if (!m_db)
325        return;
326
327    m_lastChangesCount = sqlite3_total_changes(m_db);
328}
329
330int SQLiteDatabase::lastChanges()
331{
332    if (!m_db)
333        return 0;
334
335    return sqlite3_total_changes(m_db) - m_lastChangesCount;
336}
337
338int SQLiteDatabase::lastError()
339{
340    return m_db ? sqlite3_errcode(m_db) : m_openError;
341}
342
343const char* SQLiteDatabase::lastErrorMsg()
344{
345    if (m_db)
346        return sqlite3_errmsg(m_db);
347    return m_openErrorMessage.isNull() ? notOpenErrorMessage : m_openErrorMessage.data();
348}
349
350#ifndef NDEBUG
351void SQLiteDatabase::disableThreadingChecks()
352{
353    // This doesn't guarantee that SQList was compiled with -DTHREADSAFE, or that you haven't turned off the mutexes.
354#if SQLITE_VERSION_NUMBER >= 3003001
355    m_sharable = true;
356#else
357    ASSERT(0); // Your SQLite doesn't support sharing handles across threads.
358#endif
359}
360#endif
361
362int SQLiteDatabase::authorizerFunction(void* userData, int actionCode, const char* parameter1, const char* parameter2, const char* /*databaseName*/, const char* /*trigger_or_view*/)
363{
364    DatabaseAuthorizer* auth = static_cast<DatabaseAuthorizer*>(userData);
365    ASSERT(auth);
366
367    switch (actionCode) {
368        case SQLITE_CREATE_INDEX:
369            return auth->createIndex(parameter1, parameter2);
370        case SQLITE_CREATE_TABLE:
371            return auth->createTable(parameter1);
372        case SQLITE_CREATE_TEMP_INDEX:
373            return auth->createTempIndex(parameter1, parameter2);
374        case SQLITE_CREATE_TEMP_TABLE:
375            return auth->createTempTable(parameter1);
376        case SQLITE_CREATE_TEMP_TRIGGER:
377            return auth->createTempTrigger(parameter1, parameter2);
378        case SQLITE_CREATE_TEMP_VIEW:
379            return auth->createTempView(parameter1);
380        case SQLITE_CREATE_TRIGGER:
381            return auth->createTrigger(parameter1, parameter2);
382        case SQLITE_CREATE_VIEW:
383            return auth->createView(parameter1);
384        case SQLITE_DELETE:
385            return auth->allowDelete(parameter1);
386        case SQLITE_DROP_INDEX:
387            return auth->dropIndex(parameter1, parameter2);
388        case SQLITE_DROP_TABLE:
389            return auth->dropTable(parameter1);
390        case SQLITE_DROP_TEMP_INDEX:
391            return auth->dropTempIndex(parameter1, parameter2);
392        case SQLITE_DROP_TEMP_TABLE:
393            return auth->dropTempTable(parameter1);
394        case SQLITE_DROP_TEMP_TRIGGER:
395            return auth->dropTempTrigger(parameter1, parameter2);
396        case SQLITE_DROP_TEMP_VIEW:
397            return auth->dropTempView(parameter1);
398        case SQLITE_DROP_TRIGGER:
399            return auth->dropTrigger(parameter1, parameter2);
400        case SQLITE_DROP_VIEW:
401            return auth->dropView(parameter1);
402        case SQLITE_INSERT:
403            return auth->allowInsert(parameter1);
404        case SQLITE_PRAGMA:
405            return auth->allowPragma(parameter1, parameter2);
406        case SQLITE_READ:
407            return auth->allowRead(parameter1, parameter2);
408        case SQLITE_SELECT:
409            return auth->allowSelect();
410        case SQLITE_TRANSACTION:
411            return auth->allowTransaction();
412        case SQLITE_UPDATE:
413            return auth->allowUpdate(parameter1, parameter2);
414        case SQLITE_ATTACH:
415            return auth->allowAttach(parameter1);
416        case SQLITE_DETACH:
417            return auth->allowDetach(parameter1);
418        case SQLITE_ALTER_TABLE:
419            return auth->allowAlterTable(parameter1, parameter2);
420        case SQLITE_REINDEX:
421            return auth->allowReindex(parameter1);
422#if SQLITE_VERSION_NUMBER >= 3003013
423        case SQLITE_ANALYZE:
424            return auth->allowAnalyze(parameter1);
425        case SQLITE_CREATE_VTABLE:
426            return auth->createVTable(parameter1, parameter2);
427        case SQLITE_DROP_VTABLE:
428            return auth->dropVTable(parameter1, parameter2);
429        case SQLITE_FUNCTION:
430            return auth->allowFunction(parameter2);
431#endif
432        default:
433            ASSERT_NOT_REACHED();
434            return SQLAuthDeny;
435    }
436}
437
438void SQLiteDatabase::setAuthorizer(PassRefPtr<DatabaseAuthorizer> auth)
439{
440    if (!m_db) {
441        WTF_LOG_ERROR("Attempt to set an authorizer on a non-open SQL database");
442        ASSERT_NOT_REACHED();
443        return;
444    }
445
446    MutexLocker locker(m_authorizerLock);
447
448    m_authorizer = auth;
449
450    enableAuthorizer(true);
451}
452
453void SQLiteDatabase::enableAuthorizer(bool enable)
454{
455    if (m_authorizer && enable)
456        sqlite3_set_authorizer(m_db, SQLiteDatabase::authorizerFunction, m_authorizer.get());
457    else
458        sqlite3_set_authorizer(m_db, NULL, 0);
459}
460
461bool SQLiteDatabase::isAutoCommitOn() const
462{
463    return sqlite3_get_autocommit(m_db);
464}
465
466bool SQLiteDatabase::turnOnIncrementalAutoVacuum()
467{
468    SQLiteStatement statement(*this, "PRAGMA auto_vacuum");
469    int autoVacuumMode = statement.getColumnInt(0);
470    int error = lastError();
471
472    // Check if we got an error while trying to get the value of the auto_vacuum flag.
473    // If we got a SQLITE_BUSY error, then there's probably another transaction in
474    // progress on this database. In this case, keep the current value of the
475    // auto_vacuum flag and try to set it to INCREMENTAL the next time we open this
476    // database. If the error is not SQLITE_BUSY, then we probably ran into a more
477    // serious problem and should return false (to log an error message).
478    if (error != SQLITE_ROW)
479        return false;
480
481    switch (autoVacuumMode) {
482    case AutoVacuumIncremental:
483        return true;
484    case AutoVacuumFull:
485        return executeCommand("PRAGMA auto_vacuum = 2");
486    case AutoVacuumNone:
487    default:
488        if (!executeCommand("PRAGMA auto_vacuum = 2"))
489            return false;
490        runVacuumCommand();
491        error = lastError();
492        return (error == SQLITE_OK);
493    }
494}
495
496} // namespace WebCore
497