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