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 "SQLiteDatabase.h"
     29 
     30 #include "DatabaseAuthorizer.h"
     31 #include "Logging.h"
     32 #include "SQLiteFileSystem.h"
     33 #include "SQLiteStatement.h"
     34 
     35 #include <sqlite3.h>
     36 
     37 namespace WebCore {
     38 
     39 const int SQLResultDone = SQLITE_DONE;
     40 const int SQLResultError = SQLITE_ERROR;
     41 const int SQLResultOk = SQLITE_OK;
     42 const int SQLResultRow = SQLITE_ROW;
     43 const int SQLResultSchema = SQLITE_SCHEMA;
     44 const int SQLResultFull = SQLITE_FULL;
     45 
     46 
     47 SQLiteDatabase::SQLiteDatabase()
     48     : m_db(0)
     49     , m_pageSize(-1)
     50     , m_transactionInProgress(false)
     51     , m_openingThread(0)
     52 {
     53 }
     54 
     55 SQLiteDatabase::~SQLiteDatabase()
     56 {
     57     close();
     58 }
     59 
     60 bool SQLiteDatabase::open(const String& filename)
     61 {
     62     close();
     63 
     64     m_lastError = SQLiteFileSystem::openDatabase(filename, &m_db);
     65     if (m_lastError != SQLITE_OK) {
     66         LOG_ERROR("SQLite database failed to load from %s\nCause - %s", filename.ascii().data(),
     67             sqlite3_errmsg(m_db));
     68         sqlite3_close(m_db);
     69         m_db = 0;
     70         return false;
     71     }
     72 
     73     if (isOpen())
     74         m_openingThread = currentThread();
     75 
     76     if (!SQLiteStatement(*this, "PRAGMA temp_store = MEMORY;").executeCommand())
     77         LOG_ERROR("SQLite database could not set temp_store to memory");
     78 
     79     return isOpen();
     80 }
     81 
     82 void SQLiteDatabase::close()
     83 {
     84     if (m_db) {
     85         // FIXME: This is being called on themain thread during JS GC. <rdar://problem/5739818>
     86         // ASSERT(currentThread() == m_openingThread);
     87         sqlite3_close(m_db);
     88         m_db = 0;
     89     }
     90 
     91     m_openingThread = 0;
     92 }
     93 
     94 void SQLiteDatabase::setFullsync(bool fsync)
     95 {
     96     if (fsync)
     97         executeCommand("PRAGMA fullfsync = 1;");
     98     else
     99         executeCommand("PRAGMA fullfsync = 0;");
    100 }
    101 
    102 int64_t SQLiteDatabase::maximumSize()
    103 {
    104     MutexLocker locker(m_authorizerLock);
    105     enableAuthorizer(false);
    106 
    107     SQLiteStatement statement(*this, "PRAGMA max_page_count");
    108     int64_t size = statement.getColumnInt64(0) * pageSize();
    109 
    110     enableAuthorizer(true);
    111     return size;
    112 }
    113 
    114 void SQLiteDatabase::setMaximumSize(int64_t size)
    115 {
    116     if (size < 0)
    117         size = 0;
    118 
    119     int currentPageSize = pageSize();
    120 
    121     ASSERT(currentPageSize);
    122     int64_t newMaxPageCount = currentPageSize ? size / currentPageSize : 0;
    123 
    124     MutexLocker locker(m_authorizerLock);
    125     enableAuthorizer(false);
    126 
    127     SQLiteStatement statement(*this, "PRAGMA max_page_count = " + String::number(newMaxPageCount));
    128     statement.prepare();
    129     if (statement.step() != SQLResultRow)
    130         LOG_ERROR("Failed to set maximum size of database to %lli bytes", size);
    131 
    132     enableAuthorizer(true);
    133 
    134 }
    135 
    136 int SQLiteDatabase::pageSize()
    137 {
    138     // Since the page size of a database is locked in at creation and therefore cannot be dynamic,
    139     // we can cache the value for future use
    140     if (m_pageSize == -1) {
    141         MutexLocker locker(m_authorizerLock);
    142         enableAuthorizer(false);
    143 
    144         SQLiteStatement statement(*this, "PRAGMA page_size");
    145         m_pageSize = statement.getColumnInt(0);
    146 
    147         enableAuthorizer(true);
    148     }
    149 
    150     return m_pageSize;
    151 }
    152 
    153 int64_t SQLiteDatabase::freeSpaceSize()
    154 {
    155     MutexLocker locker(m_authorizerLock);
    156     enableAuthorizer(false);
    157     // Note: freelist_count was added in SQLite 3.4.1.
    158     SQLiteStatement statement(*this, "PRAGMA freelist_count");
    159     int64_t size = statement.getColumnInt64(0) * pageSize();
    160 
    161     enableAuthorizer(true);
    162     return size;
    163 }
    164 
    165 void SQLiteDatabase::setSynchronous(SynchronousPragma sync)
    166 {
    167     executeCommand(String::format("PRAGMA synchronous = %i", sync));
    168 }
    169 
    170 void SQLiteDatabase::setBusyTimeout(int ms)
    171 {
    172     if (m_db)
    173         sqlite3_busy_timeout(m_db, ms);
    174     else
    175         LOG(SQLDatabase, "BusyTimeout set on non-open database");
    176 }
    177 
    178 void SQLiteDatabase::setBusyHandler(int(*handler)(void*, int))
    179 {
    180     if (m_db)
    181         sqlite3_busy_handler(m_db, handler, NULL);
    182     else
    183         LOG(SQLDatabase, "Busy handler set on non-open database");
    184 }
    185 
    186 bool SQLiteDatabase::executeCommand(const String& sql)
    187 {
    188     return SQLiteStatement(*this, sql).executeCommand();
    189 }
    190 
    191 bool SQLiteDatabase::returnsAtLeastOneResult(const String& sql)
    192 {
    193     return SQLiteStatement(*this, sql).returnsAtLeastOneResult();
    194 }
    195 
    196 bool SQLiteDatabase::tableExists(const String& tablename)
    197 {
    198     if (!isOpen())
    199         return false;
    200 
    201     String statement = "SELECT name FROM sqlite_master WHERE type = 'table' AND name = '" + tablename + "';";
    202 
    203     SQLiteStatement sql(*this, statement);
    204     sql.prepare();
    205     return sql.step() == SQLITE_ROW;
    206 }
    207 
    208 void SQLiteDatabase::clearAllTables()
    209 {
    210     String query = "SELECT name FROM sqlite_master WHERE type='table';";
    211     Vector<String> tables;
    212     if (!SQLiteStatement(*this, query).returnTextResults(0, tables)) {
    213         LOG(SQLDatabase, "Unable to retrieve list of tables from database");
    214         return;
    215     }
    216 
    217     for (Vector<String>::iterator table = tables.begin(); table != tables.end(); ++table ) {
    218         if (*table == "sqlite_sequence")
    219             continue;
    220         if (!executeCommand("DROP TABLE " + *table))
    221             LOG(SQLDatabase, "Unable to drop table %s", (*table).ascii().data());
    222     }
    223 }
    224 
    225 void SQLiteDatabase::runVacuumCommand()
    226 {
    227     if (!executeCommand("VACUUM;"))
    228         LOG(SQLDatabase, "Unable to vacuum database - %s", lastErrorMsg());
    229 }
    230 
    231 int64_t SQLiteDatabase::lastInsertRowID()
    232 {
    233     if (!m_db)
    234         return 0;
    235     return sqlite3_last_insert_rowid(m_db);
    236 }
    237 
    238 int SQLiteDatabase::lastChanges()
    239 {
    240     if (!m_db)
    241         return 0;
    242     return sqlite3_changes(m_db);
    243 }
    244 
    245 int SQLiteDatabase::lastError()
    246 {
    247     return m_db ? sqlite3_errcode(m_db) : SQLITE_ERROR;
    248 }
    249 
    250 const char* SQLiteDatabase::lastErrorMsg()
    251 {
    252     return sqlite3_errmsg(m_db);
    253 }
    254 
    255 int SQLiteDatabase::authorizerFunction(void* userData, int actionCode, const char* parameter1, const char* parameter2, const char* /*databaseName*/, const char* /*trigger_or_view*/)
    256 {
    257     DatabaseAuthorizer* auth = static_cast<DatabaseAuthorizer*>(userData);
    258     ASSERT(auth);
    259 
    260     switch (actionCode) {
    261         case SQLITE_CREATE_INDEX:
    262             return auth->createIndex(parameter1, parameter2);
    263         case SQLITE_CREATE_TABLE:
    264             return auth->createTable(parameter1);
    265         case SQLITE_CREATE_TEMP_INDEX:
    266             return auth->createTempIndex(parameter1, parameter2);
    267         case SQLITE_CREATE_TEMP_TABLE:
    268             return auth->createTempTable(parameter1);
    269         case SQLITE_CREATE_TEMP_TRIGGER:
    270             return auth->createTempTrigger(parameter1, parameter2);
    271         case SQLITE_CREATE_TEMP_VIEW:
    272             return auth->createTempView(parameter1);
    273         case SQLITE_CREATE_TRIGGER:
    274             return auth->createTrigger(parameter1, parameter2);
    275         case SQLITE_CREATE_VIEW:
    276             return auth->createView(parameter1);
    277         case SQLITE_DELETE:
    278             return auth->allowDelete(parameter1);
    279         case SQLITE_DROP_INDEX:
    280             return auth->dropIndex(parameter1, parameter2);
    281         case SQLITE_DROP_TABLE:
    282             return auth->dropTable(parameter1);
    283         case SQLITE_DROP_TEMP_INDEX:
    284             return auth->dropTempIndex(parameter1, parameter2);
    285         case SQLITE_DROP_TEMP_TABLE:
    286             return auth->dropTempTable(parameter1);
    287         case SQLITE_DROP_TEMP_TRIGGER:
    288             return auth->dropTempTrigger(parameter1, parameter2);
    289         case SQLITE_DROP_TEMP_VIEW:
    290             return auth->dropTempView(parameter1);
    291         case SQLITE_DROP_TRIGGER:
    292             return auth->dropTrigger(parameter1, parameter2);
    293         case SQLITE_DROP_VIEW:
    294             return auth->dropView(parameter1);
    295         case SQLITE_INSERT:
    296             return auth->allowInsert(parameter1);
    297         case SQLITE_PRAGMA:
    298             return auth->allowPragma(parameter1, parameter2);
    299         case SQLITE_READ:
    300             return auth->allowRead(parameter1, parameter2);
    301         case SQLITE_SELECT:
    302             return auth->allowSelect();
    303         case SQLITE_TRANSACTION:
    304             return auth->allowTransaction();
    305         case SQLITE_UPDATE:
    306             return auth->allowUpdate(parameter1, parameter2);
    307         case SQLITE_ATTACH:
    308             return auth->allowAttach(parameter1);
    309         case SQLITE_DETACH:
    310             return auth->allowDetach(parameter1);
    311         case SQLITE_ALTER_TABLE:
    312             return auth->allowAlterTable(parameter1, parameter2);
    313         case SQLITE_REINDEX:
    314             return auth->allowReindex(parameter1);
    315 #if SQLITE_VERSION_NUMBER >= 3003013
    316         case SQLITE_ANALYZE:
    317             return auth->allowAnalyze(parameter1);
    318         case SQLITE_CREATE_VTABLE:
    319             return auth->createVTable(parameter1, parameter2);
    320         case SQLITE_DROP_VTABLE:
    321             return auth->dropVTable(parameter1, parameter2);
    322         case SQLITE_FUNCTION:
    323             return auth->allowFunction(parameter2);
    324 #endif
    325         default:
    326             ASSERT_NOT_REACHED();
    327             return SQLAuthDeny;
    328     }
    329 }
    330 
    331 void SQLiteDatabase::setAuthorizer(PassRefPtr<DatabaseAuthorizer> auth)
    332 {
    333     if (!m_db) {
    334         LOG_ERROR("Attempt to set an authorizer on a non-open SQL database");
    335         ASSERT_NOT_REACHED();
    336         return;
    337     }
    338 
    339     MutexLocker locker(m_authorizerLock);
    340 
    341     m_authorizer = auth;
    342 
    343     enableAuthorizer(true);
    344 }
    345 
    346 void SQLiteDatabase::enableAuthorizer(bool enable)
    347 {
    348     if (m_authorizer && enable)
    349         sqlite3_set_authorizer(m_db, SQLiteDatabase::authorizerFunction, m_authorizer.get());
    350     else
    351         sqlite3_set_authorizer(m_db, NULL, 0);
    352 }
    353 
    354 void SQLiteDatabase::lock()
    355 {
    356     m_lockingMutex.lock();
    357 }
    358 
    359 void SQLiteDatabase::unlock()
    360 {
    361     m_lockingMutex.unlock();
    362 }
    363 
    364 bool SQLiteDatabase::isAutoCommitOn() const
    365 {
    366     return sqlite3_get_autocommit(m_db);
    367 }
    368 
    369 } // namespace WebCore
    370