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