Home | History | Annotate | Download | only in sqlite
      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 "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 
     36 namespace blink {
     37 
     38 const int SQLResultDone = SQLITE_DONE;
     39 const int SQLResultOk = SQLITE_OK;
     40 const int SQLResultRow = SQLITE_ROW;
     41 const int SQLResultFull = SQLITE_FULL;
     42 const int SQLResultInterrupt = SQLITE_INTERRUPT;
     43 const int SQLResultConstraint = SQLITE_CONSTRAINT;
     44 
     45 static const char notOpenErrorMessage[] = "database is not open";
     46 
     47 SQLiteDatabase::SQLiteDatabase()
     48     : m_db(0)
     49     , m_pageSize(-1)
     50     , m_transactionInProgress(false)
     51     , m_sharable(false)
     52     , m_openingThread(0)
     53     , m_openError(SQLITE_ERROR)
     54     , m_openErrorMessage()
     55     , m_lastChangesCount(0)
     56 {
     57 }
     58 
     59 SQLiteDatabase::~SQLiteDatabase()
     60 {
     61     close();
     62 }
     63 
     64 bool SQLiteDatabase::open(const String& filename, bool forWebSQLDatabase)
     65 {
     66     close();
     67 
     68     m_openError = SQLiteFileSystem::openDatabase(filename, &m_db, forWebSQLDatabase);
     69     if (m_openError != SQLITE_OK) {
     70         m_openErrorMessage = m_db ? sqlite3_errmsg(m_db) : "sqlite_open returned null";
     71         WTF_LOG_ERROR("SQLite database failed to load from %s\nCause - %s", filename.ascii().data(),
     72             m_openErrorMessage.data());
     73         sqlite3_close(m_db);
     74         m_db = 0;
     75         return false;
     76     }
     77 
     78     m_openError = sqlite3_extended_result_codes(m_db, 1);
     79     if (m_openError != SQLITE_OK) {
     80         m_openErrorMessage = sqlite3_errmsg(m_db);
     81         WTF_LOG_ERROR("SQLite database error when enabling extended errors - %s", m_openErrorMessage.data());
     82         sqlite3_close(m_db);
     83         m_db = 0;
     84         return false;
     85     }
     86 
     87     if (isOpen())
     88         m_openingThread = currentThread();
     89     else
     90         m_openErrorMessage = "sqlite_open returned null";
     91 
     92     if (!SQLiteStatement(*this, "PRAGMA temp_store = MEMORY;").executeCommand())
     93         WTF_LOG_ERROR("SQLite database could not set temp_store to memory");
     94 
     95     return isOpen();
     96 }
     97 
     98 void SQLiteDatabase::close()
     99 {
    100     if (m_db) {
    101         // FIXME: This is being called on the main thread during JS GC. <rdar://problem/5739818>
    102         // ASSERT(currentThread() == m_openingThread);
    103         sqlite3* db = m_db;
    104         {
    105             MutexLocker locker(m_databaseClosingMutex);
    106             m_db = 0;
    107         }
    108         sqlite3_close(db);
    109     }
    110 
    111     m_openingThread = 0;
    112     m_openError = SQLITE_ERROR;
    113     m_openErrorMessage = CString();
    114 }
    115 
    116 void SQLiteDatabase::setMaximumSize(int64_t size)
    117 {
    118     if (size < 0)
    119         size = 0;
    120 
    121     int currentPageSize = pageSize();
    122 
    123     ASSERT(currentPageSize || !m_db);
    124     int64_t newMaxPageCount = currentPageSize ? size / currentPageSize : 0;
    125 
    126     MutexLocker locker(m_authorizerLock);
    127     enableAuthorizer(false);
    128 
    129     SQLiteStatement statement(*this, "PRAGMA max_page_count = " + String::number(newMaxPageCount));
    130     statement.prepare();
    131     if (statement.step() != SQLResultRow)
    132 #if OS(WIN)
    133         WTF_LOG_ERROR("Failed to set maximum size of database to %I64i bytes", static_cast<long long>(size));
    134 #else
    135         WTF_LOG_ERROR("Failed to set maximum size of database to %lli bytes", static_cast<long long>(size));
    136 #endif
    137 
    138     enableAuthorizer(true);
    139 
    140 }
    141 
    142 int SQLiteDatabase::pageSize()
    143 {
    144     // Since the page size of a database is locked in at creation and therefore cannot be dynamic,
    145     // we can cache the value for future use
    146     if (m_pageSize == -1) {
    147         MutexLocker locker(m_authorizerLock);
    148         enableAuthorizer(false);
    149 
    150         SQLiteStatement statement(*this, "PRAGMA page_size");
    151         m_pageSize = statement.getColumnInt(0);
    152 
    153         enableAuthorizer(true);
    154     }
    155 
    156     return m_pageSize;
    157 }
    158 
    159 int64_t SQLiteDatabase::freeSpaceSize()
    160 {
    161     int64_t freelistCount = 0;
    162 
    163     {
    164         MutexLocker locker(m_authorizerLock);
    165         enableAuthorizer(false);
    166         // Note: freelist_count was added in SQLite 3.4.1.
    167         SQLiteStatement statement(*this, "PRAGMA freelist_count");
    168         freelistCount = statement.getColumnInt64(0);
    169         enableAuthorizer(true);
    170     }
    171 
    172     return freelistCount * pageSize();
    173 }
    174 
    175 int64_t SQLiteDatabase::totalSize()
    176 {
    177     int64_t pageCount = 0;
    178 
    179     {
    180         MutexLocker locker(m_authorizerLock);
    181         enableAuthorizer(false);
    182         SQLiteStatement statement(*this, "PRAGMA page_count");
    183         pageCount = statement.getColumnInt64(0);
    184         enableAuthorizer(true);
    185     }
    186 
    187     return pageCount * pageSize();
    188 }
    189 
    190 void SQLiteDatabase::setBusyTimeout(int ms)
    191 {
    192     if (m_db)
    193         sqlite3_busy_timeout(m_db, ms);
    194     else
    195         WTF_LOG(SQLDatabase, "BusyTimeout set on non-open database");
    196 }
    197 
    198 bool SQLiteDatabase::executeCommand(const String& sql)
    199 {
    200     return SQLiteStatement(*this, sql).executeCommand();
    201 }
    202 
    203 bool SQLiteDatabase::tableExists(const String& tablename)
    204 {
    205     if (!isOpen())
    206         return false;
    207 
    208     String statement = "SELECT name FROM sqlite_master WHERE type = 'table' AND name = '" + tablename + "';";
    209 
    210     SQLiteStatement sql(*this, statement);
    211     sql.prepare();
    212     return sql.step() == SQLITE_ROW;
    213 }
    214 
    215 int SQLiteDatabase::runVacuumCommand()
    216 {
    217     if (!executeCommand("VACUUM;"))
    218         WTF_LOG(SQLDatabase, "Unable to vacuum database - %s", lastErrorMsg());
    219     return lastError();
    220 }
    221 
    222 int SQLiteDatabase::runIncrementalVacuumCommand()
    223 {
    224     MutexLocker locker(m_authorizerLock);
    225     enableAuthorizer(false);
    226 
    227     if (!executeCommand("PRAGMA incremental_vacuum"))
    228         WTF_LOG(SQLDatabase, "Unable to run incremental vacuum - %s", lastErrorMsg());
    229 
    230     enableAuthorizer(true);
    231     return lastError();
    232 }
    233 
    234 int64_t SQLiteDatabase::lastInsertRowID()
    235 {
    236     if (!m_db)
    237         return 0;
    238     return sqlite3_last_insert_rowid(m_db);
    239 }
    240 
    241 void SQLiteDatabase::updateLastChangesCount()
    242 {
    243     if (!m_db)
    244         return;
    245 
    246     m_lastChangesCount = sqlite3_total_changes(m_db);
    247 }
    248 
    249 int SQLiteDatabase::lastChanges()
    250 {
    251     if (!m_db)
    252         return 0;
    253 
    254     return sqlite3_total_changes(m_db) - m_lastChangesCount;
    255 }
    256 
    257 int SQLiteDatabase::lastError()
    258 {
    259     return m_db ? sqlite3_errcode(m_db) : m_openError;
    260 }
    261 
    262 const char* SQLiteDatabase::lastErrorMsg()
    263 {
    264     if (m_db)
    265         return sqlite3_errmsg(m_db);
    266     return m_openErrorMessage.isNull() ? notOpenErrorMessage : m_openErrorMessage.data();
    267 }
    268 
    269 int SQLiteDatabase::authorizerFunction(void* userData, int actionCode, const char* parameter1, const char* parameter2, const char* /*databaseName*/, const char* /*trigger_or_view*/)
    270 {
    271     DatabaseAuthorizer* auth = static_cast<DatabaseAuthorizer*>(userData);
    272     ASSERT(auth);
    273 
    274     switch (actionCode) {
    275         case SQLITE_CREATE_INDEX:
    276             return auth->createIndex(parameter1, parameter2);
    277         case SQLITE_CREATE_TABLE:
    278             return auth->createTable(parameter1);
    279         case SQLITE_CREATE_TEMP_INDEX:
    280             return auth->createTempIndex(parameter1, parameter2);
    281         case SQLITE_CREATE_TEMP_TABLE:
    282             return auth->createTempTable(parameter1);
    283         case SQLITE_CREATE_TEMP_TRIGGER:
    284             return auth->createTempTrigger(parameter1, parameter2);
    285         case SQLITE_CREATE_TEMP_VIEW:
    286             return auth->createTempView(parameter1);
    287         case SQLITE_CREATE_TRIGGER:
    288             return auth->createTrigger(parameter1, parameter2);
    289         case SQLITE_CREATE_VIEW:
    290             return auth->createView(parameter1);
    291         case SQLITE_DELETE:
    292             return auth->allowDelete(parameter1);
    293         case SQLITE_DROP_INDEX:
    294             return auth->dropIndex(parameter1, parameter2);
    295         case SQLITE_DROP_TABLE:
    296             return auth->dropTable(parameter1);
    297         case SQLITE_DROP_TEMP_INDEX:
    298             return auth->dropTempIndex(parameter1, parameter2);
    299         case SQLITE_DROP_TEMP_TABLE:
    300             return auth->dropTempTable(parameter1);
    301         case SQLITE_DROP_TEMP_TRIGGER:
    302             return auth->dropTempTrigger(parameter1, parameter2);
    303         case SQLITE_DROP_TEMP_VIEW:
    304             return auth->dropTempView(parameter1);
    305         case SQLITE_DROP_TRIGGER:
    306             return auth->dropTrigger(parameter1, parameter2);
    307         case SQLITE_DROP_VIEW:
    308             return auth->dropView(parameter1);
    309         case SQLITE_INSERT:
    310             return auth->allowInsert(parameter1);
    311         case SQLITE_PRAGMA:
    312             return auth->allowPragma(parameter1, parameter2);
    313         case SQLITE_READ:
    314             return auth->allowRead(parameter1, parameter2);
    315         case SQLITE_SELECT:
    316             return auth->allowSelect();
    317         case SQLITE_TRANSACTION:
    318             return auth->allowTransaction();
    319         case SQLITE_UPDATE:
    320             return auth->allowUpdate(parameter1, parameter2);
    321         case SQLITE_ATTACH:
    322             return auth->allowAttach(parameter1);
    323         case SQLITE_DETACH:
    324             return auth->allowDetach(parameter1);
    325         case SQLITE_ALTER_TABLE:
    326             return auth->allowAlterTable(parameter1, parameter2);
    327         case SQLITE_REINDEX:
    328             return auth->allowReindex(parameter1);
    329 #if SQLITE_VERSION_NUMBER >= 3003013
    330         case SQLITE_ANALYZE:
    331             return auth->allowAnalyze(parameter1);
    332         case SQLITE_CREATE_VTABLE:
    333             return auth->createVTable(parameter1, parameter2);
    334         case SQLITE_DROP_VTABLE:
    335             return auth->dropVTable(parameter1, parameter2);
    336         case SQLITE_FUNCTION:
    337             return auth->allowFunction(parameter2);
    338 #endif
    339         default:
    340             ASSERT_NOT_REACHED();
    341             return SQLAuthDeny;
    342     }
    343 }
    344 
    345 void SQLiteDatabase::setAuthorizer(DatabaseAuthorizer* auth)
    346 {
    347     if (!m_db) {
    348         WTF_LOG_ERROR("Attempt to set an authorizer on a non-open SQL database");
    349         ASSERT_NOT_REACHED();
    350         return;
    351     }
    352 
    353     MutexLocker locker(m_authorizerLock);
    354 
    355     m_authorizer = auth;
    356 
    357     enableAuthorizer(true);
    358 }
    359 
    360 void SQLiteDatabase::enableAuthorizer(bool enable)
    361 {
    362     if (m_authorizer && enable)
    363         sqlite3_set_authorizer(m_db, SQLiteDatabase::authorizerFunction, m_authorizer.get());
    364     else
    365         sqlite3_set_authorizer(m_db, NULL, 0);
    366 }
    367 
    368 bool SQLiteDatabase::isAutoCommitOn() const
    369 {
    370     return sqlite3_get_autocommit(m_db);
    371 }
    372 
    373 bool SQLiteDatabase::turnOnIncrementalAutoVacuum()
    374 {
    375     SQLiteStatement statement(*this, "PRAGMA auto_vacuum");
    376     int autoVacuumMode = statement.getColumnInt(0);
    377     int error = lastError();
    378 
    379     // Check if we got an error while trying to get the value of the auto_vacuum flag.
    380     // If we got a SQLITE_BUSY error, then there's probably another transaction in
    381     // progress on this database. In this case, keep the current value of the
    382     // auto_vacuum flag and try to set it to INCREMENTAL the next time we open this
    383     // database. If the error is not SQLITE_BUSY, then we probably ran into a more
    384     // serious problem and should return false (to log an error message).
    385     if (error != SQLITE_ROW)
    386         return false;
    387 
    388     switch (autoVacuumMode) {
    389     case AutoVacuumIncremental:
    390         return true;
    391     case AutoVacuumFull:
    392         return executeCommand("PRAGMA auto_vacuum = 2");
    393     case AutoVacuumNone:
    394     default:
    395         if (!executeCommand("PRAGMA auto_vacuum = 2"))
    396             return false;
    397         runVacuumCommand();
    398         error = lastError();
    399         return (error == SQLITE_OK);
    400     }
    401 }
    402 
    403 void SQLiteDatabase::trace(Visitor* visitor)
    404 {
    405     visitor->trace(m_authorizer);
    406 }
    407 
    408 } // namespace blink
    409