Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(37)

Side by Side Diff: Source/WebCore/storage/AbstractDatabase.cpp

Issue 7563014: Merge 92155 - [Chromium] WebSQLDatabase version handling is broken in multi-process browsers. (Closed) Base URL: http://svn.webkit.org/repository/webkit/branches/chromium/835/
Patch Set: Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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)
OLDNEW
« no previous file with comments | « Source/WebCore/storage/AbstractDatabase.h ('k') | Source/WebCore/storage/ChangeVersionWrapper.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698