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