| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2010 Google Inc. All rights reserved. | 2 * Copyright (C) 2010 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
| 6 * are met: | 6 * are met: |
| 7 * | 7 * |
| 8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
| (...skipping 16 matching lines...) Expand all Loading... |
| 27 */ | 27 */ |
| 28 | 28 |
| 29 #include "config.h" | 29 #include "config.h" |
| 30 #include "AbstractDatabase.h" | 30 #include "AbstractDatabase.h" |
| 31 | 31 |
| 32 #if ENABLE(DATABASE) | 32 #if ENABLE(DATABASE) |
| 33 #include "DatabaseAuthorizer.h" | 33 #include "DatabaseAuthorizer.h" |
| 34 #include "DatabaseTracker.h" | 34 #include "DatabaseTracker.h" |
| 35 #include "Logging.h" | 35 #include "Logging.h" |
| 36 #include "SQLiteStatement.h" | 36 #include "SQLiteStatement.h" |
| 37 #include "SQLiteTransaction.h" |
| 37 #include "ScriptExecutionContext.h" | 38 #include "ScriptExecutionContext.h" |
| 38 #include "SecurityOrigin.h" | 39 #include "SecurityOrigin.h" |
| 39 #include <wtf/HashMap.h> | 40 #include <wtf/HashMap.h> |
| 40 #include <wtf/HashSet.h> | 41 #include <wtf/HashSet.h> |
| 41 #include <wtf/PassRefPtr.h> | 42 #include <wtf/PassRefPtr.h> |
| 42 #include <wtf/RefPtr.h> | 43 #include <wtf/RefPtr.h> |
| 43 #include <wtf/StdLibExtras.h> | 44 #include <wtf/StdLibExtras.h> |
| 44 #include <wtf/text/CString.h> | 45 #include <wtf/text/CString.h> |
| 45 #include <wtf/text/StringHash.h> | 46 #include <wtf/text/StringHash.h> |
| 46 | 47 |
| (...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 228 if (hashSet->isEmpty()) { | 229 if (hashSet->isEmpty()) { |
| 229 guidToDatabaseMap().remove(m_guid); | 230 guidToDatabaseMap().remove(m_guid); |
| 230 delete hashSet; | 231 delete hashSet; |
| 231 guidToVersionMap().remove(m_guid); | 232 guidToVersionMap().remove(m_guid); |
| 232 } | 233 } |
| 233 } | 234 } |
| 234 } | 235 } |
| 235 | 236 |
| 236 String AbstractDatabase::version() const | 237 String AbstractDatabase::version() const |
| 237 { | 238 { |
| 238 MutexLocker locker(guidMutex()); | 239 // Note: In multi-process browsers the cached value may be accurate, but we
cannot read the |
| 239 return guidToVersionMap().get(m_guid).threadsafeCopy(); | 240 // actual version from the database without potentially inducing a deadlock. |
| 241 // FIXME: Add an async version getter to the DatabaseAPI. |
| 242 return getCachedVersion(); |
| 240 } | 243 } |
| 241 | 244 |
| 242 static const int maxSqliteBusyWaitTime = 30000; | |
| 243 bool AbstractDatabase::performOpenAndVerify(bool shouldSetVersionInNewDatabase,
ExceptionCode& ec) | 245 bool AbstractDatabase::performOpenAndVerify(bool shouldSetVersionInNewDatabase,
ExceptionCode& ec) |
| 244 { | 246 { |
| 247 const int maxSqliteBusyWaitTime = 30000; |
| 248 |
| 245 if (!m_sqliteDatabase.open(m_filename, true)) { | 249 if (!m_sqliteDatabase.open(m_filename, true)) { |
| 246 LOG_ERROR("Unable to open database at path %s", m_filename.ascii().data(
)); | 250 LOG_ERROR("Unable to open database at path %s", m_filename.ascii().data(
)); |
| 247 ec = INVALID_STATE_ERR; | 251 ec = INVALID_STATE_ERR; |
| 248 return false; | 252 return false; |
| 249 } | 253 } |
| 250 if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum()) | 254 if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum()) |
| 251 LOG_ERROR("Unable to turn on incremental auto-vacuum for database %s", m
_filename.ascii().data()); | 255 LOG_ERROR("Unable to turn on incremental auto-vacuum for database %s", m
_filename.ascii().data()); |
| 252 | 256 |
| 253 ASSERT(m_databaseAuthorizer); | |
| 254 m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer); | |
| 255 m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); | 257 m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); |
| 256 | 258 |
| 257 String currentVersion; | 259 String currentVersion; |
| 258 { | 260 { |
| 259 MutexLocker locker(guidMutex()); | 261 MutexLocker locker(guidMutex()); |
| 260 | 262 |
| 261 GuidVersionMap::iterator entry = guidToVersionMap().find(m_guid); | 263 GuidVersionMap::iterator entry = guidToVersionMap().find(m_guid); |
| 262 if (entry != guidToVersionMap().end()) { | 264 if (entry != guidToVersionMap().end()) { |
| 263 // Map null string to empty string (see updateGuidVersionMap()). | 265 // Map null string to empty string (see updateGuidVersionMap()). |
| 264 currentVersion = entry->second.isNull() ? String("") : entry->second
; | 266 currentVersion = entry->second.isNull() ? String("") : entry->second
; |
| 265 LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid,
currentVersion.ascii().data()); | 267 LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid,
currentVersion.ascii().data()); |
| 268 |
| 269 #if PLATFORM(CHROMIUM) |
| 270 // Note: In multi-process browsers the cached value may be inaccurat
e, but |
| 271 // we cannot read the actual version from the database without poten
tially |
| 272 // inducing a form of deadlock, a busytimeout error when trying to |
| 273 // access the database. So we'll use the cached value if we're able
to read |
| 274 // the value without waiting, and otherwise use the cached value (wh
ich may be off). |
| 275 // FIXME: Add an async openDatabase method to the DatabaseAPI. |
| 276 const int noSqliteBusyWaitTime = 0; |
| 277 m_sqliteDatabase.setBusyTimeout(noSqliteBusyWaitTime); |
| 278 String versionFromDatabase; |
| 279 if (getVersionFromDatabase(versionFromDatabase, false)) { |
| 280 currentVersion = versionFromDatabase; |
| 281 updateGuidVersionMap(m_guid, currentVersion); |
| 282 } |
| 283 m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); |
| 284 #endif |
| 266 } else { | 285 } else { |
| 267 LOG(StorageAPI, "No cached version for guid %i", m_guid); | 286 LOG(StorageAPI, "No cached version for guid %i", m_guid); |
| 268 | 287 |
| 288 SQLiteTransaction transaction(m_sqliteDatabase); |
| 289 transaction.begin(); |
| 290 if (!transaction.inProgress()) { |
| 291 LOG_ERROR("Unable to begin transaction while opening %s", databa
seDebugName().ascii().data()); |
| 292 ec = INVALID_STATE_ERR; |
| 293 m_sqliteDatabase.close(); |
| 294 return false; |
| 295 } |
| 296 |
| 269 if (!m_sqliteDatabase.tableExists(databaseInfoTableName())) { | 297 if (!m_sqliteDatabase.tableExists(databaseInfoTableName())) { |
| 270 m_new = true; | 298 m_new = true; |
| 271 | 299 |
| 272 if (!m_sqliteDatabase.executeCommand("CREATE TABLE " + databaseI
nfoTableName() + " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLAC
E,value TEXT NOT NULL ON CONFLICT FAIL);")) { | 300 if (!m_sqliteDatabase.executeCommand("CREATE TABLE " + databaseI
nfoTableName() + " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLAC
E,value TEXT NOT NULL ON CONFLICT FAIL);")) { |
| 273 LOG_ERROR("Unable to create table %s in database %s", databa
seInfoTableName().ascii().data(), databaseDebugName().ascii().data()); | 301 LOG_ERROR("Unable to create table %s in database %s", databa
seInfoTableName().ascii().data(), databaseDebugName().ascii().data()); |
| 274 ec = INVALID_STATE_ERR; | 302 ec = INVALID_STATE_ERR; |
| 275 // Close the handle to the database file. | 303 transaction.rollback(); |
| 276 m_sqliteDatabase.close(); | 304 m_sqliteDatabase.close(); |
| 277 return false; | 305 return false; |
| 278 } | 306 } |
| 279 } | 307 } else if (!getVersionFromDatabase(currentVersion, false)) { |
| 280 | |
| 281 if (!getVersionFromDatabase(currentVersion)) { | |
| 282 LOG_ERROR("Failed to get current version from database %s", data
baseDebugName().ascii().data()); | 308 LOG_ERROR("Failed to get current version from database %s", data
baseDebugName().ascii().data()); |
| 283 ec = INVALID_STATE_ERR; | 309 ec = INVALID_STATE_ERR; |
| 284 // Close the handle to the database file. | 310 transaction.rollback(); |
| 285 m_sqliteDatabase.close(); | 311 m_sqliteDatabase.close(); |
| 286 return false; | 312 return false; |
| 287 } | 313 } |
| 314 |
| 288 if (currentVersion.length()) { | 315 if (currentVersion.length()) { |
| 289 LOG(StorageAPI, "Retrieved current version %s from database %s",
currentVersion.ascii().data(), databaseDebugName().ascii().data()); | 316 LOG(StorageAPI, "Retrieved current version %s from database %s",
currentVersion.ascii().data(), databaseDebugName().ascii().data()); |
| 290 } else if (!m_new || shouldSetVersionInNewDatabase) { | 317 } else if (!m_new || shouldSetVersionInNewDatabase) { |
| 291 LOG(StorageAPI, "Setting version %s in database %s that was just
created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data())
; | 318 LOG(StorageAPI, "Setting version %s in database %s that was just
created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data())
; |
| 292 if (!setVersionInDatabase(m_expectedVersion)) { | 319 if (!setVersionInDatabase(m_expectedVersion, false)) { |
| 293 LOG_ERROR("Failed to set version %s in database %s", m_expec
tedVersion.ascii().data(), databaseDebugName().ascii().data()); | 320 LOG_ERROR("Failed to set version %s in database %s", m_expec
tedVersion.ascii().data(), databaseDebugName().ascii().data()); |
| 294 ec = INVALID_STATE_ERR; | 321 ec = INVALID_STATE_ERR; |
| 295 // Close the handle to the database file. | 322 transaction.rollback(); |
| 296 m_sqliteDatabase.close(); | 323 m_sqliteDatabase.close(); |
| 297 return false; | 324 return false; |
| 298 } | 325 } |
| 299 currentVersion = m_expectedVersion; | 326 currentVersion = m_expectedVersion; |
| 300 } | 327 } |
| 301 | |
| 302 updateGuidVersionMap(m_guid, currentVersion); | 328 updateGuidVersionMap(m_guid, currentVersion); |
| 329 transaction.commit(); |
| 303 } | 330 } |
| 304 } | 331 } |
| 305 | 332 |
| 306 if (currentVersion.isNull()) { | 333 if (currentVersion.isNull()) { |
| 307 LOG(StorageAPI, "Database %s does not have its version set", databaseDeb
ugName().ascii().data()); | 334 LOG(StorageAPI, "Database %s does not have its version set", databaseDeb
ugName().ascii().data()); |
| 308 currentVersion = ""; | 335 currentVersion = ""; |
| 309 } | 336 } |
| 310 | 337 |
| 311 // If the expected version isn't the empty string, ensure that the current d
atabase version we have matches that version. Otherwise, set an exception. | 338 // If the expected version isn't the empty string, ensure that the current d
atabase version we have matches that version. Otherwise, set an exception. |
| 312 // If the expected version is the empty string, then we always return with w
hatever version of the database we have. | 339 // If the expected version is the empty string, then we always return with w
hatever version of the database we have. |
| 313 if ((!m_new || shouldSetVersionInNewDatabase) && m_expectedVersion.length()
&& m_expectedVersion != currentVersion) { | 340 if ((!m_new || shouldSetVersionInNewDatabase) && m_expectedVersion.length()
&& m_expectedVersion != currentVersion) { |
| 314 LOG(StorageAPI, "page expects version %s from database %s, which actuall
y has version name %s - openDatabase() call will fail", m_expectedVersion.ascii(
).data(), | 341 LOG(StorageAPI, "page expects version %s from database %s, which actuall
y has version name %s - openDatabase() call will fail", m_expectedVersion.ascii(
).data(), |
| 315 databaseDebugName().ascii().data(), currentVersion.ascii().data()); | 342 databaseDebugName().ascii().data(), currentVersion.ascii().data()); |
| 316 ec = INVALID_STATE_ERR; | 343 ec = INVALID_STATE_ERR; |
| 317 // Close the handle to the database file. | |
| 318 m_sqliteDatabase.close(); | 344 m_sqliteDatabase.close(); |
| 319 return false; | 345 return false; |
| 320 } | 346 } |
| 321 | 347 |
| 348 ASSERT(m_databaseAuthorizer); |
| 349 m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer); |
| 350 |
| 322 m_opened = true; | 351 m_opened = true; |
| 323 | 352 |
| 353 if (m_new && !shouldSetVersionInNewDatabase) |
| 354 m_expectedVersion = ""; // The caller provided a creationCallback which
will set the expected version. |
| 355 |
| 324 return true; | 356 return true; |
| 325 } | 357 } |
| 326 | 358 |
| 327 ScriptExecutionContext* AbstractDatabase::scriptExecutionContext() const | 359 ScriptExecutionContext* AbstractDatabase::scriptExecutionContext() const |
| 328 { | 360 { |
| 329 return m_scriptExecutionContext.get(); | 361 return m_scriptExecutionContext.get(); |
| 330 } | 362 } |
| 331 | 363 |
| 332 SecurityOrigin* AbstractDatabase::securityOrigin() const | 364 SecurityOrigin* AbstractDatabase::securityOrigin() const |
| 333 { | 365 { |
| (...skipping 23 matching lines...) Expand all Loading... |
| 357 return m_filename.threadsafeCopy(); | 389 return m_filename.threadsafeCopy(); |
| 358 } | 390 } |
| 359 | 391 |
| 360 // static | 392 // static |
| 361 const String& AbstractDatabase::databaseVersionKey() | 393 const String& AbstractDatabase::databaseVersionKey() |
| 362 { | 394 { |
| 363 DEFINE_STATIC_LOCAL(String, key, ("WebKitDatabaseVersionKey")); | 395 DEFINE_STATIC_LOCAL(String, key, ("WebKitDatabaseVersionKey")); |
| 364 return key; | 396 return key; |
| 365 } | 397 } |
| 366 | 398 |
| 367 bool AbstractDatabase::getVersionFromDatabase(String& version) | 399 bool AbstractDatabase::getVersionFromDatabase(String& version, bool shouldCacheV
ersion) |
| 368 { | 400 { |
| 369 DEFINE_STATIC_LOCAL(String, getVersionQuery, ("SELECT value FROM " + databas
eInfoTableName() + " WHERE key = '" + databaseVersionKey() + "';")); | 401 DEFINE_STATIC_LOCAL(String, getVersionQuery, ("SELECT value FROM " + databas
eInfoTableName() + " WHERE key = '" + databaseVersionKey() + "';")); |
| 370 | 402 |
| 371 m_databaseAuthorizer->disable(); | 403 m_databaseAuthorizer->disable(); |
| 372 | 404 |
| 373 bool result = retrieveTextResultFromDatabase(m_sqliteDatabase, getVersionQue
ry.threadsafeCopy(), version); | 405 bool result = retrieveTextResultFromDatabase(m_sqliteDatabase, getVersionQue
ry.threadsafeCopy(), version); |
| 374 if (!result) | 406 if (result) { |
| 407 if (shouldCacheVersion) |
| 408 setCachedVersion(version); |
| 409 } else |
| 375 LOG_ERROR("Failed to retrieve version from database %s", databaseDebugNa
me().ascii().data()); | 410 LOG_ERROR("Failed to retrieve version from database %s", databaseDebugNa
me().ascii().data()); |
| 376 | 411 |
| 377 m_databaseAuthorizer->enable(); | 412 m_databaseAuthorizer->enable(); |
| 378 | 413 |
| 379 return result; | 414 return result; |
| 380 } | 415 } |
| 381 | 416 |
| 382 bool AbstractDatabase::setVersionInDatabase(const String& version) | 417 bool AbstractDatabase::setVersionInDatabase(const String& version, bool shouldCa
cheVersion) |
| 383 { | 418 { |
| 384 // The INSERT will replace an existing entry for the database with the new v
ersion number, due to the UNIQUE ON CONFLICT REPLACE | 419 // The INSERT will replace an existing entry for the database with the new v
ersion number, due to the UNIQUE ON CONFLICT REPLACE |
| 385 // clause in the CREATE statement (see Database::performOpenAndVerify()). | 420 // clause in the CREATE statement (see Database::performOpenAndVerify()). |
| 386 DEFINE_STATIC_LOCAL(String, setVersionQuery, ("INSERT INTO " + databaseInfoT
ableName() + " (key, value) VALUES ('" + databaseVersionKey() + "', ?);")); | 421 DEFINE_STATIC_LOCAL(String, setVersionQuery, ("INSERT INTO " + databaseInfoT
ableName() + " (key, value) VALUES ('" + databaseVersionKey() + "', ?);")); |
| 387 | 422 |
| 388 m_databaseAuthorizer->disable(); | 423 m_databaseAuthorizer->disable(); |
| 389 | 424 |
| 390 bool result = setTextValueInDatabase(m_sqliteDatabase, setVersionQuery.threa
dsafeCopy(), version); | 425 bool result = setTextValueInDatabase(m_sqliteDatabase, setVersionQuery.threa
dsafeCopy(), version); |
| 391 if (!result) | 426 if (result) { |
| 427 if (shouldCacheVersion) |
| 428 setCachedVersion(version); |
| 429 } else |
| 392 LOG_ERROR("Failed to set version %s in database (%s)", version.ascii().d
ata(), setVersionQuery.ascii().data()); | 430 LOG_ERROR("Failed to set version %s in database (%s)", version.ascii().d
ata(), setVersionQuery.ascii().data()); |
| 393 | 431 |
| 394 m_databaseAuthorizer->enable(); | 432 m_databaseAuthorizer->enable(); |
| 395 | 433 |
| 396 return result; | 434 return result; |
| 397 } | 435 } |
| 398 | 436 |
| 399 bool AbstractDatabase::versionMatchesExpected() const | |
| 400 { | |
| 401 if (!m_expectedVersion.isEmpty()) { | |
| 402 MutexLocker locker(guidMutex()); | |
| 403 return m_expectedVersion == guidToVersionMap().get(m_guid); | |
| 404 } | |
| 405 | |
| 406 return true; | |
| 407 } | |
| 408 | |
| 409 void AbstractDatabase::setExpectedVersion(const String& version) | 437 void AbstractDatabase::setExpectedVersion(const String& version) |
| 410 { | 438 { |
| 411 m_expectedVersion = version.threadsafeCopy(); | 439 m_expectedVersion = version.threadsafeCopy(); |
| 440 } |
| 441 |
| 442 String AbstractDatabase::getCachedVersion() const |
| 443 { |
| 444 MutexLocker locker(guidMutex()); |
| 445 return guidToVersionMap().get(m_guid).threadsafeCopy(); |
| 446 } |
| 447 |
| 448 void AbstractDatabase::setCachedVersion(const String& actualVersion) |
| 449 { |
| 412 // Update the in memory database version map. | 450 // Update the in memory database version map. |
| 413 MutexLocker locker(guidMutex()); | 451 MutexLocker locker(guidMutex()); |
| 414 updateGuidVersionMap(m_guid, version); | 452 updateGuidVersionMap(m_guid, actualVersion); |
| 453 } |
| 454 |
| 455 bool AbstractDatabase::getActualVersionForTransaction(String &actualVersion) |
| 456 { |
| 457 ASSERT(m_sqliteDatabase.transactionInProgress()); |
| 458 #if PLATFORM(CHROMIUM) |
| 459 // Note: In multi-process browsers the cached value may be inaccurate. |
| 460 // So we retrieve the value from the database and update the cached value he
re. |
| 461 return getVersionFromDatabase(actualVersion, true); |
| 462 #else |
| 463 actualVersion = getCachedVersion(); |
| 464 return true; |
| 465 #endif |
| 415 } | 466 } |
| 416 | 467 |
| 417 void AbstractDatabase::disableAuthorizer() | 468 void AbstractDatabase::disableAuthorizer() |
| 418 { | 469 { |
| 419 ASSERT(m_databaseAuthorizer); | 470 ASSERT(m_databaseAuthorizer); |
| 420 m_databaseAuthorizer->disable(); | 471 m_databaseAuthorizer->disable(); |
| 421 } | 472 } |
| 422 | 473 |
| 423 void AbstractDatabase::enableAuthorizer() | 474 void AbstractDatabase::enableAuthorizer() |
| 424 { | 475 { |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 488 | 539 |
| 489 bool AbstractDatabase::isInterrupted() | 540 bool AbstractDatabase::isInterrupted() |
| 490 { | 541 { |
| 491 MutexLocker locker(m_sqliteDatabase.databaseMutex()); | 542 MutexLocker locker(m_sqliteDatabase.databaseMutex()); |
| 492 return m_sqliteDatabase.isInterrupted(); | 543 return m_sqliteDatabase.isInterrupted(); |
| 493 } | 544 } |
| 494 | 545 |
| 495 } // namespace WebCore | 546 } // namespace WebCore |
| 496 | 547 |
| 497 #endif // ENABLE(DATABASE) | 548 #endif // ENABLE(DATABASE) |
| OLD | NEW |