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