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 |