Home | History | Annotate | Download | only in sql
      1 /*
      2  * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
      3  * Copyright (C) 2007 Justin Haygood (jhaygood (at) 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 
     39 namespace WebCore {
     40 
     41 const int SQLResultDone = SQLITE_DONE;
     42 const int SQLResultError = SQLITE_ERROR;
     43 const int SQLResultOk = SQLITE_OK;
     44 const int SQLResultRow = SQLITE_ROW;
     45 const int SQLResultSchema = SQLITE_SCHEMA;
     46 const int SQLResultFull = SQLITE_FULL;
     47 const int SQLResultInterrupt = SQLITE_INTERRUPT;
     48 const int SQLResultConstraint = SQLITE_CONSTRAINT;
     49 
     50 static const char notOpenErrorMessage[] = "database is not open";
     51 
     52 SQLiteDatabase::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 
     65 SQLiteDatabase::~SQLiteDatabase()
     66 {
     67     close();
     68 }
     69 
     70 bool 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 
    104 void 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 
    122 void 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 
    136 bool SQLiteDatabase::isInterrupted()
    137 {
    138     ASSERT(!m_lockingMutex.tryLock());
    139     return m_interrupted;
    140 }
    141 
    142 void SQLiteDatabase::setFullsync(bool fsync)
    143 {
    144     if (fsync)
    145         executeCommand("PRAGMA fullfsync = 1;");
    146     else
    147         executeCommand("PRAGMA fullfsync = 0;");
    148 }
    149 
    150 int64_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 
    165 void 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 
    191 int 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 
    208 int64_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 
    224 int64_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 
    239 void SQLiteDatabase::setSynchronous(SynchronousPragma sync)
    240 {
    241     executeCommand("PRAGMA synchronous = " + String::number(sync));
    242 }
    243 
    244 void 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 
    252 void 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 
    260 bool SQLiteDatabase::executeCommand(const String& sql)
    261 {
    262     return SQLiteStatement(*this, sql).executeCommand();
    263 }
    264 
    265 bool SQLiteDatabase::returnsAtLeastOneResult(const String& sql)
    266 {
    267     return SQLiteStatement(*this, sql).returnsAtLeastOneResult();
    268 }
    269 
    270 bool 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 
    282 void 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 
    299 int SQLiteDatabase::runVacuumCommand()
    300 {
    301     if (!executeCommand("VACUUM;"))
    302         LOG(SQLDatabase, "Unable to vacuum database - %s", lastErrorMsg());
    303     return lastError();
    304 }
    305 
    306 int 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 
    318 int64_t SQLiteDatabase::lastInsertRowID()
    319 {
    320     if (!m_db)
    321         return 0;
    322     return sqlite3_last_insert_rowid(m_db);
    323 }
    324 
    325 void SQLiteDatabase::updateLastChangesCount()
    326 {
    327     if (!m_db)
    328         return;
    329 
    330     m_lastChangesCount = sqlite3_total_changes(m_db);
    331 }
    332 
    333 int SQLiteDatabase::lastChanges()
    334 {
    335     if (!m_db)
    336         return 0;
    337 
    338     return sqlite3_total_changes(m_db) - m_lastChangesCount;
    339 }
    340 
    341 int SQLiteDatabase::lastError()
    342 {
    343     return m_db ? sqlite3_errcode(m_db) : m_openError;
    344 }
    345 
    346 const 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
    354 void 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 
    365 int 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 
    441 void 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 
    456 void 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 
    464 bool SQLiteDatabase::isAutoCommitOn() const
    465 {
    466     return sqlite3_get_autocommit(m_db);
    467 }
    468 
    469 bool 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