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