Bumping manifests a=b2g-bump
[gecko.git] / dom / indexedDB / OpenDatabaseHelper.cpp
blob1f3cde303a3341879a9e9e8cb451171ba5712ad2
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "mozilla/DebugOnly.h"
7 #include "OpenDatabaseHelper.h"
9 #include "nsIBFCacheEntry.h"
10 #include "nsIFile.h"
12 #include <algorithm>
13 #include "mozilla/dom/quota/AcquireListener.h"
14 #include "mozilla/dom/quota/OriginOrPatternString.h"
15 #include "mozilla/dom/quota/QuotaManager.h"
16 #include "mozilla/storage.h"
17 #include "nsEscape.h"
18 #include "nsNetUtil.h"
19 #include "nsThreadUtils.h"
20 #include "snappy/snappy.h"
22 #include "Client.h"
23 #include "IDBEvents.h"
24 #include "IDBFactory.h"
25 #include "IndexedDatabaseManager.h"
26 #include "ProfilerHelpers.h"
27 #include "ReportInternalError.h"
29 using namespace mozilla;
30 using namespace mozilla::dom;
31 USING_INDEXEDDB_NAMESPACE
32 USING_QUOTA_NAMESPACE
34 namespace {
36 // If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major
37 // schema version.
38 static_assert(JS_STRUCTURED_CLONE_VERSION == 4,
39 "Need to update the major schema version.");
41 // Major schema version. Bump for almost everything.
42 const uint32_t kMajorSchemaVersion = 16;
44 // Minor schema version. Should almost always be 0 (maybe bump on release
45 // branches if we have to).
46 const uint32_t kMinorSchemaVersion = 0;
48 // The schema version we store in the SQLite database is a (signed) 32-bit
49 // integer. The major version is left-shifted 4 bits so the max value is
50 // 0xFFFFFFF. The minor version occupies the lower 4 bits and its max is 0xF.
51 static_assert(kMajorSchemaVersion <= 0xFFFFFFF,
52 "Major version needs to fit in 28 bits.");
53 static_assert(kMinorSchemaVersion <= 0xF,
54 "Minor version needs to fit in 4 bits.");
56 inline
57 int32_t
58 MakeSchemaVersion(uint32_t aMajorSchemaVersion,
59 uint32_t aMinorSchemaVersion)
61 return int32_t((aMajorSchemaVersion << 4) + aMinorSchemaVersion);
64 const int32_t kSQLiteSchemaVersion = int32_t((kMajorSchemaVersion << 4) +
65 kMinorSchemaVersion);
67 const uint32_t kGoldenRatioU32 = 0x9E3779B9U;
69 inline
70 uint32_t
71 RotateBitsLeft32(uint32_t value, uint8_t bits)
73 MOZ_ASSERT(bits < 32);
74 return (value << bits) | (value >> (32 - bits));
77 inline
78 uint32_t
79 HashName(const nsAString& aName)
81 const char16_t* str = aName.BeginReading();
82 size_t length = aName.Length();
84 uint32_t hash = 0;
85 for (size_t i = 0; i < length; i++) {
86 hash = kGoldenRatioU32 * (RotateBitsLeft32(hash, 5) ^ str[i]);
89 return hash;
92 nsresult
93 GetDatabaseFilename(const nsAString& aName,
94 nsAString& aDatabaseFilename)
96 aDatabaseFilename.AppendInt(HashName(aName));
98 nsCString escapedName;
99 if (!NS_Escape(NS_ConvertUTF16toUTF8(aName), escapedName, url_XPAlphas)) {
100 NS_WARNING("Can't escape database name!");
101 return NS_ERROR_UNEXPECTED;
104 const char* forwardIter = escapedName.BeginReading();
105 const char* backwardIter = escapedName.EndReading() - 1;
107 nsCString substring;
108 while (forwardIter <= backwardIter && substring.Length() < 21) {
109 if (substring.Length() % 2) {
110 substring.Append(*backwardIter--);
112 else {
113 substring.Append(*forwardIter++);
117 aDatabaseFilename.Append(NS_ConvertASCIItoUTF16(substring));
119 return NS_OK;
122 nsresult
123 CreateFileTables(mozIStorageConnection* aDBConn)
125 AssertIsOnIOThread();
126 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
128 PROFILER_LABEL("OpenDatabaseHelper", "CreateFileTables",
129 js::ProfileEntry::Category::STORAGE);
131 // Table `file`
132 nsresult rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
133 "CREATE TABLE file ("
134 "id INTEGER PRIMARY KEY, "
135 "refcount INTEGER NOT NULL"
136 ");"
138 NS_ENSURE_SUCCESS(rv, rv);
140 rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
141 "CREATE TRIGGER object_data_insert_trigger "
142 "AFTER INSERT ON object_data "
143 "FOR EACH ROW "
144 "WHEN NEW.file_ids IS NOT NULL "
145 "BEGIN "
146 "SELECT update_refcount(NULL, NEW.file_ids); "
147 "END;"
149 NS_ENSURE_SUCCESS(rv, rv);
151 rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
152 "CREATE TRIGGER object_data_update_trigger "
153 "AFTER UPDATE OF file_ids ON object_data "
154 "FOR EACH ROW "
155 "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
156 "BEGIN "
157 "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
158 "END;"
160 NS_ENSURE_SUCCESS(rv, rv);
162 rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
163 "CREATE TRIGGER object_data_delete_trigger "
164 "AFTER DELETE ON object_data "
165 "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL "
166 "BEGIN "
167 "SELECT update_refcount(OLD.file_ids, NULL); "
168 "END;"
170 NS_ENSURE_SUCCESS(rv, rv);
172 rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
173 "CREATE TRIGGER file_update_trigger "
174 "AFTER UPDATE ON file "
175 "FOR EACH ROW WHEN NEW.refcount = 0 "
176 "BEGIN "
177 "DELETE FROM file WHERE id = OLD.id; "
178 "END;"
180 NS_ENSURE_SUCCESS(rv, rv);
182 return NS_OK;
185 nsresult
186 CreateTables(mozIStorageConnection* aDBConn)
188 AssertIsOnIOThread();
189 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
190 NS_ASSERTION(aDBConn, "Passing a null database connection!");
192 PROFILER_LABEL("OpenDatabaseHelper", "CreateTables",
193 js::ProfileEntry::Category::STORAGE);
195 // Table `database`
196 nsresult rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
197 "CREATE TABLE database ("
198 "name TEXT NOT NULL, "
199 "version INTEGER NOT NULL DEFAULT 0"
200 ");"
202 NS_ENSURE_SUCCESS(rv, rv);
204 // Table `object_store`
205 rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
206 "CREATE TABLE object_store ("
207 "id INTEGER PRIMARY KEY, "
208 "auto_increment INTEGER NOT NULL DEFAULT 0, "
209 "name TEXT NOT NULL, "
210 "key_path TEXT, "
211 "UNIQUE (name)"
212 ");"
214 NS_ENSURE_SUCCESS(rv, rv);
216 // Table `object_data`
217 rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
218 "CREATE TABLE object_data ("
219 "id INTEGER PRIMARY KEY, "
220 "object_store_id INTEGER NOT NULL, "
221 "key_value BLOB DEFAULT NULL, "
222 "file_ids TEXT, "
223 "data BLOB NOT NULL, "
224 "UNIQUE (object_store_id, key_value), "
225 "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
226 "CASCADE"
227 ");"
229 NS_ENSURE_SUCCESS(rv, rv);
231 // Table `index`
232 rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
233 "CREATE TABLE object_store_index ("
234 "id INTEGER PRIMARY KEY, "
235 "object_store_id INTEGER NOT NULL, "
236 "name TEXT NOT NULL, "
237 "key_path TEXT NOT NULL, "
238 "unique_index INTEGER NOT NULL, "
239 "multientry INTEGER NOT NULL, "
240 "UNIQUE (object_store_id, name), "
241 "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
242 "CASCADE"
243 ");"
245 NS_ENSURE_SUCCESS(rv, rv);
247 // Table `index_data`
248 rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
249 "CREATE TABLE index_data ("
250 "index_id INTEGER NOT NULL, "
251 "value BLOB NOT NULL, "
252 "object_data_key BLOB NOT NULL, "
253 "object_data_id INTEGER NOT NULL, "
254 "PRIMARY KEY (index_id, value, object_data_key), "
255 "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
256 "CASCADE, "
257 "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
258 "CASCADE"
259 ");"
261 NS_ENSURE_SUCCESS(rv, rv);
263 // Need this to make cascading deletes from object_data and object_store fast.
264 rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
265 "CREATE INDEX index_data_object_data_id_index "
266 "ON index_data (object_data_id);"
268 NS_ENSURE_SUCCESS(rv, rv);
270 // Table `unique_index_data`
271 rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
272 "CREATE TABLE unique_index_data ("
273 "index_id INTEGER NOT NULL, "
274 "value BLOB NOT NULL, "
275 "object_data_key BLOB NOT NULL, "
276 "object_data_id INTEGER NOT NULL, "
277 "PRIMARY KEY (index_id, value, object_data_key), "
278 "UNIQUE (index_id, value), "
279 "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
280 "CASCADE "
281 "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
282 "CASCADE"
283 ");"
285 NS_ENSURE_SUCCESS(rv, rv);
287 // Need this to make cascading deletes from object_data and object_store fast.
288 rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
289 "CREATE INDEX unique_index_data_object_data_id_index "
290 "ON unique_index_data (object_data_id);"
292 NS_ENSURE_SUCCESS(rv, rv);
294 rv = CreateFileTables(aDBConn);
295 NS_ENSURE_SUCCESS(rv, rv);
297 rv = aDBConn->SetSchemaVersion(kSQLiteSchemaVersion);
298 NS_ENSURE_SUCCESS(rv, rv);
300 return NS_OK;
303 nsresult
304 UpgradeSchemaFrom4To5(mozIStorageConnection* aConnection)
306 AssertIsOnIOThread();
307 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
309 PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom4To5",
310 js::ProfileEntry::Category::STORAGE);
312 nsresult rv;
314 // All we changed is the type of the version column, so lets try to
315 // convert that to an integer, and if we fail, set it to 0.
316 nsCOMPtr<mozIStorageStatement> stmt;
317 rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
318 "SELECT name, version, dataVersion "
319 "FROM database"
320 ), getter_AddRefs(stmt));
321 NS_ENSURE_SUCCESS(rv, rv);
323 nsString name;
324 int32_t intVersion;
325 int64_t dataVersion;
328 mozStorageStatementScoper scoper(stmt);
330 bool hasResults;
331 rv = stmt->ExecuteStep(&hasResults);
332 NS_ENSURE_SUCCESS(rv, rv);
333 NS_ENSURE_TRUE(hasResults, NS_ERROR_FAILURE);
335 nsString version;
336 rv = stmt->GetString(1, version);
337 NS_ENSURE_SUCCESS(rv, rv);
339 intVersion = version.ToInteger(&rv);
340 if (NS_FAILED(rv)) {
341 intVersion = 0;
344 rv = stmt->GetString(0, name);
345 NS_ENSURE_SUCCESS(rv, rv);
347 rv = stmt->GetInt64(2, &dataVersion);
348 NS_ENSURE_SUCCESS(rv, rv);
351 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
352 "DROP TABLE database"
354 NS_ENSURE_SUCCESS(rv, rv);
356 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
357 "CREATE TABLE database ("
358 "name TEXT NOT NULL, "
359 "version INTEGER NOT NULL DEFAULT 0, "
360 "dataVersion INTEGER NOT NULL"
361 ");"
363 NS_ENSURE_SUCCESS(rv, rv);
365 rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
366 "INSERT INTO database (name, version, dataVersion) "
367 "VALUES (:name, :version, :dataVersion)"
368 ), getter_AddRefs(stmt));
369 NS_ENSURE_SUCCESS(rv, rv);
372 mozStorageStatementScoper scoper(stmt);
374 rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), name);
375 NS_ENSURE_SUCCESS(rv, rv);
377 rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("version"), intVersion);
378 NS_ENSURE_SUCCESS(rv, rv);
380 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("dataVersion"), dataVersion);
381 NS_ENSURE_SUCCESS(rv, rv);
383 rv = stmt->Execute();
384 NS_ENSURE_SUCCESS(rv, rv);
387 rv = aConnection->SetSchemaVersion(5);
388 NS_ENSURE_SUCCESS(rv, rv);
390 return NS_OK;
393 nsresult
394 UpgradeSchemaFrom5To6(mozIStorageConnection* aConnection)
396 AssertIsOnIOThread();
397 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
399 PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom5To6",
400 js::ProfileEntry::Category::STORAGE);
402 // First, drop all the indexes we're no longer going to use.
403 nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
404 "DROP INDEX key_index;"
406 NS_ENSURE_SUCCESS(rv, rv);
408 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
409 "DROP INDEX ai_key_index;"
411 NS_ENSURE_SUCCESS(rv, rv);
413 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
414 "DROP INDEX value_index;"
416 NS_ENSURE_SUCCESS(rv, rv);
418 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
419 "DROP INDEX ai_value_index;"
421 NS_ENSURE_SUCCESS(rv, rv);
423 // Now, reorder the columns of object_data to put the blob data last. We do
424 // this by copying into a temporary table, dropping the original, then copying
425 // back into a newly created table.
426 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
427 "CREATE TEMPORARY TABLE temp_upgrade ("
428 "id INTEGER PRIMARY KEY, "
429 "object_store_id, "
430 "key_value, "
431 "data "
432 ");"
434 NS_ENSURE_SUCCESS(rv, rv);
436 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
437 "INSERT INTO temp_upgrade "
438 "SELECT id, object_store_id, key_value, data "
439 "FROM object_data;"
441 NS_ENSURE_SUCCESS(rv, rv);
443 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
444 "DROP TABLE object_data;"
446 NS_ENSURE_SUCCESS(rv, rv);
448 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
449 "CREATE TABLE object_data ("
450 "id INTEGER PRIMARY KEY, "
451 "object_store_id INTEGER NOT NULL, "
452 "key_value DEFAULT NULL, "
453 "data BLOB NOT NULL, "
454 "UNIQUE (object_store_id, key_value), "
455 "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
456 "CASCADE"
457 ");"
459 NS_ENSURE_SUCCESS(rv, rv);
461 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
462 "INSERT INTO object_data "
463 "SELECT id, object_store_id, key_value, data "
464 "FROM temp_upgrade;"
466 NS_ENSURE_SUCCESS(rv, rv);
468 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
469 "DROP TABLE temp_upgrade;"
471 NS_ENSURE_SUCCESS(rv, rv);
473 // We need to add a unique constraint to our ai_object_data table. Copy all
474 // the data out of it using a temporary table as before.
475 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
476 "CREATE TEMPORARY TABLE temp_upgrade ("
477 "id INTEGER PRIMARY KEY, "
478 "object_store_id, "
479 "data "
480 ");"
482 NS_ENSURE_SUCCESS(rv, rv);
484 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
485 "INSERT INTO temp_upgrade "
486 "SELECT id, object_store_id, data "
487 "FROM ai_object_data;"
489 NS_ENSURE_SUCCESS(rv, rv);
491 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
492 "DROP TABLE ai_object_data;"
494 NS_ENSURE_SUCCESS(rv, rv);
496 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
497 "CREATE TABLE ai_object_data ("
498 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
499 "object_store_id INTEGER NOT NULL, "
500 "data BLOB NOT NULL, "
501 "UNIQUE (object_store_id, id), "
502 "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
503 "CASCADE"
504 ");"
506 NS_ENSURE_SUCCESS(rv, rv);
508 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
509 "INSERT INTO ai_object_data "
510 "SELECT id, object_store_id, data "
511 "FROM temp_upgrade;"
513 NS_ENSURE_SUCCESS(rv, rv);
515 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
516 "DROP TABLE temp_upgrade;"
518 NS_ENSURE_SUCCESS(rv, rv);
520 // Fix up the index_data table. We're reordering the columns as well as
521 // changing the primary key from being a simple id to being a composite.
522 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
523 "CREATE TEMPORARY TABLE temp_upgrade ("
524 "index_id, "
525 "value, "
526 "object_data_key, "
527 "object_data_id "
528 ");"
530 NS_ENSURE_SUCCESS(rv, rv);
532 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
533 "INSERT INTO temp_upgrade "
534 "SELECT index_id, value, object_data_key, object_data_id "
535 "FROM index_data;"
537 NS_ENSURE_SUCCESS(rv, rv);
539 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
540 "DROP TABLE index_data;"
542 NS_ENSURE_SUCCESS(rv, rv);
544 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
545 "CREATE TABLE index_data ("
546 "index_id INTEGER NOT NULL, "
547 "value NOT NULL, "
548 "object_data_key NOT NULL, "
549 "object_data_id INTEGER NOT NULL, "
550 "PRIMARY KEY (index_id, value, object_data_key), "
551 "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
552 "CASCADE, "
553 "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
554 "CASCADE"
555 ");"
557 NS_ENSURE_SUCCESS(rv, rv);
559 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
560 "INSERT OR IGNORE INTO index_data "
561 "SELECT index_id, value, object_data_key, object_data_id "
562 "FROM temp_upgrade;"
564 NS_ENSURE_SUCCESS(rv, rv);
566 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
567 "DROP TABLE temp_upgrade;"
569 NS_ENSURE_SUCCESS(rv, rv);
571 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
572 "CREATE INDEX index_data_object_data_id_index "
573 "ON index_data (object_data_id);"
575 NS_ENSURE_SUCCESS(rv, rv);
577 // Fix up the unique_index_data table. We're reordering the columns as well as
578 // changing the primary key from being a simple id to being a composite.
579 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
580 "CREATE TEMPORARY TABLE temp_upgrade ("
581 "index_id, "
582 "value, "
583 "object_data_key, "
584 "object_data_id "
585 ");"
587 NS_ENSURE_SUCCESS(rv, rv);
589 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
590 "INSERT INTO temp_upgrade "
591 "SELECT index_id, value, object_data_key, object_data_id "
592 "FROM unique_index_data;"
594 NS_ENSURE_SUCCESS(rv, rv);
596 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
597 "DROP TABLE unique_index_data;"
599 NS_ENSURE_SUCCESS(rv, rv);
601 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
602 "CREATE TABLE unique_index_data ("
603 "index_id INTEGER NOT NULL, "
604 "value NOT NULL, "
605 "object_data_key NOT NULL, "
606 "object_data_id INTEGER NOT NULL, "
607 "PRIMARY KEY (index_id, value, object_data_key), "
608 "UNIQUE (index_id, value), "
609 "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
610 "CASCADE "
611 "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
612 "CASCADE"
613 ");"
615 NS_ENSURE_SUCCESS(rv, rv);
617 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
618 "INSERT INTO unique_index_data "
619 "SELECT index_id, value, object_data_key, object_data_id "
620 "FROM temp_upgrade;"
622 NS_ENSURE_SUCCESS(rv, rv);
624 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
625 "DROP TABLE temp_upgrade;"
627 NS_ENSURE_SUCCESS(rv, rv);
629 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
630 "CREATE INDEX unique_index_data_object_data_id_index "
631 "ON unique_index_data (object_data_id);"
633 NS_ENSURE_SUCCESS(rv, rv);
635 // Fix up the ai_index_data table. We're reordering the columns as well as
636 // changing the primary key from being a simple id to being a composite.
637 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
638 "CREATE TEMPORARY TABLE temp_upgrade ("
639 "index_id, "
640 "value, "
641 "ai_object_data_id "
642 ");"
644 NS_ENSURE_SUCCESS(rv, rv);
646 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
647 "INSERT INTO temp_upgrade "
648 "SELECT index_id, value, ai_object_data_id "
649 "FROM ai_index_data;"
651 NS_ENSURE_SUCCESS(rv, rv);
653 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
654 "DROP TABLE ai_index_data;"
656 NS_ENSURE_SUCCESS(rv, rv);
658 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
659 "CREATE TABLE ai_index_data ("
660 "index_id INTEGER NOT NULL, "
661 "value NOT NULL, "
662 "ai_object_data_id INTEGER NOT NULL, "
663 "PRIMARY KEY (index_id, value, ai_object_data_id), "
664 "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
665 "CASCADE, "
666 "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
667 "CASCADE"
668 ");"
670 NS_ENSURE_SUCCESS(rv, rv);
672 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
673 "INSERT OR IGNORE INTO ai_index_data "
674 "SELECT index_id, value, ai_object_data_id "
675 "FROM temp_upgrade;"
677 NS_ENSURE_SUCCESS(rv, rv);
679 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
680 "DROP TABLE temp_upgrade;"
682 NS_ENSURE_SUCCESS(rv, rv);
684 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
685 "CREATE INDEX ai_index_data_ai_object_data_id_index "
686 "ON ai_index_data (ai_object_data_id);"
688 NS_ENSURE_SUCCESS(rv, rv);
690 // Fix up the ai_unique_index_data table. We're reordering the columns as well
691 // as changing the primary key from being a simple id to being a composite.
692 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
693 "CREATE TEMPORARY TABLE temp_upgrade ("
694 "index_id, "
695 "value, "
696 "ai_object_data_id "
697 ");"
699 NS_ENSURE_SUCCESS(rv, rv);
701 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
702 "INSERT INTO temp_upgrade "
703 "SELECT index_id, value, ai_object_data_id "
704 "FROM ai_unique_index_data;"
706 NS_ENSURE_SUCCESS(rv, rv);
708 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
709 "DROP TABLE ai_unique_index_data;"
711 NS_ENSURE_SUCCESS(rv, rv);
713 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
714 "CREATE TABLE ai_unique_index_data ("
715 "index_id INTEGER NOT NULL, "
716 "value NOT NULL, "
717 "ai_object_data_id INTEGER NOT NULL, "
718 "UNIQUE (index_id, value), "
719 "PRIMARY KEY (index_id, value, ai_object_data_id), "
720 "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
721 "CASCADE, "
722 "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
723 "CASCADE"
724 ");"
726 NS_ENSURE_SUCCESS(rv, rv);
728 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
729 "INSERT INTO ai_unique_index_data "
730 "SELECT index_id, value, ai_object_data_id "
731 "FROM temp_upgrade;"
733 NS_ENSURE_SUCCESS(rv, rv);
735 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
736 "DROP TABLE temp_upgrade;"
738 NS_ENSURE_SUCCESS(rv, rv);
740 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
741 "CREATE INDEX ai_unique_index_data_ai_object_data_id_index "
742 "ON ai_unique_index_data (ai_object_data_id);"
744 NS_ENSURE_SUCCESS(rv, rv);
746 rv = aConnection->SetSchemaVersion(6);
747 NS_ENSURE_SUCCESS(rv, rv);
749 return NS_OK;
752 nsresult
753 UpgradeSchemaFrom6To7(mozIStorageConnection* aConnection)
755 AssertIsOnIOThread();
756 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
758 PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom6To7",
759 js::ProfileEntry::Category::STORAGE);
761 nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
762 "CREATE TEMPORARY TABLE temp_upgrade ("
763 "id, "
764 "name, "
765 "key_path, "
766 "auto_increment"
767 ");"
769 NS_ENSURE_SUCCESS(rv, rv);
771 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
772 "INSERT INTO temp_upgrade "
773 "SELECT id, name, key_path, auto_increment "
774 "FROM object_store;"
776 NS_ENSURE_SUCCESS(rv, rv);
778 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
779 "DROP TABLE object_store;"
781 NS_ENSURE_SUCCESS(rv, rv);
783 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
784 "CREATE TABLE object_store ("
785 "id INTEGER PRIMARY KEY, "
786 "auto_increment INTEGER NOT NULL DEFAULT 0, "
787 "name TEXT NOT NULL, "
788 "key_path TEXT, "
789 "UNIQUE (name)"
790 ");"
792 NS_ENSURE_SUCCESS(rv, rv);
794 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
795 "INSERT INTO object_store "
796 "SELECT id, auto_increment, name, nullif(key_path, '') "
797 "FROM temp_upgrade;"
799 NS_ENSURE_SUCCESS(rv, rv);
801 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
802 "DROP TABLE temp_upgrade;"
804 NS_ENSURE_SUCCESS(rv, rv);
806 rv = aConnection->SetSchemaVersion(7);
807 NS_ENSURE_SUCCESS(rv, rv);
809 return NS_OK;
812 nsresult
813 UpgradeSchemaFrom7To8(mozIStorageConnection* aConnection)
815 AssertIsOnIOThread();
816 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
818 PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom7To8",
819 js::ProfileEntry::Category::STORAGE);
821 nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
822 "CREATE TEMPORARY TABLE temp_upgrade ("
823 "id, "
824 "object_store_id, "
825 "name, "
826 "key_path, "
827 "unique_index, "
828 "object_store_autoincrement"
829 ");"
831 NS_ENSURE_SUCCESS(rv, rv);
833 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
834 "INSERT INTO temp_upgrade "
835 "SELECT id, object_store_id, name, key_path, "
836 "unique_index, object_store_autoincrement "
837 "FROM object_store_index;"
839 NS_ENSURE_SUCCESS(rv, rv);
841 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
842 "DROP TABLE object_store_index;"
844 NS_ENSURE_SUCCESS(rv, rv);
846 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
847 "CREATE TABLE object_store_index ("
848 "id INTEGER, "
849 "object_store_id INTEGER NOT NULL, "
850 "name TEXT NOT NULL, "
851 "key_path TEXT NOT NULL, "
852 "unique_index INTEGER NOT NULL, "
853 "multientry INTEGER NOT NULL, "
854 "object_store_autoincrement INTERGER NOT NULL, "
855 "PRIMARY KEY (id), "
856 "UNIQUE (object_store_id, name), "
857 "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
858 "CASCADE"
859 ");"
861 NS_ENSURE_SUCCESS(rv, rv);
863 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
864 "INSERT INTO object_store_index "
865 "SELECT id, object_store_id, name, key_path, "
866 "unique_index, 0, object_store_autoincrement "
867 "FROM temp_upgrade;"
869 NS_ENSURE_SUCCESS(rv, rv);
871 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
872 "DROP TABLE temp_upgrade;"
874 NS_ENSURE_SUCCESS(rv, rv);
876 rv = aConnection->SetSchemaVersion(8);
877 NS_ENSURE_SUCCESS(rv, rv);
879 return NS_OK;
882 class CompressDataBlobsFunction MOZ_FINAL : public mozIStorageFunction
884 ~CompressDataBlobsFunction() {}
886 public:
887 NS_DECL_ISUPPORTS
889 NS_IMETHOD
890 OnFunctionCall(mozIStorageValueArray* aArguments,
891 nsIVariant** aResult)
893 PROFILER_LABEL("CompressDataBlobsFunction", "OnFunctionCall",
894 js::ProfileEntry::Category::STORAGE);
896 uint32_t argc;
897 nsresult rv = aArguments->GetNumEntries(&argc);
898 NS_ENSURE_SUCCESS(rv, rv);
900 if (argc != 1) {
901 NS_WARNING("Don't call me with the wrong number of arguments!");
902 return NS_ERROR_UNEXPECTED;
905 int32_t type;
906 rv = aArguments->GetTypeOfIndex(0, &type);
907 NS_ENSURE_SUCCESS(rv, rv);
909 if (type != mozIStorageStatement::VALUE_TYPE_BLOB) {
910 NS_WARNING("Don't call me with the wrong type of arguments!");
911 return NS_ERROR_UNEXPECTED;
914 const uint8_t* uncompressed;
915 uint32_t uncompressedLength;
916 rv = aArguments->GetSharedBlob(0, &uncompressedLength, &uncompressed);
917 NS_ENSURE_SUCCESS(rv, rv);
919 size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
920 char* compressed = (char*)moz_malloc(compressedLength);
921 NS_ENSURE_TRUE(compressed, NS_ERROR_OUT_OF_MEMORY);
923 snappy::RawCompress(reinterpret_cast<const char*>(uncompressed),
924 uncompressedLength, compressed, &compressedLength);
926 std::pair<uint8_t *, int> data((uint8_t*)compressed,
927 int(compressedLength));
928 // The variant takes ownership of | compressed |.
929 nsCOMPtr<nsIVariant> result = new mozilla::storage::AdoptedBlobVariant(data);
931 result.forget(aResult);
932 return NS_OK;
936 NS_IMPL_ISUPPORTS(CompressDataBlobsFunction, mozIStorageFunction)
938 nsresult
939 UpgradeSchemaFrom8To9_0(mozIStorageConnection* aConnection)
941 AssertIsOnIOThread();
942 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
944 PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom8To9_0",
945 js::ProfileEntry::Category::STORAGE);
947 // We no longer use the dataVersion column.
948 nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
949 "UPDATE database SET dataVersion = 0;"
951 NS_ENSURE_SUCCESS(rv, rv);
953 nsCOMPtr<mozIStorageFunction> compressor = new CompressDataBlobsFunction();
955 NS_NAMED_LITERAL_CSTRING(compressorName, "compress");
957 rv = aConnection->CreateFunction(compressorName, 1, compressor);
958 NS_ENSURE_SUCCESS(rv, rv);
960 // Turn off foreign key constraints before we do anything here.
961 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
962 "UPDATE object_data SET data = compress(data);"
964 NS_ENSURE_SUCCESS(rv, rv);
966 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
967 "UPDATE ai_object_data SET data = compress(data);"
969 NS_ENSURE_SUCCESS(rv, rv);
971 rv = aConnection->RemoveFunction(compressorName);
972 NS_ENSURE_SUCCESS(rv, rv);
974 rv = aConnection->SetSchemaVersion(MakeSchemaVersion(9, 0));
975 NS_ENSURE_SUCCESS(rv, rv);
977 return NS_OK;
980 nsresult
981 UpgradeSchemaFrom9_0To10_0(mozIStorageConnection* aConnection)
983 AssertIsOnIOThread();
984 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
986 PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom9_0To10_0",
987 js::ProfileEntry::Category::STORAGE);
989 nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
990 "ALTER TABLE object_data ADD COLUMN file_ids TEXT;"
992 NS_ENSURE_SUCCESS(rv, rv);
994 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
995 "ALTER TABLE ai_object_data ADD COLUMN file_ids TEXT;"
997 NS_ENSURE_SUCCESS(rv, rv);
999 rv = CreateFileTables(aConnection);
1000 NS_ENSURE_SUCCESS(rv, rv);
1002 rv = aConnection->SetSchemaVersion(MakeSchemaVersion(10, 0));
1003 NS_ENSURE_SUCCESS(rv, rv);
1005 return NS_OK;
1008 nsresult
1009 UpgradeSchemaFrom10_0To11_0(mozIStorageConnection* aConnection)
1011 AssertIsOnIOThread();
1012 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
1014 PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom10_0To11_0",
1015 js::ProfileEntry::Category::STORAGE);
1017 nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1018 "CREATE TEMPORARY TABLE temp_upgrade ("
1019 "id, "
1020 "object_store_id, "
1021 "name, "
1022 "key_path, "
1023 "unique_index, "
1024 "multientry"
1025 ");"
1027 NS_ENSURE_SUCCESS(rv, rv);
1029 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1030 "INSERT INTO temp_upgrade "
1031 "SELECT id, object_store_id, name, key_path, "
1032 "unique_index, multientry "
1033 "FROM object_store_index;"
1035 NS_ENSURE_SUCCESS(rv, rv);
1037 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1038 "DROP TABLE object_store_index;"
1040 NS_ENSURE_SUCCESS(rv, rv);
1042 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1043 "CREATE TABLE object_store_index ("
1044 "id INTEGER PRIMARY KEY, "
1045 "object_store_id INTEGER NOT NULL, "
1046 "name TEXT NOT NULL, "
1047 "key_path TEXT NOT NULL, "
1048 "unique_index INTEGER NOT NULL, "
1049 "multientry INTEGER NOT NULL, "
1050 "UNIQUE (object_store_id, name), "
1051 "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
1052 "CASCADE"
1053 ");"
1055 NS_ENSURE_SUCCESS(rv, rv);
1057 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1058 "INSERT INTO object_store_index "
1059 "SELECT id, object_store_id, name, key_path, "
1060 "unique_index, multientry "
1061 "FROM temp_upgrade;"
1063 NS_ENSURE_SUCCESS(rv, rv);
1065 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1066 "DROP TABLE temp_upgrade;"
1068 NS_ENSURE_SUCCESS(rv, rv);
1070 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1071 "DROP TRIGGER object_data_insert_trigger;"
1073 NS_ENSURE_SUCCESS(rv, rv);
1075 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1076 "INSERT INTO object_data (object_store_id, key_value, data, file_ids) "
1077 "SELECT object_store_id, id, data, file_ids "
1078 "FROM ai_object_data;"
1080 NS_ENSURE_SUCCESS(rv, rv);
1082 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1083 "CREATE TRIGGER object_data_insert_trigger "
1084 "AFTER INSERT ON object_data "
1085 "FOR EACH ROW "
1086 "WHEN NEW.file_ids IS NOT NULL "
1087 "BEGIN "
1088 "SELECT update_refcount(NULL, NEW.file_ids); "
1089 "END;"
1091 NS_ENSURE_SUCCESS(rv, rv);
1093 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1094 "INSERT INTO index_data (index_id, value, object_data_key, object_data_id) "
1095 "SELECT ai_index_data.index_id, ai_index_data.value, ai_index_data.ai_object_data_id, object_data.id "
1096 "FROM ai_index_data "
1097 "INNER JOIN object_store_index ON "
1098 "object_store_index.id = ai_index_data.index_id "
1099 "INNER JOIN object_data ON "
1100 "object_data.object_store_id = object_store_index.object_store_id AND "
1101 "object_data.key_value = ai_index_data.ai_object_data_id;"
1103 NS_ENSURE_SUCCESS(rv, rv);
1105 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1106 "INSERT INTO unique_index_data (index_id, value, object_data_key, object_data_id) "
1107 "SELECT ai_unique_index_data.index_id, ai_unique_index_data.value, ai_unique_index_data.ai_object_data_id, object_data.id "
1108 "FROM ai_unique_index_data "
1109 "INNER JOIN object_store_index ON "
1110 "object_store_index.id = ai_unique_index_data.index_id "
1111 "INNER JOIN object_data ON "
1112 "object_data.object_store_id = object_store_index.object_store_id AND "
1113 "object_data.key_value = ai_unique_index_data.ai_object_data_id;"
1115 NS_ENSURE_SUCCESS(rv, rv);
1117 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1118 "UPDATE object_store "
1119 "SET auto_increment = (SELECT max(id) FROM ai_object_data) + 1 "
1120 "WHERE auto_increment;"
1122 NS_ENSURE_SUCCESS(rv, rv);
1124 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1125 "DROP TABLE ai_unique_index_data;"
1127 NS_ENSURE_SUCCESS(rv, rv);
1129 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1130 "DROP TABLE ai_index_data;"
1132 NS_ENSURE_SUCCESS(rv, rv);
1134 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1135 "DROP TABLE ai_object_data;"
1137 NS_ENSURE_SUCCESS(rv, rv);
1139 rv = aConnection->SetSchemaVersion(MakeSchemaVersion(11, 0));
1140 NS_ENSURE_SUCCESS(rv, rv);
1142 return NS_OK;
1145 class EncodeKeysFunction MOZ_FINAL : public mozIStorageFunction
1147 ~EncodeKeysFunction() {}
1149 public:
1150 NS_DECL_ISUPPORTS
1152 NS_IMETHOD
1153 OnFunctionCall(mozIStorageValueArray* aArguments,
1154 nsIVariant** aResult)
1156 PROFILER_LABEL("EncodeKeysFunction", "OnFunctionCall",
1157 js::ProfileEntry::Category::STORAGE);
1159 uint32_t argc;
1160 nsresult rv = aArguments->GetNumEntries(&argc);
1161 NS_ENSURE_SUCCESS(rv, rv);
1163 if (argc != 1) {
1164 NS_WARNING("Don't call me with the wrong number of arguments!");
1165 return NS_ERROR_UNEXPECTED;
1168 int32_t type;
1169 rv = aArguments->GetTypeOfIndex(0, &type);
1170 NS_ENSURE_SUCCESS(rv, rv);
1172 Key key;
1173 if (type == mozIStorageStatement::VALUE_TYPE_INTEGER) {
1174 int64_t intKey;
1175 aArguments->GetInt64(0, &intKey);
1176 key.SetFromInteger(intKey);
1178 else if (type == mozIStorageStatement::VALUE_TYPE_TEXT) {
1179 nsString stringKey;
1180 aArguments->GetString(0, stringKey);
1181 key.SetFromString(stringKey);
1183 else {
1184 NS_WARNING("Don't call me with the wrong type of arguments!");
1185 return NS_ERROR_UNEXPECTED;
1188 const nsCString& buffer = key.GetBuffer();
1190 std::pair<const void *, int> data(static_cast<const void*>(buffer.get()),
1191 int(buffer.Length()));
1193 nsCOMPtr<nsIVariant> result = new mozilla::storage::BlobVariant(data);
1195 result.forget(aResult);
1196 return NS_OK;
1200 NS_IMPL_ISUPPORTS(EncodeKeysFunction, mozIStorageFunction)
1202 nsresult
1203 UpgradeSchemaFrom11_0To12_0(mozIStorageConnection* aConnection)
1205 AssertIsOnIOThread();
1206 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
1208 PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom11_0To12_0",
1209 js::ProfileEntry::Category::STORAGE);
1211 NS_NAMED_LITERAL_CSTRING(encoderName, "encode");
1213 nsCOMPtr<mozIStorageFunction> encoder = new EncodeKeysFunction();
1215 nsresult rv = aConnection->CreateFunction(encoderName, 1, encoder);
1216 NS_ENSURE_SUCCESS(rv, rv);
1218 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1219 "CREATE TEMPORARY TABLE temp_upgrade ("
1220 "id INTEGER PRIMARY KEY, "
1221 "object_store_id, "
1222 "key_value, "
1223 "data, "
1224 "file_ids "
1225 ");"
1227 NS_ENSURE_SUCCESS(rv, rv);
1229 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1230 "INSERT INTO temp_upgrade "
1231 "SELECT id, object_store_id, encode(key_value), data, file_ids "
1232 "FROM object_data;"
1234 NS_ENSURE_SUCCESS(rv, rv);
1236 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1237 "DROP TABLE object_data;"
1239 NS_ENSURE_SUCCESS(rv, rv);
1241 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1242 "CREATE TABLE object_data ("
1243 "id INTEGER PRIMARY KEY, "
1244 "object_store_id INTEGER NOT NULL, "
1245 "key_value BLOB DEFAULT NULL, "
1246 "file_ids TEXT, "
1247 "data BLOB NOT NULL, "
1248 "UNIQUE (object_store_id, key_value), "
1249 "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
1250 "CASCADE"
1251 ");"
1253 NS_ENSURE_SUCCESS(rv, rv);
1255 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1256 "INSERT INTO object_data "
1257 "SELECT id, object_store_id, key_value, file_ids, data "
1258 "FROM temp_upgrade;"
1260 NS_ENSURE_SUCCESS(rv, rv);
1262 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1263 "DROP TABLE temp_upgrade;"
1265 NS_ENSURE_SUCCESS(rv, rv);
1267 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1268 "CREATE TRIGGER object_data_insert_trigger "
1269 "AFTER INSERT ON object_data "
1270 "FOR EACH ROW "
1271 "WHEN NEW.file_ids IS NOT NULL "
1272 "BEGIN "
1273 "SELECT update_refcount(NULL, NEW.file_ids); "
1274 "END;"
1276 NS_ENSURE_SUCCESS(rv, rv);
1278 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1279 "CREATE TRIGGER object_data_update_trigger "
1280 "AFTER UPDATE OF file_ids ON object_data "
1281 "FOR EACH ROW "
1282 "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
1283 "BEGIN "
1284 "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
1285 "END;"
1287 NS_ENSURE_SUCCESS(rv, rv);
1289 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1290 "CREATE TRIGGER object_data_delete_trigger "
1291 "AFTER DELETE ON object_data "
1292 "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL "
1293 "BEGIN "
1294 "SELECT update_refcount(OLD.file_ids, NULL); "
1295 "END;"
1297 NS_ENSURE_SUCCESS(rv, rv);
1299 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1300 "CREATE TEMPORARY TABLE temp_upgrade ("
1301 "index_id, "
1302 "value, "
1303 "object_data_key, "
1304 "object_data_id "
1305 ");"
1307 NS_ENSURE_SUCCESS(rv, rv);
1309 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1310 "INSERT INTO temp_upgrade "
1311 "SELECT index_id, encode(value), encode(object_data_key), object_data_id "
1312 "FROM index_data;"
1314 NS_ENSURE_SUCCESS(rv, rv);
1316 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1317 "DROP TABLE index_data;"
1319 NS_ENSURE_SUCCESS(rv, rv);
1321 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1322 "CREATE TABLE index_data ("
1323 "index_id INTEGER NOT NULL, "
1324 "value BLOB NOT NULL, "
1325 "object_data_key BLOB NOT NULL, "
1326 "object_data_id INTEGER NOT NULL, "
1327 "PRIMARY KEY (index_id, value, object_data_key), "
1328 "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
1329 "CASCADE, "
1330 "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
1331 "CASCADE"
1332 ");"
1334 NS_ENSURE_SUCCESS(rv, rv);
1336 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1337 "INSERT INTO index_data "
1338 "SELECT index_id, value, object_data_key, object_data_id "
1339 "FROM temp_upgrade;"
1341 NS_ENSURE_SUCCESS(rv, rv);
1343 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1344 "DROP TABLE temp_upgrade;"
1346 NS_ENSURE_SUCCESS(rv, rv);
1348 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1349 "CREATE INDEX index_data_object_data_id_index "
1350 "ON index_data (object_data_id);"
1352 NS_ENSURE_SUCCESS(rv, rv);
1354 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1355 "CREATE TEMPORARY TABLE temp_upgrade ("
1356 "index_id, "
1357 "value, "
1358 "object_data_key, "
1359 "object_data_id "
1360 ");"
1362 NS_ENSURE_SUCCESS(rv, rv);
1364 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1365 "INSERT INTO temp_upgrade "
1366 "SELECT index_id, encode(value), encode(object_data_key), object_data_id "
1367 "FROM unique_index_data;"
1369 NS_ENSURE_SUCCESS(rv, rv);
1371 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1372 "DROP TABLE unique_index_data;"
1374 NS_ENSURE_SUCCESS(rv, rv);
1376 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1377 "CREATE TABLE unique_index_data ("
1378 "index_id INTEGER NOT NULL, "
1379 "value BLOB NOT NULL, "
1380 "object_data_key BLOB NOT NULL, "
1381 "object_data_id INTEGER NOT NULL, "
1382 "PRIMARY KEY (index_id, value, object_data_key), "
1383 "UNIQUE (index_id, value), "
1384 "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
1385 "CASCADE "
1386 "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
1387 "CASCADE"
1388 ");"
1390 NS_ENSURE_SUCCESS(rv, rv);
1392 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1393 "INSERT INTO unique_index_data "
1394 "SELECT index_id, value, object_data_key, object_data_id "
1395 "FROM temp_upgrade;"
1397 NS_ENSURE_SUCCESS(rv, rv);
1399 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1400 "DROP TABLE temp_upgrade;"
1402 NS_ENSURE_SUCCESS(rv, rv);
1404 rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1405 "CREATE INDEX unique_index_data_object_data_id_index "
1406 "ON unique_index_data (object_data_id);"
1408 NS_ENSURE_SUCCESS(rv, rv);
1410 rv = aConnection->RemoveFunction(encoderName);
1411 NS_ENSURE_SUCCESS(rv, rv);
1413 rv = aConnection->SetSchemaVersion(MakeSchemaVersion(12, 0));
1414 NS_ENSURE_SUCCESS(rv, rv);
1416 return NS_OK;
1419 nsresult
1420 UpgradeSchemaFrom12_0To13_0(mozIStorageConnection* aConnection,
1421 bool* aVacuumNeeded)
1423 AssertIsOnIOThread();
1424 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
1426 PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom12_0To13_0",
1427 js::ProfileEntry::Category::STORAGE);
1429 nsresult rv;
1431 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
1432 int32_t defaultPageSize;
1433 rv = aConnection->GetDefaultPageSize(&defaultPageSize);
1434 NS_ENSURE_SUCCESS(rv, rv);
1436 // Enable auto_vacuum mode and update the page size to the platform default.
1437 nsAutoCString upgradeQuery("PRAGMA auto_vacuum = FULL; PRAGMA page_size = ");
1438 upgradeQuery.AppendInt(defaultPageSize);
1440 rv = aConnection->ExecuteSimpleSQL(upgradeQuery);
1441 NS_ENSURE_SUCCESS(rv, rv);
1443 *aVacuumNeeded = true;
1444 #endif
1446 rv = aConnection->SetSchemaVersion(MakeSchemaVersion(13, 0));
1447 NS_ENSURE_SUCCESS(rv, rv);
1449 return NS_OK;
1452 nsresult
1453 UpgradeSchemaFrom13_0To14_0(mozIStorageConnection* aConnection)
1455 // The only change between 13 and 14 was a different structured
1456 // clone format, but it's backwards-compatible.
1457 nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(14, 0));
1458 NS_ENSURE_SUCCESS(rv, rv);
1460 return NS_OK;
1463 nsresult
1464 UpgradeSchemaFrom14_0To15_0(mozIStorageConnection* aConnection)
1466 // The only change between 14 and 15 was a different structured
1467 // clone format, but it's backwards-compatible.
1468 nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(15, 0));
1469 NS_ENSURE_SUCCESS(rv, rv);
1471 return NS_OK;
1474 nsresult
1475 UpgradeSchemaFrom15_0To16_0(mozIStorageConnection* aConnection)
1477 // The only change between 15 and 16 was a different structured
1478 // clone format, but it's backwards-compatible.
1479 nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(16, 0));
1480 NS_ENSURE_SUCCESS(rv, rv);
1482 return NS_OK;
1485 class VersionChangeEventsRunnable;
1487 class SetVersionHelper : public AsyncConnectionHelper,
1488 public IDBTransactionListener,
1489 public AcquireListener
1491 friend class VersionChangeEventsRunnable;
1493 public:
1494 SetVersionHelper(IDBTransaction* aTransaction,
1495 IDBOpenDBRequest* aRequest,
1496 OpenDatabaseHelper* aHelper,
1497 uint64_t aRequestedVersion,
1498 uint64_t aCurrentVersion)
1499 : AsyncConnectionHelper(aTransaction, aRequest),
1500 mOpenRequest(aRequest), mOpenHelper(aHelper),
1501 mRequestedVersion(aRequestedVersion),
1502 mCurrentVersion(aCurrentVersion)
1504 mTransaction->SetTransactionListener(this);
1507 NS_DECL_ISUPPORTS_INHERITED
1509 virtual nsresult GetSuccessResult(JSContext* aCx,
1510 JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
1512 virtual nsresult
1513 OnExclusiveAccessAcquired() MOZ_OVERRIDE;
1515 protected:
1516 virtual ~SetVersionHelper() {}
1518 virtual nsresult Init() MOZ_OVERRIDE;
1520 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
1521 MOZ_OVERRIDE;
1523 // SetVersionHelper never fires an error event at the request. It hands that
1524 // responsibility back to the OpenDatabaseHelper
1525 virtual void OnError() MOZ_OVERRIDE
1528 // Need an upgradeneeded event here.
1529 virtual already_AddRefed<nsIDOMEvent> CreateSuccessEvent(
1530 mozilla::dom::EventTarget* aOwner) MOZ_OVERRIDE;
1532 virtual nsresult NotifyTransactionPreComplete(IDBTransaction* aTransaction)
1533 MOZ_OVERRIDE;
1534 virtual nsresult NotifyTransactionPostComplete(IDBTransaction* aTransaction)
1535 MOZ_OVERRIDE;
1537 virtual ChildProcessSendResult
1538 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE
1540 return Success_NotSent;
1543 virtual nsresult UnpackResponseFromParentProcess(
1544 const ResponseValue& aResponseValue)
1545 MOZ_OVERRIDE
1547 MOZ_CRASH("Should never get here!");
1550 uint64_t RequestedVersion() const
1552 return mRequestedVersion;
1555 private:
1556 // In-params
1557 nsRefPtr<IDBOpenDBRequest> mOpenRequest;
1558 nsRefPtr<OpenDatabaseHelper> mOpenHelper;
1559 uint64_t mRequestedVersion;
1560 uint64_t mCurrentVersion;
1563 class DeleteDatabaseHelper : public AsyncConnectionHelper,
1564 public AcquireListener
1566 friend class VersionChangeEventsRunnable;
1567 public:
1568 DeleteDatabaseHelper(IDBOpenDBRequest* aRequest,
1569 OpenDatabaseHelper* aHelper,
1570 uint64_t aCurrentVersion,
1571 const nsAString& aName,
1572 const nsACString& aGroup,
1573 const nsACString& aASCIIOrigin,
1574 PersistenceType aPersistenceType)
1575 : AsyncConnectionHelper(static_cast<IDBDatabase*>(nullptr), aRequest),
1576 mOpenHelper(aHelper), mOpenRequest(aRequest),
1577 mCurrentVersion(aCurrentVersion), mName(aName),
1578 mGroup(aGroup), mASCIIOrigin(aASCIIOrigin),
1579 mPersistenceType(aPersistenceType)
1582 NS_DECL_ISUPPORTS_INHERITED
1584 nsresult GetSuccessResult(JSContext* aCx,
1585 JS::MutableHandle<JS::Value> aVal);
1587 void ReleaseMainThreadObjects()
1589 mOpenHelper = nullptr;
1590 mOpenRequest = nullptr;
1592 AsyncConnectionHelper::ReleaseMainThreadObjects();
1595 virtual nsresult
1596 OnExclusiveAccessAcquired() MOZ_OVERRIDE;
1598 protected:
1599 virtual ~DeleteDatabaseHelper() {}
1601 nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
1602 nsresult Init();
1604 // DeleteDatabaseHelper never fires events at the request. It hands that
1605 // responsibility back to the OpenDatabaseHelper
1606 void OnError()
1608 mOpenHelper->NotifyDeleteFinished();
1611 nsresult OnSuccess()
1613 return mOpenHelper->NotifyDeleteFinished();
1616 uint64_t RequestedVersion() const
1618 return 0;
1621 virtual ChildProcessSendResult
1622 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE
1624 return Success_NotSent;
1627 virtual nsresult UnpackResponseFromParentProcess(
1628 const ResponseValue& aResponseValue)
1629 MOZ_OVERRIDE
1631 MOZ_CRASH("Should never get here!");
1634 private:
1635 // In-params
1636 nsRefPtr<OpenDatabaseHelper> mOpenHelper;
1637 nsRefPtr<IDBOpenDBRequest> mOpenRequest;
1638 uint64_t mCurrentVersion;
1639 nsString mName;
1640 nsCString mGroup;
1641 nsCString mASCIIOrigin;
1642 PersistenceType mPersistenceType;
1645 // Responsible for firing "versionchange" events at all live and non-closed
1646 // databases, and for firing a "blocked" event at the requesting database if any
1647 // databases fail to close.
1648 class VersionChangeEventsRunnable : public nsRunnable
1650 public:
1651 VersionChangeEventsRunnable(
1652 IDBDatabase* aRequestingDatabase,
1653 IDBOpenDBRequest* aRequest,
1654 nsTArray<nsCOMPtr<nsIOfflineStorage> >& aWaitingDatabases,
1655 int64_t aOldVersion,
1656 int64_t aNewVersion)
1657 : mRequestingDatabase(aRequestingDatabase),
1658 mRequest(aRequest),
1659 mOldVersion(aOldVersion),
1660 mNewVersion(aNewVersion)
1662 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1663 NS_ASSERTION(aRequestingDatabase, "Null pointer!");
1664 NS_ASSERTION(aRequest, "Null pointer!");
1666 mWaitingDatabases.SwapElements(aWaitingDatabases);
1669 NS_IMETHOD Run()
1671 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1673 PROFILER_MAIN_THREAD_LABEL("OpenDatabaseHelper", "VersionChangeEventsRunnable::Run",
1674 js::ProfileEntry::Category::STORAGE);
1676 // Fire version change events at all of the databases that are not already
1677 // closed. Also kick bfcached documents out of bfcache.
1678 uint32_t count = mWaitingDatabases.Length();
1679 for (uint32_t index = 0; index < count; index++) {
1680 IDBDatabase* database =
1681 IDBDatabase::FromStorage(mWaitingDatabases[index]);
1682 NS_ASSERTION(database, "This shouldn't be null!");
1684 if (database->IsClosed()) {
1685 continue;
1688 // First check if the document the IDBDatabase is part of is bfcached.
1689 nsCOMPtr<nsIDocument> ownerDoc = database->GetOwnerDocument();
1690 nsIBFCacheEntry* bfCacheEntry;
1691 if (ownerDoc && (bfCacheEntry = ownerDoc->GetBFCacheEntry())) {
1692 bfCacheEntry->RemoveFromBFCacheSync();
1693 NS_ASSERTION(database->IsClosed(),
1694 "Kicking doc out of bfcache should have closed database");
1695 continue;
1698 // Next check if it's in the process of being bfcached.
1699 nsPIDOMWindow* owner = database->GetOwner();
1700 if (owner && owner->IsFrozen()) {
1701 // We can't kick the document out of the bfcache because it's not yet
1702 // fully in the bfcache. Instead we'll abort everything for the window
1703 // and mark it as not-bfcacheable.
1704 QuotaManager* quotaManager = QuotaManager::Get();
1705 NS_ASSERTION(quotaManager, "Huh?");
1706 quotaManager->AbortCloseStoragesForWindow(owner);
1708 NS_ASSERTION(database->IsClosed(),
1709 "AbortCloseStoragesForWindow should have closed database");
1710 if (ownerDoc) {
1711 ownerDoc->DisallowBFCaching();
1713 continue;
1716 // Otherwise fire a versionchange event.
1717 nsRefPtr<Event> event =
1718 IDBVersionChangeEvent::Create(database, mOldVersion, mNewVersion);
1719 NS_ENSURE_TRUE(event, NS_ERROR_FAILURE);
1721 bool dummy;
1722 database->DispatchEvent(event, &dummy);
1725 // Now check to see if any didn't close. If there are some running still
1726 // then fire the blocked event.
1727 for (uint32_t index = 0; index < count; index++) {
1728 if (!mWaitingDatabases[index]->IsClosed()) {
1729 nsRefPtr<Event> event =
1730 IDBVersionChangeEvent::CreateBlocked(mRequest,
1731 mOldVersion, mNewVersion);
1732 NS_ENSURE_TRUE(event, NS_ERROR_FAILURE);
1734 bool dummy;
1735 mRequest->DispatchEvent(event, &dummy);
1737 break;
1741 return NS_OK;
1744 template <class T>
1745 static
1746 void QueueVersionChange(nsTArray<nsCOMPtr<nsIOfflineStorage> >& aDatabases,
1747 void* aClosure);
1748 private:
1749 nsRefPtr<IDBDatabase> mRequestingDatabase;
1750 nsRefPtr<IDBOpenDBRequest> mRequest;
1751 nsTArray<nsCOMPtr<nsIOfflineStorage> > mWaitingDatabases;
1752 int64_t mOldVersion;
1753 int64_t mNewVersion;
1756 } // anonymous namespace
1758 NS_IMPL_ISUPPORTS(OpenDatabaseHelper, nsIRunnable)
1760 nsresult
1761 OpenDatabaseHelper::Init()
1763 QuotaManager::GetStorageId(mPersistenceType, mASCIIOrigin, Client::IDB,
1764 mName, mDatabaseId);
1765 MOZ_ASSERT(!mDatabaseId.IsEmpty());
1767 return NS_OK;
1770 nsresult
1771 OpenDatabaseHelper::WaitForOpenAllowed()
1773 NS_ASSERTION(mState == eCreated, "We've already been dispatched?");
1774 NS_ASSERTION(NS_IsMainThread(), "All hell is about to break lose!");
1776 mState = eOpenPending;
1778 QuotaManager* quotaManager = QuotaManager::Get();
1779 NS_ASSERTION(quotaManager, "This should never be null!");
1781 return quotaManager->
1782 WaitForOpenAllowed(OriginOrPatternString::FromOrigin(mASCIIOrigin),
1783 Nullable<PersistenceType>(mPersistenceType), mDatabaseId,
1784 this);
1787 nsresult
1788 OpenDatabaseHelper::Dispatch(nsIEventTarget* aTarget)
1790 NS_ASSERTION(mState == eCreated || mState == eOpenPending,
1791 "We've already been dispatched?");
1793 mState = eDBWork;
1795 return aTarget->Dispatch(this, NS_DISPATCH_NORMAL);
1798 nsresult
1799 OpenDatabaseHelper::DispatchToIOThread()
1801 QuotaManager* quotaManager = QuotaManager::Get();
1802 NS_ASSERTION(quotaManager, "This should never be null!");
1804 return Dispatch(quotaManager->IOThread());
1807 nsresult
1808 OpenDatabaseHelper::RunImmediately()
1810 NS_ASSERTION(mState == eCreated || mState == eOpenPending,
1811 "We've already been dispatched?");
1812 NS_ASSERTION(NS_FAILED(mResultCode),
1813 "Should only be short-circuiting if we failed!");
1814 NS_ASSERTION(NS_IsMainThread(), "All hell is about to break lose!");
1816 mState = eFiringEvents;
1818 return this->Run();
1821 nsresult
1822 OpenDatabaseHelper::DoDatabaseWork()
1824 AssertIsOnIOThread();
1825 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
1827 PROFILER_LABEL("OpenDatabaseHelper", "DoDatabaseWork",
1828 js::ProfileEntry::Category::STORAGE);
1830 mState = eFiringEvents; // In case we fail somewhere along the line.
1832 if (QuotaManager::IsShuttingDown()) {
1833 IDB_REPORT_INTERNAL_ERR();
1834 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1837 NS_ASSERTION(mOpenDBRequest, "This should never be null!");
1839 // This will be null for non-window contexts.
1840 nsPIDOMWindow* window = mOpenDBRequest->GetOwner();
1842 AutoEnterWindow autoWindow(window);
1844 nsCOMPtr<nsIFile> dbDirectory;
1846 QuotaManager* quotaManager = QuotaManager::Get();
1847 NS_ASSERTION(quotaManager, "This should never be null!");
1849 nsresult rv =
1850 quotaManager->EnsureOriginIsInitialized(mPersistenceType, mGroup,
1851 mASCIIOrigin, mTrackingQuota,
1852 getter_AddRefs(dbDirectory));
1853 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1855 rv = dbDirectory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
1856 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1858 bool exists;
1859 rv = dbDirectory->Exists(&exists);
1860 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1862 if (!exists) {
1863 rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
1864 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1866 #ifdef DEBUG
1867 else {
1868 bool isDirectory;
1869 NS_ASSERTION(NS_SUCCEEDED(dbDirectory->IsDirectory(&isDirectory)) &&
1870 isDirectory, "Should have caught this earlier!");
1872 #endif
1874 nsAutoString filename;
1875 rv = GetDatabaseFilename(mName, filename);
1876 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1878 nsCOMPtr<nsIFile> dbFile;
1879 rv = dbDirectory->Clone(getter_AddRefs(dbFile));
1880 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1882 rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite"));
1883 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1885 rv = dbFile->GetPath(mDatabaseFilePath);
1886 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1888 nsCOMPtr<nsIFile> fmDirectory;
1889 rv = dbDirectory->Clone(getter_AddRefs(fmDirectory));
1890 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1892 rv = fmDirectory->Append(filename);
1893 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1895 nsCOMPtr<mozIStorageConnection> connection;
1896 rv = CreateDatabaseConnection(dbFile, fmDirectory, mName, mPersistenceType,
1897 mGroup, mASCIIOrigin,
1898 getter_AddRefs(connection));
1899 if (NS_FAILED(rv) &&
1900 NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) {
1901 IDB_REPORT_INTERNAL_ERR();
1902 rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1904 NS_ENSURE_SUCCESS(rv, rv);
1906 rv = IDBFactory::LoadDatabaseInformation(connection, mDatabaseId,
1907 &mCurrentVersion, mObjectStores);
1908 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1910 if (mForDeletion) {
1911 mState = eDeletePending;
1912 return NS_OK;
1915 for (uint32_t i = 0; i < mObjectStores.Length(); i++) {
1916 nsRefPtr<ObjectStoreInfo>& objectStoreInfo = mObjectStores[i];
1917 for (uint32_t j = 0; j < objectStoreInfo->indexes.Length(); j++) {
1918 IndexInfo& indexInfo = objectStoreInfo->indexes[j];
1919 mLastIndexId = std::max(indexInfo.id, mLastIndexId);
1921 mLastObjectStoreId = std::max(objectStoreInfo->id, mLastObjectStoreId);
1924 // See if we need to do a VERSION_CHANGE transaction
1926 // Optional version semantics.
1927 if (!mRequestedVersion) {
1928 // If the requested version was not specified and the database was created,
1929 // treat it as if version 1 were requested.
1930 if (mCurrentVersion == 0) {
1931 mRequestedVersion = 1;
1933 else {
1934 // Otherwise, treat it as if the current version were requested.
1935 mRequestedVersion = mCurrentVersion;
1939 if (mCurrentVersion > mRequestedVersion) {
1940 return NS_ERROR_DOM_INDEXEDDB_VERSION_ERR;
1943 if (mCurrentVersion != mRequestedVersion) {
1944 mState = eSetVersionPending;
1947 IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
1948 NS_ASSERTION(mgr, "This should never be null!");
1950 nsRefPtr<FileManager> fileManager =
1951 mgr->GetFileManager(mPersistenceType, mASCIIOrigin, mName);
1952 if (!fileManager) {
1953 fileManager = new FileManager(mPersistenceType, mGroup, mASCIIOrigin,
1954 mPrivilege, mName);
1956 rv = fileManager->Init(fmDirectory, connection);
1957 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1959 mgr->AddFileManager(fileManager);
1962 mFileManager = fileManager.forget();
1964 return NS_OK;
1967 // static
1968 nsresult
1969 OpenDatabaseHelper::CreateDatabaseConnection(
1970 nsIFile* aDBFile,
1971 nsIFile* aFMDirectory,
1972 const nsAString& aName,
1973 PersistenceType aPersistenceType,
1974 const nsACString& aGroup,
1975 const nsACString& aOrigin,
1976 mozIStorageConnection** aConnection)
1978 AssertIsOnIOThread();
1979 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
1981 PROFILER_LABEL("OpenDatabaseHelper", "CreateDatabaseConnection",
1982 js::ProfileEntry::Category::STORAGE);
1984 nsresult rv;
1985 bool exists;
1987 if (IndexedDatabaseManager::InLowDiskSpaceMode()) {
1988 rv = aDBFile->Exists(&exists);
1989 NS_ENSURE_SUCCESS(rv, rv);
1991 if (!exists) {
1992 NS_WARNING("Refusing to create database because disk space is low!");
1993 return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
1997 nsCOMPtr<nsIFileURL> dbFileUrl =
1998 IDBFactory::GetDatabaseFileURL(aDBFile, aPersistenceType, aGroup, aOrigin);
1999 NS_ENSURE_TRUE(dbFileUrl, NS_ERROR_FAILURE);
2001 nsCOMPtr<mozIStorageService> ss =
2002 do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
2003 NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE);
2005 nsCOMPtr<mozIStorageConnection> connection;
2006 rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection));
2007 if (rv == NS_ERROR_FILE_CORRUPTED) {
2008 // If we're just opening the database during origin initialization, then
2009 // we don't want to erase any files. The failure here will fail origin
2010 // initialization too.
2011 if (aName.IsVoid()) {
2012 return rv;
2015 // Nuke the database file. The web services can recreate their data.
2016 rv = aDBFile->Remove(false);
2017 NS_ENSURE_SUCCESS(rv, rv);
2019 rv = aFMDirectory->Exists(&exists);
2020 NS_ENSURE_SUCCESS(rv, rv);
2022 if (exists) {
2023 bool isDirectory;
2024 rv = aFMDirectory->IsDirectory(&isDirectory);
2025 NS_ENSURE_SUCCESS(rv, rv);
2026 IDB_ENSURE_TRUE(isDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2028 rv = aFMDirectory->Remove(true);
2029 NS_ENSURE_SUCCESS(rv, rv);
2032 rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection));
2034 NS_ENSURE_SUCCESS(rv, rv);
2036 rv = IDBFactory::SetDefaultPragmas(connection);
2037 NS_ENSURE_SUCCESS(rv, rv);
2039 rv = connection->EnableModule(NS_LITERAL_CSTRING("filesystem"));
2040 NS_ENSURE_SUCCESS(rv, rv);
2042 // Check to make sure that the database schema is correct.
2043 int32_t schemaVersion;
2044 rv = connection->GetSchemaVersion(&schemaVersion);
2045 NS_ENSURE_SUCCESS(rv, rv);
2047 // Unknown schema will fail origin initialization too
2048 if (!schemaVersion && aName.IsVoid()) {
2049 IDB_WARNING("Unable to open IndexedDB database, schema is not set!");
2050 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
2053 if (schemaVersion > kSQLiteSchemaVersion) {
2054 IDB_WARNING("Unable to open IndexedDB database, schema is too high!");
2055 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
2058 bool vacuumNeeded = false;
2060 if (schemaVersion != kSQLiteSchemaVersion) {
2061 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
2062 if (!schemaVersion) {
2063 // Have to do this before opening a transaction.
2064 rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
2065 // Turn on auto_vacuum mode to reclaim disk space on mobile devices.
2066 "PRAGMA auto_vacuum = FULL; "
2068 if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
2069 // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE,
2070 // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR.
2071 rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
2073 NS_ENSURE_SUCCESS(rv, rv);
2075 #endif
2077 mozStorageTransaction transaction(connection, false,
2078 mozIStorageConnection::TRANSACTION_IMMEDIATE);
2080 if (!schemaVersion) {
2081 // Brand new file, initialize our tables.
2082 rv = CreateTables(connection);
2083 NS_ENSURE_SUCCESS(rv, rv);
2085 NS_ASSERTION(NS_SUCCEEDED(connection->GetSchemaVersion(&schemaVersion)) &&
2086 schemaVersion == kSQLiteSchemaVersion,
2087 "CreateTables set a bad schema version!");
2089 nsCOMPtr<mozIStorageStatement> stmt;
2090 nsresult rv = connection->CreateStatement(NS_LITERAL_CSTRING(
2091 "INSERT INTO database (name) "
2092 "VALUES (:name)"
2093 ), getter_AddRefs(stmt));
2094 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2096 rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName);
2097 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2099 rv = stmt->Execute();
2100 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2102 else {
2103 // This logic needs to change next time we change the schema!
2104 static_assert(kSQLiteSchemaVersion == int32_t((16 << 4) + 0),
2105 "Need upgrade code from schema version increase.");
2107 while (schemaVersion != kSQLiteSchemaVersion) {
2108 if (schemaVersion == 4) {
2109 rv = UpgradeSchemaFrom4To5(connection);
2111 else if (schemaVersion == 5) {
2112 rv = UpgradeSchemaFrom5To6(connection);
2114 else if (schemaVersion == 6) {
2115 rv = UpgradeSchemaFrom6To7(connection);
2117 else if (schemaVersion == 7) {
2118 rv = UpgradeSchemaFrom7To8(connection);
2120 else if (schemaVersion == 8) {
2121 rv = UpgradeSchemaFrom8To9_0(connection);
2122 vacuumNeeded = true;
2124 else if (schemaVersion == MakeSchemaVersion(9, 0)) {
2125 rv = UpgradeSchemaFrom9_0To10_0(connection);
2127 else if (schemaVersion == MakeSchemaVersion(10, 0)) {
2128 rv = UpgradeSchemaFrom10_0To11_0(connection);
2130 else if (schemaVersion == MakeSchemaVersion(11, 0)) {
2131 rv = UpgradeSchemaFrom11_0To12_0(connection);
2133 else if (schemaVersion == MakeSchemaVersion(12, 0)) {
2134 rv = UpgradeSchemaFrom12_0To13_0(connection, &vacuumNeeded);
2136 else if (schemaVersion == MakeSchemaVersion(13, 0)) {
2137 rv = UpgradeSchemaFrom13_0To14_0(connection);
2139 else if (schemaVersion == MakeSchemaVersion(14, 0)) {
2140 rv = UpgradeSchemaFrom14_0To15_0(connection);
2142 else if (schemaVersion == MakeSchemaVersion(15, 0)) {
2143 rv = UpgradeSchemaFrom15_0To16_0(connection);
2145 else {
2146 NS_WARNING("Unable to open IndexedDB database, no upgrade path is "
2147 "available!");
2148 IDB_REPORT_INTERNAL_ERR();
2149 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
2151 NS_ENSURE_SUCCESS(rv, rv);
2153 rv = connection->GetSchemaVersion(&schemaVersion);
2154 NS_ENSURE_SUCCESS(rv, rv);
2157 NS_ASSERTION(schemaVersion == kSQLiteSchemaVersion, "Huh?!");
2160 rv = transaction.Commit();
2161 if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
2162 // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE,
2163 // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR.
2164 rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
2166 NS_ENSURE_SUCCESS(rv, rv);
2169 if (vacuumNeeded) {
2170 rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM;"));
2171 NS_ENSURE_SUCCESS(rv, rv);
2174 connection.forget(aConnection);
2175 return NS_OK;
2178 nsresult
2179 OpenDatabaseHelper::StartSetVersion()
2181 NS_ASSERTION(mState == eSetVersionPending, "Why are we here?");
2183 // In case we fail, fire error events
2184 mState = eFiringEvents;
2186 nsresult rv = EnsureSuccessResult();
2187 NS_ENSURE_SUCCESS(rv, rv);
2189 Sequence<nsString> storesToOpen;
2190 nsRefPtr<IDBTransaction> transaction =
2191 IDBTransaction::Create(mDatabase, storesToOpen,
2192 IDBTransaction::VERSION_CHANGE, true);
2193 IDB_ENSURE_TRUE(transaction, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2195 nsRefPtr<SetVersionHelper> helper =
2196 new SetVersionHelper(transaction, mOpenDBRequest, this, mRequestedVersion,
2197 mCurrentVersion);
2199 QuotaManager* quotaManager = QuotaManager::Get();
2200 NS_ASSERTION(quotaManager, "This should never be null!");
2202 rv = quotaManager->AcquireExclusiveAccess(
2203 mDatabase, mDatabase->Origin(),
2204 Nullable<PersistenceType>(mDatabase->Type()), helper,
2205 &VersionChangeEventsRunnable::QueueVersionChange<SetVersionHelper>,
2206 helper);
2207 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2209 // The SetVersionHelper is responsible for dispatching us back to the
2210 // main thread again and changing the state to eSetVersionCompleted.
2211 mState = eSetVersionPending;
2212 return NS_OK;
2215 nsresult
2216 OpenDatabaseHelper::StartDelete()
2218 NS_ASSERTION(mState == eDeletePending, "Why are we here?");
2220 // In case we fail, fire error events
2221 mState = eFiringEvents;
2223 nsresult rv = EnsureSuccessResult();
2224 NS_ENSURE_SUCCESS(rv, rv);
2226 nsRefPtr<DeleteDatabaseHelper> helper =
2227 new DeleteDatabaseHelper(mOpenDBRequest, this, mCurrentVersion, mName,
2228 mGroup, mASCIIOrigin, mPersistenceType);
2230 QuotaManager* quotaManager = QuotaManager::Get();
2231 NS_ASSERTION(quotaManager, "This should never be null!");
2233 rv = quotaManager->AcquireExclusiveAccess(
2234 mDatabase, mDatabase->Origin(),
2235 Nullable<PersistenceType>(mDatabase->Type()), helper,
2236 &VersionChangeEventsRunnable::QueueVersionChange<DeleteDatabaseHelper>,
2237 helper);
2238 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2240 // The DeleteDatabaseHelper is responsible for dispatching us back to the
2241 // main thread again and changing the state to eDeleteCompleted.
2242 mState = eDeletePending;
2243 return NS_OK;
2246 NS_IMETHODIMP
2247 OpenDatabaseHelper::Run()
2249 NS_ASSERTION(mState != eCreated, "Dispatch was not called?!?");
2251 if (NS_IsMainThread()) {
2252 PROFILER_MAIN_THREAD_LABEL("OpenDatabaseHelper", "Run",
2253 js::ProfileEntry::Category::STORAGE);
2255 if (mState == eOpenPending) {
2256 if (NS_FAILED(mResultCode)) {
2257 return RunImmediately();
2260 return DispatchToIOThread();
2263 // If we need to queue up a SetVersionHelper, do that here.
2264 if (mState == eSetVersionPending) {
2265 nsresult rv = StartSetVersion();
2267 if (NS_SUCCEEDED(rv)) {
2268 return rv;
2271 SetError(rv);
2272 // fall through and run the default error processing
2274 else if (mState == eDeletePending) {
2275 nsresult rv = StartDelete();
2277 if (NS_SUCCEEDED(rv)) {
2278 return rv;
2281 SetError(rv);
2282 // fall through and run the default error processing
2285 // We've done whatever work we need to do on the DB thread, and any
2286 // SetVersion/DeleteDatabase stuff is done by now.
2287 NS_ASSERTION(mState == eFiringEvents ||
2288 mState == eSetVersionCompleted ||
2289 mState == eDeleteCompleted, "Why are we here?");
2291 switch (mState) {
2292 case eSetVersionCompleted: {
2293 mState = eFiringEvents;
2294 break;
2297 case eDeleteCompleted: {
2298 // Destroy the database now (we should have the only ref).
2299 mDatabase = nullptr;
2301 DatabaseInfo::Remove(mDatabaseId);
2303 mState = eFiringEvents;
2304 break;
2307 case eFiringEvents: {
2308 // Notify the request that we're done, but only if we didn't just
2309 // finish a [SetVersion/DeleteDatabase]Helper. In that case, the
2310 // helper tells the request that it is done, and we avoid calling
2311 // NotifyHelperCompleted twice.
2313 nsresult rv = mOpenDBRequest->NotifyHelperCompleted(this);
2314 if (NS_SUCCEEDED(mResultCode) && NS_FAILED(rv)) {
2315 mResultCode = rv;
2317 break;
2320 default:
2321 NS_NOTREACHED("Shouldn't get here!");
2324 NS_ASSERTION(mState == eFiringEvents, "Why are we here?");
2326 IDB_PROFILER_MARK("IndexedDB Request %llu: Running main thread "
2327 "response (rv = %lu)",
2328 "IDBRequest[%llu] MT Done",
2329 mRequest->GetSerialNumber(), mResultCode);
2331 if (NS_FAILED(mResultCode)) {
2332 DispatchErrorEvent();
2333 } else {
2334 DispatchSuccessEvent();
2337 QuotaManager* quotaManager = QuotaManager::Get();
2338 NS_ASSERTION(quotaManager, "This should never be null!");
2340 quotaManager->
2341 AllowNextSynchronizedOp(OriginOrPatternString::FromOrigin(mASCIIOrigin),
2342 Nullable<PersistenceType>(mPersistenceType),
2343 mDatabaseId);
2345 ReleaseMainThreadObjects();
2347 return NS_OK;
2350 PROFILER_LABEL("OpenDatabaseHelper", "Run",
2351 js::ProfileEntry::Category::STORAGE);
2353 // We're on the DB thread.
2354 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
2356 IDB_PROFILER_MARK("IndexedDB Request %llu: Beginning database work",
2357 "IDBRequest[%llu] DT Start", mRequest->GetSerialNumber());
2359 NS_ASSERTION(mState == eDBWork, "Why are we here?");
2360 mResultCode = DoDatabaseWork();
2361 NS_ASSERTION(mState != eDBWork, "We should be doing something else now.");
2363 IDB_PROFILER_MARK("IndexedDB Request %llu: Finished database work (rv = %lu)",
2364 "IDBRequest[%llu] DT Done", mRequest->GetSerialNumber(),
2365 mResultCode);
2367 return NS_DispatchToMainThread(this);
2370 nsresult
2371 OpenDatabaseHelper::EnsureSuccessResult()
2373 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2375 PROFILER_MAIN_THREAD_LABEL("OpenDatabaseHelper", "EnsureSuccessResult",
2376 js::ProfileEntry::Category::STORAGE);
2378 nsRefPtr<DatabaseInfo> dbInfo;
2379 if (DatabaseInfo::Get(mDatabaseId, getter_AddRefs(dbInfo))) {
2381 #ifdef DEBUG
2383 NS_ASSERTION(dbInfo->name == mName &&
2384 dbInfo->version == mCurrentVersion &&
2385 dbInfo->persistenceType == mPersistenceType &&
2386 dbInfo->id == mDatabaseId &&
2387 dbInfo->filePath == mDatabaseFilePath,
2388 "Metadata mismatch!");
2390 uint32_t objectStoreCount = mObjectStores.Length();
2391 for (uint32_t index = 0; index < objectStoreCount; index++) {
2392 nsRefPtr<ObjectStoreInfo>& info = mObjectStores[index];
2394 ObjectStoreInfo* otherInfo = dbInfo->GetObjectStore(info->name);
2395 NS_ASSERTION(otherInfo, "ObjectStore not known!");
2397 NS_ASSERTION(info->name == otherInfo->name &&
2398 info->id == otherInfo->id &&
2399 info->keyPath == otherInfo->keyPath,
2400 "Metadata mismatch!");
2401 NS_ASSERTION(dbInfo->ContainsStoreName(info->name),
2402 "Object store names out of date!");
2403 NS_ASSERTION(info->indexes.Length() == otherInfo->indexes.Length(),
2404 "Bad index length!");
2406 uint32_t indexCount = info->indexes.Length();
2407 for (uint32_t indexIndex = 0; indexIndex < indexCount; indexIndex++) {
2408 const IndexInfo& indexInfo = info->indexes[indexIndex];
2409 const IndexInfo& otherIndexInfo = otherInfo->indexes[indexIndex];
2410 NS_ASSERTION(indexInfo.id == otherIndexInfo.id,
2411 "Bad index id!");
2412 NS_ASSERTION(indexInfo.name == otherIndexInfo.name,
2413 "Bad index name!");
2414 NS_ASSERTION(indexInfo.keyPath == otherIndexInfo.keyPath,
2415 "Bad index keyPath!");
2416 NS_ASSERTION(indexInfo.unique == otherIndexInfo.unique,
2417 "Bad index unique value!");
2421 #endif
2424 else {
2425 nsRefPtr<DatabaseInfo> newInfo(new DatabaseInfo());
2427 newInfo->name = mName;
2428 newInfo->group = mGroup;
2429 newInfo->origin = mASCIIOrigin;
2430 newInfo->persistenceType = mPersistenceType;
2431 newInfo->id = mDatabaseId;
2432 newInfo->filePath = mDatabaseFilePath;
2434 if (!DatabaseInfo::Put(newInfo)) {
2435 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
2438 newInfo.swap(dbInfo);
2440 nsresult rv = IDBFactory::SetDatabaseMetadata(dbInfo, mCurrentVersion,
2441 mObjectStores);
2442 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2444 NS_ASSERTION(mObjectStores.IsEmpty(), "Should have swapped!");
2447 dbInfo->nextObjectStoreId = mLastObjectStoreId + 1;
2448 dbInfo->nextIndexId = mLastIndexId + 1;
2450 nsRefPtr<IDBDatabase> database =
2451 IDBDatabase::Create(mOpenDBRequest, mOpenDBRequest->Factory(),
2452 dbInfo.forget(), mASCIIOrigin, mFileManager,
2453 mContentParent);
2454 if (!database) {
2455 IDB_REPORT_INTERNAL_ERR();
2456 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
2459 NS_ASSERTION(!mDatabase, "Shouldn't have a database yet!");
2460 mDatabase.swap(database);
2462 return NS_OK;
2465 nsresult
2466 OpenDatabaseHelper::GetSuccessResult(JSContext* aCx,
2467 JS::MutableHandle<JS::Value> aVal)
2469 // Be careful not to load the database twice.
2470 if (!mDatabase) {
2471 nsresult rv = EnsureSuccessResult();
2472 NS_ENSURE_SUCCESS(rv, rv);
2475 return WrapNative(aCx, NS_ISUPPORTS_CAST(EventTarget*, mDatabase),
2476 aVal);
2479 nsresult
2480 OpenDatabaseHelper::NotifySetVersionFinished()
2482 NS_ASSERTION(NS_IsMainThread(), "Wrong thread");
2483 NS_ASSERTION(mState = eSetVersionPending, "How did we get here?");
2485 // Allow transaction creation to proceed.
2486 mDatabase->ExitSetVersionTransaction();
2488 mState = eSetVersionCompleted;
2490 // Dispatch ourself back to the main thread
2491 return NS_DispatchToCurrentThread(this);
2494 nsresult
2495 OpenDatabaseHelper::NotifyDeleteFinished()
2497 NS_ASSERTION(NS_IsMainThread(), "Wrong thread");
2498 NS_ASSERTION(mState == eDeletePending, "How did we get here?");
2500 mState = eDeleteCompleted;
2502 // Dispatch ourself back to the main thread
2503 return NS_DispatchToCurrentThread(this);
2506 void
2507 OpenDatabaseHelper::BlockDatabase()
2509 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2510 NS_ASSERTION(mDatabase, "This is going bad fast.");
2512 mDatabase->EnterSetVersionTransaction();
2515 void
2516 OpenDatabaseHelper::DispatchSuccessEvent()
2518 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2520 PROFILER_MAIN_THREAD_LABEL("OpenDatabaseHelper", "DispatchSuccessEvent",
2521 js::ProfileEntry::Category::STORAGE);
2523 nsRefPtr<nsIDOMEvent> event =
2524 CreateGenericEvent(mOpenDBRequest, NS_LITERAL_STRING(SUCCESS_EVT_STR),
2525 eDoesNotBubble, eNotCancelable);
2526 if (!event) {
2527 NS_ERROR("Failed to create event!");
2528 return;
2531 bool dummy;
2532 mOpenDBRequest->DispatchEvent(event, &dummy);
2535 void
2536 OpenDatabaseHelper::DispatchErrorEvent()
2538 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2540 PROFILER_MAIN_THREAD_LABEL("OpenDatabaseHelper", "DispatchErrorEvent",
2541 js::ProfileEntry::Category::STORAGE);
2543 nsRefPtr<nsIDOMEvent> event =
2544 CreateGenericEvent(mOpenDBRequest, NS_LITERAL_STRING(ERROR_EVT_STR),
2545 eDoesBubble, eCancelable);
2546 if (!event) {
2547 NS_ERROR("Failed to create event!");
2548 return;
2551 ErrorResult rv;
2552 nsRefPtr<DOMError> error = mOpenDBRequest->GetError(rv);
2554 NS_ASSERTION(!rv.Failed(), "This shouldn't be failing at this point!");
2555 if (!error) {
2556 mOpenDBRequest->SetError(mResultCode);
2559 bool dummy;
2560 mOpenDBRequest->DispatchEvent(event, &dummy);
2563 void
2564 OpenDatabaseHelper::ReleaseMainThreadObjects()
2566 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2568 mOpenDBRequest = nullptr;
2569 mDatabase = nullptr;
2571 HelperBase::ReleaseMainThreadObjects();
2574 NS_IMPL_ISUPPORTS_INHERITED0(SetVersionHelper, AsyncConnectionHelper)
2576 nsresult
2577 SetVersionHelper::Init()
2579 // Block transaction creation until we are done.
2580 mOpenHelper->BlockDatabase();
2582 return NS_OK;
2585 nsresult
2586 SetVersionHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
2588 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
2589 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
2590 NS_ASSERTION(aConnection, "Passing a null connection!");
2592 PROFILER_LABEL("SetVersionHelper", "DoDatabaseWork",
2593 js::ProfileEntry::Category::STORAGE);
2595 nsCOMPtr<mozIStorageStatement> stmt;
2596 nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
2597 "UPDATE database "
2598 "SET version = :version"
2599 ), getter_AddRefs(stmt));
2600 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2602 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("version"),
2603 mRequestedVersion);
2604 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2606 if (NS_FAILED(stmt->Execute())) {
2607 return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
2610 return NS_OK;
2613 nsresult
2614 SetVersionHelper::GetSuccessResult(JSContext* aCx,
2615 JS::MutableHandle<JS::Value> aVal)
2617 DatabaseInfo* info = mDatabase->Info();
2618 info->version = mRequestedVersion;
2620 NS_ASSERTION(mTransaction, "Better have a transaction!");
2622 mOpenRequest->SetTransaction(mTransaction);
2624 return WrapNative(aCx, NS_ISUPPORTS_CAST(EventTarget*, mDatabase),
2625 aVal);
2628 nsresult
2629 SetVersionHelper::OnExclusiveAccessAcquired()
2631 nsresult rv = DispatchToTransactionPool();
2632 NS_ENSURE_SUCCESS(rv, rv);
2634 return NS_OK;
2637 // static
2638 template <class T>
2639 void
2640 VersionChangeEventsRunnable::QueueVersionChange(
2641 nsTArray<nsCOMPtr<nsIOfflineStorage> >& aDatabases,
2642 void* aClosure)
2644 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2645 NS_ASSERTION(!aDatabases.IsEmpty(), "Why are we here?");
2647 T* closure = static_cast<T*>(aClosure);
2649 nsRefPtr<VersionChangeEventsRunnable> eventsRunnable =
2650 new VersionChangeEventsRunnable(closure->mOpenHelper->Database(),
2651 closure->mOpenRequest,
2652 aDatabases,
2653 closure->mCurrentVersion,
2654 closure->RequestedVersion());
2656 NS_DispatchToCurrentThread(eventsRunnable);
2659 already_AddRefed<nsIDOMEvent>
2660 SetVersionHelper::CreateSuccessEvent(mozilla::dom::EventTarget* aOwner)
2662 NS_ASSERTION(mCurrentVersion < mRequestedVersion, "Huh?");
2664 return IDBVersionChangeEvent::CreateUpgradeNeeded(aOwner,
2665 mCurrentVersion,
2666 mRequestedVersion);
2669 nsresult
2670 SetVersionHelper::NotifyTransactionPreComplete(IDBTransaction* aTransaction)
2672 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2673 NS_ASSERTION(aTransaction, "This is unexpected.");
2674 NS_ASSERTION(mOpenRequest, "Why don't we have a request?");
2676 return mOpenHelper->NotifySetVersionFinished();
2679 nsresult
2680 SetVersionHelper::NotifyTransactionPostComplete(IDBTransaction* aTransaction)
2682 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2683 NS_ASSERTION(aTransaction, "This is unexpected.");
2684 NS_ASSERTION(mOpenRequest, "Why don't we have a request?");
2686 // If we hit an error, the OpenDatabaseHelper needs to get that error too.
2687 nsresult rv = GetResultCode();
2688 if (NS_FAILED(rv)) {
2689 mOpenHelper->SetError(rv);
2692 // If the transaction was aborted, we should throw an error message.
2693 if (aTransaction->IsAborted()) {
2694 mOpenHelper->SetError(aTransaction->GetAbortCode());
2697 mOpenRequest->SetTransaction(nullptr);
2698 mOpenRequest = nullptr;
2700 mOpenHelper = nullptr;
2702 return rv;
2705 NS_IMPL_ISUPPORTS_INHERITED0(DeleteDatabaseHelper, AsyncConnectionHelper);
2707 nsresult
2708 DeleteDatabaseHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
2710 AssertIsOnIOThread();
2711 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
2712 NS_ASSERTION(!aConnection, "How did we get a connection here?");
2714 PROFILER_LABEL("DeleteDatabaseHelper", "DoDatabaseWork",
2715 js::ProfileEntry::Category::STORAGE);
2717 const StoragePrivilege& privilege = mOpenHelper->Privilege();
2719 QuotaManager* quotaManager = QuotaManager::Get();
2720 NS_ASSERTION(quotaManager, "This should never fail!");
2722 nsCOMPtr<nsIFile> directory;
2723 nsresult rv = quotaManager->GetDirectoryForOrigin(mPersistenceType,
2724 mASCIIOrigin,
2725 getter_AddRefs(directory));
2726 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2728 NS_ASSERTION(directory, "What?");
2730 rv = directory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
2731 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2733 nsAutoString filename;
2734 rv = GetDatabaseFilename(mName, filename);
2735 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2737 nsCOMPtr<nsIFile> dbFile;
2738 rv = directory->Clone(getter_AddRefs(dbFile));
2739 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2741 rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite"));
2742 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2744 bool exists = false;
2745 rv = dbFile->Exists(&exists);
2746 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2748 if (exists) {
2749 int64_t fileSize;
2751 if (privilege != Chrome) {
2752 rv = dbFile->GetFileSize(&fileSize);
2753 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2756 rv = dbFile->Remove(false);
2757 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2759 if (privilege != Chrome) {
2760 QuotaManager* quotaManager = QuotaManager::Get();
2761 NS_ASSERTION(quotaManager, "Shouldn't be null!");
2763 quotaManager->DecreaseUsageForOrigin(mPersistenceType, mGroup,
2764 mASCIIOrigin, fileSize);
2768 nsCOMPtr<nsIFile> dbJournalFile;
2769 rv = directory->Clone(getter_AddRefs(dbJournalFile));
2770 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2772 rv = dbJournalFile->Append(filename + NS_LITERAL_STRING(".sqlite-journal"));
2773 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2775 rv = dbJournalFile->Exists(&exists);
2776 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2778 if (exists) {
2779 rv = dbJournalFile->Remove(false);
2780 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2783 nsCOMPtr<nsIFile> fmDirectory;
2784 rv = directory->Clone(getter_AddRefs(fmDirectory));
2785 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2787 rv = fmDirectory->Append(filename);
2788 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2790 rv = fmDirectory->Exists(&exists);
2791 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2793 if (exists) {
2794 bool isDirectory;
2795 rv = fmDirectory->IsDirectory(&isDirectory);
2796 NS_ENSURE_SUCCESS(rv, rv);
2797 IDB_ENSURE_TRUE(isDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2799 uint64_t usage = 0;
2801 if (privilege != Chrome) {
2802 rv = FileManager::GetUsage(fmDirectory, &usage);
2803 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2806 rv = fmDirectory->Remove(true);
2807 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2809 if (privilege != Chrome) {
2810 QuotaManager* quotaManager = QuotaManager::Get();
2811 NS_ASSERTION(quotaManager, "Shouldn't be null!");
2813 quotaManager->DecreaseUsageForOrigin(mPersistenceType, mGroup,
2814 mASCIIOrigin, usage);
2818 IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
2819 NS_ASSERTION(mgr, "This should never fail!");
2821 mgr->InvalidateFileManager(mPersistenceType, mASCIIOrigin, mName);
2823 return NS_OK;
2826 nsresult
2827 DeleteDatabaseHelper::GetSuccessResult(JSContext* aCx, JS::MutableHandle<JS::Value> aVal)
2829 return NS_OK;
2832 nsresult
2833 DeleteDatabaseHelper::OnExclusiveAccessAcquired()
2835 QuotaManager* quotaManager = QuotaManager::Get();
2836 NS_ASSERTION(quotaManager, "We should definitely have a manager here");
2838 nsresult rv = Dispatch(quotaManager->IOThread());
2839 NS_ENSURE_SUCCESS(rv, rv);
2841 return NS_OK;
2844 nsresult
2845 DeleteDatabaseHelper::Init()
2847 // Note that there's no need to block the database here, since the page
2848 // never gets to touch it, and all other databases must be closed.
2850 return NS_OK;