Bug 1729395 - Handle message sender going away during message processing r=robwu
[gecko.git] / dom / indexedDB / SchemaUpgrades.cpp
blob846771dfbd7c1537a509d7608449b3ee3bbb312f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "SchemaUpgrades.h"
9 // local includes
10 #include "ActorsParentCommon.h"
11 #include "DatabaseFileInfo.h"
12 #include "DatabaseFileManager.h"
13 #include "DBSchema.h"
14 #include "IndexedDatabase.h"
15 #include "IndexedDatabaseInlines.h"
16 #include "IndexedDBCommon.h"
17 #include "ReportInternalError.h"
19 // global includes
20 #include <stdlib.h>
21 #include <algorithm>
22 #include <tuple>
23 #include <type_traits>
24 #include <utility>
25 #include "ErrorList.h"
26 #include "MainThreadUtils.h"
27 #include "SafeRefPtr.h"
28 #include "js/RootingAPI.h"
29 #include "js/StructuredClone.h"
30 #include "js/Value.h"
31 #include "jsapi.h"
32 #include "mozIStorageConnection.h"
33 #include "mozIStorageFunction.h"
34 #include "mozIStorageStatement.h"
35 #include "mozIStorageValueArray.h"
36 #include "mozStorageHelper.h"
37 #include "mozilla/Assertions.h"
38 #include "mozilla/ErrorResult.h"
39 #include "mozilla/MacroForEach.h"
40 #include "mozilla/Monitor.h"
41 #include "mozilla/OriginAttributes.h"
42 #include "mozilla/RefPtr.h"
43 #include "mozilla/SchedulerGroup.h"
44 #include "mozilla/Span.h"
45 #include "mozilla/TaskCategory.h"
46 #include "mozilla/UniquePtr.h"
47 #include "mozilla/dom/ScriptSettings.h"
48 #include "mozilla/dom/indexedDB/IDBResult.h"
49 #include "mozilla/dom/indexedDB/Key.h"
50 #include "mozilla/dom/quota/PersistenceType.h"
51 #include "mozilla/dom/quota/QuotaCommon.h"
52 #include "mozilla/fallible.h"
53 #include "mozilla/ipc/BackgroundParent.h"
54 #include "mozilla/mozalloc.h"
55 #include "mozilla/ProfilerLabels.h"
56 #include "mozilla/storage/Variant.h"
57 #include "nsCOMPtr.h"
58 #include "nsDebug.h"
59 #include "nsError.h"
60 #include "nsISupports.h"
61 #include "nsIVariant.h"
62 #include "nsLiteralString.h"
63 #include "nsString.h"
64 #include "nsTArray.h"
65 #include "nsTLiteralString.h"
66 #include "nsTStringRepr.h"
67 #include "nsThreadUtils.h"
68 #include "nscore.h"
69 #include "snappy/snappy.h"
71 struct JSContext;
72 class JSObject;
74 #if defined(MOZ_WIDGET_ANDROID)
75 # define IDB_MOBILE
76 #endif
78 namespace mozilla::dom::indexedDB {
80 using mozilla::ipc::IsOnBackgroundThread;
81 using quota::AssertIsOnIOThread;
82 using quota::PERSISTENCE_TYPE_INVALID;
84 namespace {
86 nsresult UpgradeSchemaFrom4To5(mozIStorageConnection& aConnection) {
87 AssertIsOnIOThread();
89 AUTO_PROFILER_LABEL("UpgradeSchemaFrom4To5", DOM);
91 nsresult rv;
93 // All we changed is the type of the version column, so lets try to
94 // convert that to an integer, and if we fail, set it to 0.
95 nsCOMPtr<mozIStorageStatement> stmt;
96 rv = aConnection.CreateStatement(
97 "SELECT name, version, dataVersion "
98 "FROM database"_ns,
99 getter_AddRefs(stmt));
100 if (NS_WARN_IF(NS_FAILED(rv))) {
101 return rv;
104 nsString name;
105 int32_t intVersion;
106 int64_t dataVersion;
109 mozStorageStatementScoper scoper(stmt);
111 QM_TRY_INSPECT(const bool& hasResults,
112 MOZ_TO_RESULT_INVOKE(stmt, ExecuteStep));
114 if (NS_WARN_IF(!hasResults)) {
115 return NS_ERROR_FAILURE;
118 nsString version;
119 rv = stmt->GetString(1, version);
120 if (NS_WARN_IF(NS_FAILED(rv))) {
121 return rv;
124 intVersion = version.ToInteger(&rv);
125 if (NS_FAILED(rv)) {
126 intVersion = 0;
129 rv = stmt->GetString(0, name);
130 if (NS_WARN_IF(NS_FAILED(rv))) {
131 return rv;
134 rv = stmt->GetInt64(2, &dataVersion);
135 if (NS_WARN_IF(NS_FAILED(rv))) {
136 return rv;
140 rv = aConnection.ExecuteSimpleSQL("DROP TABLE database"_ns);
141 if (NS_WARN_IF(NS_FAILED(rv))) {
142 return rv;
145 rv = aConnection.ExecuteSimpleSQL(
146 "CREATE TABLE database ("
147 "name TEXT NOT NULL, "
148 "version INTEGER NOT NULL DEFAULT 0, "
149 "dataVersion INTEGER NOT NULL"
150 ");"_ns);
151 if (NS_WARN_IF(NS_FAILED(rv))) {
152 return rv;
155 // The parameter names are not used, parameters are bound by index only
156 // locally in the same function.
157 rv = aConnection.CreateStatement(
158 "INSERT INTO database (name, version, dataVersion) "
159 "VALUES (:name, :version, :dataVersion)"_ns,
160 getter_AddRefs(stmt));
161 if (NS_WARN_IF(NS_FAILED(rv))) {
162 return rv;
166 mozStorageStatementScoper scoper(stmt);
168 rv = stmt->BindStringByIndex(0, name);
169 if (NS_WARN_IF(NS_FAILED(rv))) {
170 return rv;
173 rv = stmt->BindInt32ByIndex(1, intVersion);
174 if (NS_WARN_IF(NS_FAILED(rv))) {
175 return rv;
178 rv = stmt->BindInt64ByIndex(2, dataVersion);
179 if (NS_WARN_IF(NS_FAILED(rv))) {
180 return rv;
183 rv = stmt->Execute();
184 if (NS_WARN_IF(NS_FAILED(rv))) {
185 return rv;
189 rv = aConnection.SetSchemaVersion(5);
190 if (NS_WARN_IF(NS_FAILED(rv))) {
191 return rv;
194 return NS_OK;
197 nsresult UpgradeSchemaFrom5To6(mozIStorageConnection& aConnection) {
198 AssertIsOnIOThread();
200 AUTO_PROFILER_LABEL("UpgradeSchemaFrom5To6", DOM);
202 // First, drop all the indexes we're no longer going to use.
203 nsresult rv = aConnection.ExecuteSimpleSQL("DROP INDEX key_index;"_ns);
204 if (NS_WARN_IF(NS_FAILED(rv))) {
205 return rv;
208 rv = aConnection.ExecuteSimpleSQL("DROP INDEX ai_key_index;"_ns);
209 if (NS_WARN_IF(NS_FAILED(rv))) {
210 return rv;
213 rv = aConnection.ExecuteSimpleSQL("DROP INDEX value_index;"_ns);
214 if (NS_WARN_IF(NS_FAILED(rv))) {
215 return rv;
218 rv = aConnection.ExecuteSimpleSQL("DROP INDEX ai_value_index;"_ns);
219 if (NS_WARN_IF(NS_FAILED(rv))) {
220 return rv;
223 // Now, reorder the columns of object_data to put the blob data last. We do
224 // this by copying into a temporary table, dropping the original, then copying
225 // back into a newly created table.
226 rv = aConnection.ExecuteSimpleSQL(
227 "CREATE TEMPORARY TABLE temp_upgrade ("
228 "id INTEGER PRIMARY KEY, "
229 "object_store_id, "
230 "key_value, "
231 "data "
232 ");"_ns);
233 if (NS_WARN_IF(NS_FAILED(rv))) {
234 return rv;
237 rv = aConnection.ExecuteSimpleSQL(
238 "INSERT INTO temp_upgrade "
239 "SELECT id, object_store_id, key_value, data "
240 "FROM object_data;"_ns);
241 if (NS_WARN_IF(NS_FAILED(rv))) {
242 return rv;
245 rv = aConnection.ExecuteSimpleSQL("DROP TABLE object_data;"_ns);
246 if (NS_WARN_IF(NS_FAILED(rv))) {
247 return rv;
250 rv = aConnection.ExecuteSimpleSQL(
251 "CREATE TABLE object_data ("
252 "id INTEGER PRIMARY KEY, "
253 "object_store_id INTEGER NOT NULL, "
254 "key_value DEFAULT NULL, "
255 "data BLOB NOT NULL, "
256 "UNIQUE (object_store_id, key_value), "
257 "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
258 "CASCADE"
259 ");"_ns);
260 if (NS_WARN_IF(NS_FAILED(rv))) {
261 return rv;
264 rv = aConnection.ExecuteSimpleSQL(
265 "INSERT INTO object_data "
266 "SELECT id, object_store_id, key_value, data "
267 "FROM temp_upgrade;"_ns);
268 if (NS_WARN_IF(NS_FAILED(rv))) {
269 return rv;
272 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
273 if (NS_WARN_IF(NS_FAILED(rv))) {
274 return rv;
277 // We need to add a unique constraint to our ai_object_data table. Copy all
278 // the data out of it using a temporary table as before.
279 rv = aConnection.ExecuteSimpleSQL(
280 "CREATE TEMPORARY TABLE temp_upgrade ("
281 "id INTEGER PRIMARY KEY, "
282 "object_store_id, "
283 "data "
284 ");"_ns);
285 if (NS_WARN_IF(NS_FAILED(rv))) {
286 return rv;
289 rv = aConnection.ExecuteSimpleSQL(
290 "INSERT INTO temp_upgrade "
291 "SELECT id, object_store_id, data "
292 "FROM ai_object_data;"_ns);
293 if (NS_WARN_IF(NS_FAILED(rv))) {
294 return rv;
297 rv = aConnection.ExecuteSimpleSQL("DROP TABLE ai_object_data;"_ns);
298 if (NS_WARN_IF(NS_FAILED(rv))) {
299 return rv;
302 rv = aConnection.ExecuteSimpleSQL(
303 "CREATE TABLE ai_object_data ("
304 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
305 "object_store_id INTEGER NOT NULL, "
306 "data BLOB NOT NULL, "
307 "UNIQUE (object_store_id, id), "
308 "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
309 "CASCADE"
310 ");"_ns);
311 if (NS_WARN_IF(NS_FAILED(rv))) {
312 return rv;
315 rv = aConnection.ExecuteSimpleSQL(
316 "INSERT INTO ai_object_data "
317 "SELECT id, object_store_id, data "
318 "FROM temp_upgrade;"_ns);
319 if (NS_WARN_IF(NS_FAILED(rv))) {
320 return rv;
323 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
324 if (NS_WARN_IF(NS_FAILED(rv))) {
325 return rv;
328 // Fix up the index_data table. We're reordering the columns as well as
329 // changing the primary key from being a simple id to being a composite.
330 rv = aConnection.ExecuteSimpleSQL(
331 "CREATE TEMPORARY TABLE temp_upgrade ("
332 "index_id, "
333 "value, "
334 "object_data_key, "
335 "object_data_id "
336 ");"_ns);
337 if (NS_WARN_IF(NS_FAILED(rv))) {
338 return rv;
341 rv = aConnection.ExecuteSimpleSQL(
342 "INSERT INTO temp_upgrade "
343 "SELECT index_id, value, object_data_key, object_data_id "
344 "FROM index_data;"_ns);
345 if (NS_WARN_IF(NS_FAILED(rv))) {
346 return rv;
349 rv = aConnection.ExecuteSimpleSQL("DROP TABLE index_data;"_ns);
350 if (NS_WARN_IF(NS_FAILED(rv))) {
351 return rv;
354 rv = aConnection.ExecuteSimpleSQL(
355 "CREATE TABLE index_data ("
356 "index_id INTEGER NOT NULL, "
357 "value NOT NULL, "
358 "object_data_key NOT NULL, "
359 "object_data_id INTEGER NOT NULL, "
360 "PRIMARY KEY (index_id, value, object_data_key), "
361 "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
362 "CASCADE, "
363 "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
364 "CASCADE"
365 ");"_ns);
366 if (NS_WARN_IF(NS_FAILED(rv))) {
367 return rv;
370 rv = aConnection.ExecuteSimpleSQL(
371 "INSERT OR IGNORE INTO index_data "
372 "SELECT index_id, value, object_data_key, object_data_id "
373 "FROM temp_upgrade;"_ns);
374 if (NS_WARN_IF(NS_FAILED(rv))) {
375 return rv;
378 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
379 if (NS_WARN_IF(NS_FAILED(rv))) {
380 return rv;
383 rv = aConnection.ExecuteSimpleSQL(
384 "CREATE INDEX index_data_object_data_id_index "
385 "ON index_data (object_data_id);"_ns);
386 if (NS_WARN_IF(NS_FAILED(rv))) {
387 return rv;
390 // Fix up the unique_index_data table. We're reordering the columns as well as
391 // changing the primary key from being a simple id to being a composite.
392 rv = aConnection.ExecuteSimpleSQL(
393 "CREATE TEMPORARY TABLE temp_upgrade ("
394 "index_id, "
395 "value, "
396 "object_data_key, "
397 "object_data_id "
398 ");"_ns);
399 if (NS_WARN_IF(NS_FAILED(rv))) {
400 return rv;
403 rv = aConnection.ExecuteSimpleSQL(
404 "INSERT INTO temp_upgrade "
405 "SELECT index_id, value, object_data_key, object_data_id "
406 "FROM unique_index_data;"_ns);
407 if (NS_WARN_IF(NS_FAILED(rv))) {
408 return rv;
411 rv = aConnection.ExecuteSimpleSQL("DROP TABLE unique_index_data;"_ns);
412 if (NS_WARN_IF(NS_FAILED(rv))) {
413 return rv;
416 rv = aConnection.ExecuteSimpleSQL(
417 "CREATE TABLE unique_index_data ("
418 "index_id INTEGER NOT NULL, "
419 "value NOT NULL, "
420 "object_data_key NOT NULL, "
421 "object_data_id INTEGER NOT NULL, "
422 "PRIMARY KEY (index_id, value, object_data_key), "
423 "UNIQUE (index_id, value), "
424 "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
425 "CASCADE "
426 "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
427 "CASCADE"
428 ");"_ns);
429 if (NS_WARN_IF(NS_FAILED(rv))) {
430 return rv;
433 rv = aConnection.ExecuteSimpleSQL(
434 "INSERT INTO unique_index_data "
435 "SELECT index_id, value, object_data_key, object_data_id "
436 "FROM temp_upgrade;"_ns);
437 if (NS_WARN_IF(NS_FAILED(rv))) {
438 return rv;
441 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
442 if (NS_WARN_IF(NS_FAILED(rv))) {
443 return rv;
446 rv = aConnection.ExecuteSimpleSQL(
447 "CREATE INDEX unique_index_data_object_data_id_index "
448 "ON unique_index_data (object_data_id);"_ns);
449 if (NS_WARN_IF(NS_FAILED(rv))) {
450 return rv;
453 // Fix up the ai_index_data table. We're reordering the columns as well as
454 // changing the primary key from being a simple id to being a composite.
455 rv = aConnection.ExecuteSimpleSQL(
456 "CREATE TEMPORARY TABLE temp_upgrade ("
457 "index_id, "
458 "value, "
459 "ai_object_data_id "
460 ");"_ns);
461 if (NS_WARN_IF(NS_FAILED(rv))) {
462 return rv;
465 rv = aConnection.ExecuteSimpleSQL(
466 "INSERT INTO temp_upgrade "
467 "SELECT index_id, value, ai_object_data_id "
468 "FROM ai_index_data;"_ns);
469 if (NS_WARN_IF(NS_FAILED(rv))) {
470 return rv;
473 rv = aConnection.ExecuteSimpleSQL("DROP TABLE ai_index_data;"_ns);
474 if (NS_WARN_IF(NS_FAILED(rv))) {
475 return rv;
478 rv = aConnection.ExecuteSimpleSQL(
479 "CREATE TABLE ai_index_data ("
480 "index_id INTEGER NOT NULL, "
481 "value NOT NULL, "
482 "ai_object_data_id INTEGER NOT NULL, "
483 "PRIMARY KEY (index_id, value, ai_object_data_id), "
484 "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
485 "CASCADE, "
486 "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
487 "CASCADE"
488 ");"_ns);
489 if (NS_WARN_IF(NS_FAILED(rv))) {
490 return rv;
493 rv = aConnection.ExecuteSimpleSQL(
494 "INSERT OR IGNORE INTO ai_index_data "
495 "SELECT index_id, value, ai_object_data_id "
496 "FROM temp_upgrade;"_ns);
497 if (NS_WARN_IF(NS_FAILED(rv))) {
498 return rv;
501 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
502 if (NS_WARN_IF(NS_FAILED(rv))) {
503 return rv;
506 rv = aConnection.ExecuteSimpleSQL(
507 "CREATE INDEX ai_index_data_ai_object_data_id_index "
508 "ON ai_index_data (ai_object_data_id);"_ns);
509 if (NS_WARN_IF(NS_FAILED(rv))) {
510 return rv;
513 // Fix up the ai_unique_index_data table. We're reordering the columns as well
514 // as changing the primary key from being a simple id to being a composite.
515 rv = aConnection.ExecuteSimpleSQL(
516 "CREATE TEMPORARY TABLE temp_upgrade ("
517 "index_id, "
518 "value, "
519 "ai_object_data_id "
520 ");"_ns);
521 if (NS_WARN_IF(NS_FAILED(rv))) {
522 return rv;
525 rv = aConnection.ExecuteSimpleSQL(
526 "INSERT INTO temp_upgrade "
527 "SELECT index_id, value, ai_object_data_id "
528 "FROM ai_unique_index_data;"_ns);
529 if (NS_WARN_IF(NS_FAILED(rv))) {
530 return rv;
533 rv = aConnection.ExecuteSimpleSQL("DROP TABLE ai_unique_index_data;"_ns);
534 if (NS_WARN_IF(NS_FAILED(rv))) {
535 return rv;
538 rv = aConnection.ExecuteSimpleSQL(
539 "CREATE TABLE ai_unique_index_data ("
540 "index_id INTEGER NOT NULL, "
541 "value NOT NULL, "
542 "ai_object_data_id INTEGER NOT NULL, "
543 "UNIQUE (index_id, value), "
544 "PRIMARY KEY (index_id, value, ai_object_data_id), "
545 "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
546 "CASCADE, "
547 "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
548 "CASCADE"
549 ");"_ns);
550 if (NS_WARN_IF(NS_FAILED(rv))) {
551 return rv;
554 rv = aConnection.ExecuteSimpleSQL(
555 "INSERT INTO ai_unique_index_data "
556 "SELECT index_id, value, ai_object_data_id "
557 "FROM temp_upgrade;"_ns);
558 if (NS_WARN_IF(NS_FAILED(rv))) {
559 return rv;
562 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
563 if (NS_WARN_IF(NS_FAILED(rv))) {
564 return rv;
567 rv = aConnection.ExecuteSimpleSQL(
568 "CREATE INDEX ai_unique_index_data_ai_object_data_id_index "
569 "ON ai_unique_index_data (ai_object_data_id);"_ns);
570 if (NS_WARN_IF(NS_FAILED(rv))) {
571 return rv;
574 rv = aConnection.SetSchemaVersion(6);
575 if (NS_WARN_IF(NS_FAILED(rv))) {
576 return rv;
579 return NS_OK;
582 nsresult UpgradeSchemaFrom6To7(mozIStorageConnection& aConnection) {
583 AssertIsOnIOThread();
585 AUTO_PROFILER_LABEL("UpgradeSchemaFrom6To7", DOM);
587 nsresult rv = aConnection.ExecuteSimpleSQL(
588 "CREATE TEMPORARY TABLE temp_upgrade ("
589 "id, "
590 "name, "
591 "key_path, "
592 "auto_increment"
593 ");"_ns);
594 if (NS_WARN_IF(NS_FAILED(rv))) {
595 return rv;
598 rv = aConnection.ExecuteSimpleSQL(
599 "INSERT INTO temp_upgrade "
600 "SELECT id, name, key_path, auto_increment "
601 "FROM object_store;"_ns);
602 if (NS_WARN_IF(NS_FAILED(rv))) {
603 return rv;
606 rv = aConnection.ExecuteSimpleSQL("DROP TABLE object_store;"_ns);
607 if (NS_WARN_IF(NS_FAILED(rv))) {
608 return rv;
611 rv = aConnection.ExecuteSimpleSQL(
612 "CREATE TABLE object_store ("
613 "id INTEGER PRIMARY KEY, "
614 "auto_increment INTEGER NOT NULL DEFAULT 0, "
615 "name TEXT NOT NULL, "
616 "key_path TEXT, "
617 "UNIQUE (name)"
618 ");"_ns);
619 if (NS_WARN_IF(NS_FAILED(rv))) {
620 return rv;
623 rv = aConnection.ExecuteSimpleSQL(
624 "INSERT INTO object_store "
625 "SELECT id, auto_increment, name, nullif(key_path, '') "
626 "FROM temp_upgrade;"_ns);
627 if (NS_WARN_IF(NS_FAILED(rv))) {
628 return rv;
631 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
632 if (NS_WARN_IF(NS_FAILED(rv))) {
633 return rv;
636 rv = aConnection.SetSchemaVersion(7);
637 if (NS_WARN_IF(NS_FAILED(rv))) {
638 return rv;
641 return NS_OK;
644 nsresult UpgradeSchemaFrom7To8(mozIStorageConnection& aConnection) {
645 AssertIsOnIOThread();
647 AUTO_PROFILER_LABEL("UpgradeSchemaFrom7To8", DOM);
649 nsresult rv = aConnection.ExecuteSimpleSQL(
650 "CREATE TEMPORARY TABLE temp_upgrade ("
651 "id, "
652 "object_store_id, "
653 "name, "
654 "key_path, "
655 "unique_index, "
656 "object_store_autoincrement"
657 ");"_ns);
658 if (NS_WARN_IF(NS_FAILED(rv))) {
659 return rv;
662 rv = aConnection.ExecuteSimpleSQL(
663 "INSERT INTO temp_upgrade "
664 "SELECT id, object_store_id, name, key_path, "
665 "unique_index, object_store_autoincrement "
666 "FROM object_store_index;"_ns);
667 if (NS_WARN_IF(NS_FAILED(rv))) {
668 return rv;
671 rv = aConnection.ExecuteSimpleSQL("DROP TABLE object_store_index;"_ns);
672 if (NS_WARN_IF(NS_FAILED(rv))) {
673 return rv;
676 rv = aConnection.ExecuteSimpleSQL(
677 "CREATE TABLE object_store_index ("
678 "id INTEGER, "
679 "object_store_id INTEGER NOT NULL, "
680 "name TEXT NOT NULL, "
681 "key_path TEXT NOT NULL, "
682 "unique_index INTEGER NOT NULL, "
683 "multientry INTEGER NOT NULL, "
684 "object_store_autoincrement INTERGER NOT NULL, "
685 "PRIMARY KEY (id), "
686 "UNIQUE (object_store_id, name), "
687 "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
688 "CASCADE"
689 ");"_ns);
690 if (NS_WARN_IF(NS_FAILED(rv))) {
691 return rv;
694 rv = aConnection.ExecuteSimpleSQL(
695 "INSERT INTO object_store_index "
696 "SELECT id, object_store_id, name, key_path, "
697 "unique_index, 0, object_store_autoincrement "
698 "FROM temp_upgrade;"_ns);
699 if (NS_WARN_IF(NS_FAILED(rv))) {
700 return rv;
703 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
704 if (NS_WARN_IF(NS_FAILED(rv))) {
705 return rv;
708 rv = aConnection.SetSchemaVersion(8);
709 if (NS_WARN_IF(NS_FAILED(rv))) {
710 return rv;
713 return NS_OK;
716 class CompressDataBlobsFunction final : public mozIStorageFunction {
717 public:
718 NS_DECL_ISUPPORTS
720 private:
721 ~CompressDataBlobsFunction() = default;
723 NS_IMETHOD
724 OnFunctionCall(mozIStorageValueArray* aArguments,
725 nsIVariant** aResult) override {
726 MOZ_ASSERT(aArguments);
727 MOZ_ASSERT(aResult);
729 AUTO_PROFILER_LABEL("CompressDataBlobsFunction::OnFunctionCall", DOM);
731 uint32_t argc;
732 nsresult rv = aArguments->GetNumEntries(&argc);
733 if (NS_WARN_IF(NS_FAILED(rv))) {
734 return rv;
737 if (argc != 1) {
738 NS_WARNING("Don't call me with the wrong number of arguments!");
739 return NS_ERROR_UNEXPECTED;
742 int32_t type;
743 rv = aArguments->GetTypeOfIndex(0, &type);
744 if (NS_WARN_IF(NS_FAILED(rv))) {
745 return rv;
748 if (type != mozIStorageStatement::VALUE_TYPE_BLOB) {
749 NS_WARNING("Don't call me with the wrong type of arguments!");
750 return NS_ERROR_UNEXPECTED;
753 const uint8_t* uncompressed;
754 uint32_t uncompressedLength;
755 rv = aArguments->GetSharedBlob(0, &uncompressedLength, &uncompressed);
756 if (NS_WARN_IF(NS_FAILED(rv))) {
757 return rv;
760 size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
761 UniqueFreePtr<uint8_t> compressed(
762 static_cast<uint8_t*>(malloc(compressedLength)));
763 if (NS_WARN_IF(!compressed)) {
764 return NS_ERROR_OUT_OF_MEMORY;
767 snappy::RawCompress(
768 reinterpret_cast<const char*>(uncompressed), uncompressedLength,
769 reinterpret_cast<char*>(compressed.get()), &compressedLength);
771 std::pair<uint8_t*, int> data(compressed.release(), int(compressedLength));
773 nsCOMPtr<nsIVariant> result =
774 new mozilla::storage::AdoptedBlobVariant(data);
776 result.forget(aResult);
777 return NS_OK;
781 nsresult UpgradeSchemaFrom8To9_0(mozIStorageConnection& aConnection) {
782 AssertIsOnIOThread();
784 AUTO_PROFILER_LABEL("UpgradeSchemaFrom8To9_0", DOM);
786 // We no longer use the dataVersion column.
787 nsresult rv =
788 aConnection.ExecuteSimpleSQL("UPDATE database SET dataVersion = 0;"_ns);
789 if (NS_WARN_IF(NS_FAILED(rv))) {
790 return rv;
793 nsCOMPtr<mozIStorageFunction> compressor = new CompressDataBlobsFunction();
795 constexpr auto compressorName = "compress"_ns;
797 rv = aConnection.CreateFunction(compressorName, 1, compressor);
798 if (NS_WARN_IF(NS_FAILED(rv))) {
799 return rv;
802 // Turn off foreign key constraints before we do anything here.
803 rv = aConnection.ExecuteSimpleSQL(
804 "UPDATE object_data SET data = compress(data);"_ns);
805 if (NS_WARN_IF(NS_FAILED(rv))) {
806 return rv;
809 rv = aConnection.ExecuteSimpleSQL(
810 "UPDATE ai_object_data SET data = compress(data);"_ns);
811 if (NS_WARN_IF(NS_FAILED(rv))) {
812 return rv;
815 rv = aConnection.RemoveFunction(compressorName);
816 if (NS_WARN_IF(NS_FAILED(rv))) {
817 return rv;
820 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(9, 0));
821 if (NS_WARN_IF(NS_FAILED(rv))) {
822 return rv;
825 return NS_OK;
828 nsresult UpgradeSchemaFrom9_0To10_0(mozIStorageConnection& aConnection) {
829 AssertIsOnIOThread();
831 AUTO_PROFILER_LABEL("UpgradeSchemaFrom9_0To10_0", DOM);
833 nsresult rv = aConnection.ExecuteSimpleSQL(
834 "ALTER TABLE object_data ADD COLUMN file_ids TEXT;"_ns);
835 if (NS_WARN_IF(NS_FAILED(rv))) {
836 return rv;
839 rv = aConnection.ExecuteSimpleSQL(
840 "ALTER TABLE ai_object_data ADD COLUMN file_ids TEXT;"_ns);
841 if (NS_WARN_IF(NS_FAILED(rv))) {
842 return rv;
845 rv = CreateFileTables(aConnection);
846 if (NS_WARN_IF(NS_FAILED(rv))) {
847 return rv;
850 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(10, 0));
851 if (NS_WARN_IF(NS_FAILED(rv))) {
852 return rv;
855 return NS_OK;
858 nsresult UpgradeSchemaFrom10_0To11_0(mozIStorageConnection& aConnection) {
859 AssertIsOnIOThread();
861 AUTO_PROFILER_LABEL("UpgradeSchemaFrom10_0To11_0", DOM);
863 nsresult rv = aConnection.ExecuteSimpleSQL(
864 "CREATE TEMPORARY TABLE temp_upgrade ("
865 "id, "
866 "object_store_id, "
867 "name, "
868 "key_path, "
869 "unique_index, "
870 "multientry"
871 ");"_ns);
872 if (NS_WARN_IF(NS_FAILED(rv))) {
873 return rv;
876 rv = aConnection.ExecuteSimpleSQL(
877 "INSERT INTO temp_upgrade "
878 "SELECT id, object_store_id, name, key_path, "
879 "unique_index, multientry "
880 "FROM object_store_index;"_ns);
881 if (NS_WARN_IF(NS_FAILED(rv))) {
882 return rv;
885 rv = aConnection.ExecuteSimpleSQL("DROP TABLE object_store_index;"_ns);
886 if (NS_WARN_IF(NS_FAILED(rv))) {
887 return rv;
890 rv = aConnection.ExecuteSimpleSQL(
891 "CREATE TABLE object_store_index ("
892 "id INTEGER PRIMARY KEY, "
893 "object_store_id INTEGER NOT NULL, "
894 "name TEXT NOT NULL, "
895 "key_path TEXT NOT NULL, "
896 "unique_index INTEGER NOT NULL, "
897 "multientry INTEGER NOT NULL, "
898 "UNIQUE (object_store_id, name), "
899 "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
900 "CASCADE"
901 ");"_ns);
902 if (NS_WARN_IF(NS_FAILED(rv))) {
903 return rv;
906 rv = aConnection.ExecuteSimpleSQL(
907 "INSERT INTO object_store_index "
908 "SELECT id, object_store_id, name, key_path, "
909 "unique_index, multientry "
910 "FROM temp_upgrade;"_ns);
911 if (NS_WARN_IF(NS_FAILED(rv))) {
912 return rv;
915 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
916 if (NS_WARN_IF(NS_FAILED(rv))) {
917 return rv;
920 rv = aConnection.ExecuteSimpleSQL(
921 "DROP TRIGGER object_data_insert_trigger;"_ns);
922 if (NS_WARN_IF(NS_FAILED(rv))) {
923 return rv;
926 rv = aConnection.ExecuteSimpleSQL(
927 "INSERT INTO object_data (object_store_id, key_value, data, file_ids) "
928 "SELECT object_store_id, id, data, file_ids "
929 "FROM ai_object_data;"_ns);
930 if (NS_WARN_IF(NS_FAILED(rv))) {
931 return rv;
934 rv = aConnection.ExecuteSimpleSQL(
935 "CREATE TRIGGER object_data_insert_trigger "
936 "AFTER INSERT ON object_data "
937 "FOR EACH ROW "
938 "WHEN NEW.file_ids IS NOT NULL "
939 "BEGIN "
940 "SELECT update_refcount(NULL, NEW.file_ids); "
941 "END;"_ns);
942 if (NS_WARN_IF(NS_FAILED(rv))) {
943 return rv;
946 rv = aConnection.ExecuteSimpleSQL(
947 "INSERT INTO index_data (index_id, value, object_data_key, "
948 "object_data_id) "
949 "SELECT ai_index_data.index_id, ai_index_data.value, "
950 "ai_index_data.ai_object_data_id, object_data.id "
951 "FROM ai_index_data "
952 "INNER JOIN object_store_index ON "
953 "object_store_index.id = ai_index_data.index_id "
954 "INNER JOIN object_data ON "
955 "object_data.object_store_id = object_store_index.object_store_id AND "
956 "object_data.key_value = ai_index_data.ai_object_data_id;"_ns);
957 if (NS_WARN_IF(NS_FAILED(rv))) {
958 return rv;
961 rv = aConnection.ExecuteSimpleSQL(
962 "INSERT INTO unique_index_data (index_id, value, object_data_key, "
963 "object_data_id) "
964 "SELECT ai_unique_index_data.index_id, ai_unique_index_data.value, "
965 "ai_unique_index_data.ai_object_data_id, object_data.id "
966 "FROM ai_unique_index_data "
967 "INNER JOIN object_store_index ON "
968 "object_store_index.id = ai_unique_index_data.index_id "
969 "INNER JOIN object_data ON "
970 "object_data.object_store_id = object_store_index.object_store_id AND "
971 "object_data.key_value = ai_unique_index_data.ai_object_data_id;"_ns);
972 if (NS_WARN_IF(NS_FAILED(rv))) {
973 return rv;
976 rv = aConnection.ExecuteSimpleSQL(
977 "UPDATE object_store "
978 "SET auto_increment = (SELECT max(id) FROM ai_object_data) + 1 "
979 "WHERE auto_increment;"_ns);
980 if (NS_WARN_IF(NS_FAILED(rv))) {
981 return rv;
984 rv = aConnection.ExecuteSimpleSQL("DROP TABLE ai_unique_index_data;"_ns);
985 if (NS_WARN_IF(NS_FAILED(rv))) {
986 return rv;
989 rv = aConnection.ExecuteSimpleSQL("DROP TABLE ai_index_data;"_ns);
990 if (NS_WARN_IF(NS_FAILED(rv))) {
991 return rv;
994 rv = aConnection.ExecuteSimpleSQL("DROP TABLE ai_object_data;"_ns);
995 if (NS_WARN_IF(NS_FAILED(rv))) {
996 return rv;
999 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(11, 0));
1000 if (NS_WARN_IF(NS_FAILED(rv))) {
1001 return rv;
1004 return NS_OK;
1007 class EncodeKeysFunction final : public mozIStorageFunction {
1008 public:
1009 NS_DECL_ISUPPORTS
1011 private:
1012 ~EncodeKeysFunction() = default;
1014 NS_IMETHOD
1015 OnFunctionCall(mozIStorageValueArray* aArguments,
1016 nsIVariant** aResult) override {
1017 MOZ_ASSERT(aArguments);
1018 MOZ_ASSERT(aResult);
1020 AUTO_PROFILER_LABEL("EncodeKeysFunction::OnFunctionCall", DOM);
1022 uint32_t argc;
1023 nsresult rv = aArguments->GetNumEntries(&argc);
1024 if (NS_WARN_IF(NS_FAILED(rv))) {
1025 return rv;
1028 if (argc != 1) {
1029 NS_WARNING("Don't call me with the wrong number of arguments!");
1030 return NS_ERROR_UNEXPECTED;
1033 int32_t type;
1034 rv = aArguments->GetTypeOfIndex(0, &type);
1035 if (NS_WARN_IF(NS_FAILED(rv))) {
1036 return rv;
1039 QM_TRY_INSPECT(
1040 const auto& key, ([type, aArguments]() -> Result<Key, nsresult> {
1041 switch (type) {
1042 case mozIStorageStatement::VALUE_TYPE_INTEGER: {
1043 int64_t intKey;
1044 aArguments->GetInt64(0, &intKey);
1046 Key key;
1047 key.SetFromInteger(intKey);
1049 return key;
1051 case mozIStorageStatement::VALUE_TYPE_TEXT: {
1052 nsString stringKey;
1053 aArguments->GetString(0, stringKey);
1055 Key key;
1056 QM_TRY(key.SetFromString(stringKey));
1058 return key;
1060 default:
1061 NS_WARNING("Don't call me with the wrong type of arguments!");
1062 return Err(NS_ERROR_UNEXPECTED);
1064 }()));
1066 const nsCString& buffer = key.GetBuffer();
1068 std::pair<const void*, int> data(static_cast<const void*>(buffer.get()),
1069 int(buffer.Length()));
1071 nsCOMPtr<nsIVariant> result = new mozilla::storage::BlobVariant(data);
1073 result.forget(aResult);
1074 return NS_OK;
1078 nsresult UpgradeSchemaFrom11_0To12_0(mozIStorageConnection& aConnection) {
1079 AssertIsOnIOThread();
1081 AUTO_PROFILER_LABEL("UpgradeSchemaFrom11_0To12_0", DOM);
1083 constexpr auto encoderName = "encode"_ns;
1085 nsCOMPtr<mozIStorageFunction> encoder = new EncodeKeysFunction();
1087 nsresult rv = aConnection.CreateFunction(encoderName, 1, encoder);
1088 if (NS_WARN_IF(NS_FAILED(rv))) {
1089 return rv;
1092 rv = aConnection.ExecuteSimpleSQL(
1093 "CREATE TEMPORARY TABLE temp_upgrade ("
1094 "id INTEGER PRIMARY KEY, "
1095 "object_store_id, "
1096 "key_value, "
1097 "data, "
1098 "file_ids "
1099 ");"_ns);
1100 if (NS_WARN_IF(NS_FAILED(rv))) {
1101 return rv;
1104 rv = aConnection.ExecuteSimpleSQL(
1105 "INSERT INTO temp_upgrade "
1106 "SELECT id, object_store_id, encode(key_value), data, file_ids "
1107 "FROM object_data;"_ns);
1108 if (NS_WARN_IF(NS_FAILED(rv))) {
1109 return rv;
1112 rv = aConnection.ExecuteSimpleSQL("DROP TABLE object_data;"_ns);
1113 if (NS_WARN_IF(NS_FAILED(rv))) {
1114 return rv;
1117 rv = aConnection.ExecuteSimpleSQL(
1118 "CREATE TABLE object_data ("
1119 "id INTEGER PRIMARY KEY, "
1120 "object_store_id INTEGER NOT NULL, "
1121 "key_value BLOB DEFAULT NULL, "
1122 "file_ids TEXT, "
1123 "data BLOB NOT NULL, "
1124 "UNIQUE (object_store_id, key_value), "
1125 "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
1126 "CASCADE"
1127 ");"_ns);
1128 if (NS_WARN_IF(NS_FAILED(rv))) {
1129 return rv;
1132 rv = aConnection.ExecuteSimpleSQL(
1133 "INSERT INTO object_data "
1134 "SELECT id, object_store_id, key_value, file_ids, data "
1135 "FROM temp_upgrade;"_ns);
1136 if (NS_WARN_IF(NS_FAILED(rv))) {
1137 return rv;
1140 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
1141 if (NS_WARN_IF(NS_FAILED(rv))) {
1142 return rv;
1145 rv = aConnection.ExecuteSimpleSQL(
1146 "CREATE TRIGGER object_data_insert_trigger "
1147 "AFTER INSERT ON object_data "
1148 "FOR EACH ROW "
1149 "WHEN NEW.file_ids IS NOT NULL "
1150 "BEGIN "
1151 "SELECT update_refcount(NULL, NEW.file_ids); "
1152 "END;"_ns);
1153 if (NS_WARN_IF(NS_FAILED(rv))) {
1154 return rv;
1157 rv = aConnection.ExecuteSimpleSQL(
1158 "CREATE TRIGGER object_data_update_trigger "
1159 "AFTER UPDATE OF file_ids ON object_data "
1160 "FOR EACH ROW "
1161 "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
1162 "BEGIN "
1163 "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
1164 "END;"_ns);
1165 if (NS_WARN_IF(NS_FAILED(rv))) {
1166 return rv;
1169 rv = aConnection.ExecuteSimpleSQL(
1170 "CREATE TRIGGER object_data_delete_trigger "
1171 "AFTER DELETE ON object_data "
1172 "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL "
1173 "BEGIN "
1174 "SELECT update_refcount(OLD.file_ids, NULL); "
1175 "END;"_ns);
1176 if (NS_WARN_IF(NS_FAILED(rv))) {
1177 return rv;
1180 rv = aConnection.ExecuteSimpleSQL(
1181 "CREATE TEMPORARY TABLE temp_upgrade ("
1182 "index_id, "
1183 "value, "
1184 "object_data_key, "
1185 "object_data_id "
1186 ");"_ns);
1187 if (NS_WARN_IF(NS_FAILED(rv))) {
1188 return rv;
1191 rv = aConnection.ExecuteSimpleSQL(
1192 "INSERT INTO temp_upgrade "
1193 "SELECT index_id, encode(value), encode(object_data_key), object_data_id "
1194 "FROM index_data;"_ns);
1195 if (NS_WARN_IF(NS_FAILED(rv))) {
1196 return rv;
1199 rv = aConnection.ExecuteSimpleSQL("DROP TABLE index_data;"_ns);
1200 if (NS_WARN_IF(NS_FAILED(rv))) {
1201 return rv;
1204 rv = aConnection.ExecuteSimpleSQL(
1205 "CREATE TABLE index_data ("
1206 "index_id INTEGER NOT NULL, "
1207 "value BLOB NOT NULL, "
1208 "object_data_key BLOB NOT NULL, "
1209 "object_data_id INTEGER NOT NULL, "
1210 "PRIMARY KEY (index_id, value, object_data_key), "
1211 "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
1212 "CASCADE, "
1213 "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
1214 "CASCADE"
1215 ");"_ns);
1216 if (NS_WARN_IF(NS_FAILED(rv))) {
1217 return rv;
1220 rv = aConnection.ExecuteSimpleSQL(
1221 "INSERT INTO index_data "
1222 "SELECT index_id, value, object_data_key, object_data_id "
1223 "FROM temp_upgrade;"_ns);
1224 if (NS_WARN_IF(NS_FAILED(rv))) {
1225 return rv;
1228 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
1229 if (NS_WARN_IF(NS_FAILED(rv))) {
1230 return rv;
1233 rv = aConnection.ExecuteSimpleSQL(
1234 "CREATE INDEX index_data_object_data_id_index "
1235 "ON index_data (object_data_id);"_ns);
1236 if (NS_WARN_IF(NS_FAILED(rv))) {
1237 return rv;
1240 rv = aConnection.ExecuteSimpleSQL(
1241 "CREATE TEMPORARY TABLE temp_upgrade ("
1242 "index_id, "
1243 "value, "
1244 "object_data_key, "
1245 "object_data_id "
1246 ");"_ns);
1247 if (NS_WARN_IF(NS_FAILED(rv))) {
1248 return rv;
1251 rv = aConnection.ExecuteSimpleSQL(
1252 "INSERT INTO temp_upgrade "
1253 "SELECT index_id, encode(value), encode(object_data_key), object_data_id "
1254 "FROM unique_index_data;"_ns);
1255 if (NS_WARN_IF(NS_FAILED(rv))) {
1256 return rv;
1259 rv = aConnection.ExecuteSimpleSQL("DROP TABLE unique_index_data;"_ns);
1260 if (NS_WARN_IF(NS_FAILED(rv))) {
1261 return rv;
1264 rv = aConnection.ExecuteSimpleSQL(
1265 "CREATE TABLE unique_index_data ("
1266 "index_id INTEGER NOT NULL, "
1267 "value BLOB NOT NULL, "
1268 "object_data_key BLOB NOT NULL, "
1269 "object_data_id INTEGER NOT NULL, "
1270 "PRIMARY KEY (index_id, value, object_data_key), "
1271 "UNIQUE (index_id, value), "
1272 "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
1273 "CASCADE "
1274 "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
1275 "CASCADE"
1276 ");"_ns);
1277 if (NS_WARN_IF(NS_FAILED(rv))) {
1278 return rv;
1281 rv = aConnection.ExecuteSimpleSQL(
1282 "INSERT INTO unique_index_data "
1283 "SELECT index_id, value, object_data_key, object_data_id "
1284 "FROM temp_upgrade;"_ns);
1285 if (NS_WARN_IF(NS_FAILED(rv))) {
1286 return rv;
1289 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
1290 if (NS_WARN_IF(NS_FAILED(rv))) {
1291 return rv;
1294 rv = aConnection.ExecuteSimpleSQL(
1295 "CREATE INDEX unique_index_data_object_data_id_index "
1296 "ON unique_index_data (object_data_id);"_ns);
1297 if (NS_WARN_IF(NS_FAILED(rv))) {
1298 return rv;
1301 rv = aConnection.RemoveFunction(encoderName);
1302 if (NS_WARN_IF(NS_FAILED(rv))) {
1303 return rv;
1306 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(12, 0));
1307 if (NS_WARN_IF(NS_FAILED(rv))) {
1308 return rv;
1311 return NS_OK;
1314 nsresult UpgradeSchemaFrom12_0To13_0(mozIStorageConnection& aConnection,
1315 bool* aVacuumNeeded) {
1316 AssertIsOnIOThread();
1318 AUTO_PROFILER_LABEL("UpgradeSchemaFrom12_0To13_0", DOM);
1320 nsresult rv;
1322 #ifdef IDB_MOBILE
1323 int32_t defaultPageSize;
1324 rv = aConnection.GetDefaultPageSize(&defaultPageSize);
1325 if (NS_WARN_IF(NS_FAILED(rv))) {
1326 return rv;
1329 // Enable auto_vacuum mode and update the page size to the platform default.
1330 nsAutoCString upgradeQuery("PRAGMA auto_vacuum = FULL; PRAGMA page_size = ");
1331 upgradeQuery.AppendInt(defaultPageSize);
1333 rv = aConnection.ExecuteSimpleSQL(upgradeQuery);
1334 if (NS_WARN_IF(NS_FAILED(rv))) {
1335 return rv;
1338 *aVacuumNeeded = true;
1339 #endif
1341 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(13, 0));
1342 if (NS_WARN_IF(NS_FAILED(rv))) {
1343 return rv;
1346 return NS_OK;
1349 nsresult UpgradeSchemaFrom13_0To14_0(mozIStorageConnection& aConnection) {
1350 AssertIsOnIOThread();
1352 // The only change between 13 and 14 was a different structured
1353 // clone format, but it's backwards-compatible.
1354 nsresult rv = aConnection.SetSchemaVersion(MakeSchemaVersion(14, 0));
1355 if (NS_WARN_IF(NS_FAILED(rv))) {
1356 return rv;
1359 return NS_OK;
1362 nsresult UpgradeSchemaFrom14_0To15_0(mozIStorageConnection& aConnection) {
1363 // The only change between 14 and 15 was a different structured
1364 // clone format, but it's backwards-compatible.
1365 nsresult rv = aConnection.SetSchemaVersion(MakeSchemaVersion(15, 0));
1366 if (NS_WARN_IF(NS_FAILED(rv))) {
1367 return rv;
1370 return NS_OK;
1373 nsresult UpgradeSchemaFrom15_0To16_0(mozIStorageConnection& aConnection) {
1374 // The only change between 15 and 16 was a different structured
1375 // clone format, but it's backwards-compatible.
1376 nsresult rv = aConnection.SetSchemaVersion(MakeSchemaVersion(16, 0));
1377 if (NS_WARN_IF(NS_FAILED(rv))) {
1378 return rv;
1381 return NS_OK;
1384 nsresult UpgradeSchemaFrom16_0To17_0(mozIStorageConnection& aConnection) {
1385 // The only change between 16 and 17 was a different structured
1386 // clone format, but it's backwards-compatible.
1387 nsresult rv = aConnection.SetSchemaVersion(MakeSchemaVersion(17, 0));
1388 if (NS_WARN_IF(NS_FAILED(rv))) {
1389 return rv;
1392 return NS_OK;
1395 class UpgradeSchemaFrom17_0To18_0Helper final {
1396 class InsertIndexDataValuesFunction;
1397 class UpgradeKeyFunction;
1399 public:
1400 static nsresult DoUpgrade(mozIStorageConnection& aConnection,
1401 const nsACString& aOrigin);
1403 private:
1404 static nsresult DoUpgradeInternal(mozIStorageConnection& aConnection,
1405 const nsACString& aOrigin);
1407 UpgradeSchemaFrom17_0To18_0Helper() = delete;
1408 ~UpgradeSchemaFrom17_0To18_0Helper() = delete;
1411 class UpgradeSchemaFrom17_0To18_0Helper::InsertIndexDataValuesFunction final
1412 : public mozIStorageFunction {
1413 public:
1414 InsertIndexDataValuesFunction() = default;
1416 NS_DECL_ISUPPORTS
1418 private:
1419 ~InsertIndexDataValuesFunction() = default;
1421 NS_DECL_MOZISTORAGEFUNCTION
1424 NS_IMPL_ISUPPORTS(
1425 UpgradeSchemaFrom17_0To18_0Helper::InsertIndexDataValuesFunction,
1426 mozIStorageFunction);
1428 NS_IMETHODIMP
1429 UpgradeSchemaFrom17_0To18_0Helper::InsertIndexDataValuesFunction::
1430 OnFunctionCall(mozIStorageValueArray* aValues, nsIVariant** _retval) {
1431 MOZ_ASSERT(!NS_IsMainThread());
1432 MOZ_ASSERT(!IsOnBackgroundThread());
1433 MOZ_ASSERT(aValues);
1434 MOZ_ASSERT(_retval);
1436 #ifdef DEBUG
1438 uint32_t argCount;
1439 MOZ_ALWAYS_SUCCEEDS(aValues->GetNumEntries(&argCount));
1440 MOZ_ASSERT(argCount == 4);
1442 int32_t valueType;
1443 MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(0, &valueType));
1444 MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_NULL ||
1445 valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
1447 MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(1, &valueType));
1448 MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_INTEGER);
1450 MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(2, &valueType));
1451 MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_INTEGER);
1453 MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(3, &valueType));
1454 MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
1456 #endif
1458 // Read out the previous value. It may be NULL, in which case we'll just end
1459 // up with an empty array.
1460 QM_TRY_UNWRAP(auto indexValues, ReadCompressedIndexDataValues(*aValues, 0));
1462 IndexOrObjectStoreId indexId;
1463 nsresult rv = aValues->GetInt64(1, &indexId);
1464 if (NS_WARN_IF(NS_FAILED(rv))) {
1465 return rv;
1468 int32_t unique;
1469 rv = aValues->GetInt32(2, &unique);
1470 if (NS_WARN_IF(NS_FAILED(rv))) {
1471 return rv;
1474 Key value;
1475 rv = value.SetFromValueArray(aValues, 3);
1476 if (NS_WARN_IF(NS_FAILED(rv))) {
1477 return rv;
1480 // Update the array with the new addition.
1481 if (NS_WARN_IF(!indexValues.InsertElementSorted(
1482 IndexDataValue(indexId, !!unique, value), fallible))) {
1483 IDB_REPORT_INTERNAL_ERR();
1484 return NS_ERROR_OUT_OF_MEMORY;
1487 // Compress the array.
1488 QM_TRY_UNWRAP((auto [indexValuesBlob, indexValuesBlobLength]),
1489 MakeCompressedIndexDataValues(indexValues));
1491 // The compressed blob is the result of this function.
1492 nsCOMPtr<nsIVariant> result = new storage::AdoptedBlobVariant(
1493 std::pair(indexValuesBlob.release(), indexValuesBlobLength));
1495 result.forget(_retval);
1496 return NS_OK;
1499 class UpgradeSchemaFrom17_0To18_0Helper::UpgradeKeyFunction final
1500 : public mozIStorageFunction {
1501 public:
1502 UpgradeKeyFunction() = default;
1504 static nsresult CopyAndUpgradeKeyBuffer(const uint8_t* aSource,
1505 const uint8_t* aSourceEnd,
1506 uint8_t* aDestination) {
1507 return CopyAndUpgradeKeyBufferInternal(aSource, aSourceEnd, aDestination,
1508 0 /* aTagOffset */,
1509 0 /* aRecursionDepth */);
1512 NS_DECL_ISUPPORTS
1514 private:
1515 ~UpgradeKeyFunction() = default;
1517 static nsresult CopyAndUpgradeKeyBufferInternal(const uint8_t*& aSource,
1518 const uint8_t* aSourceEnd,
1519 uint8_t*& aDestination,
1520 uint8_t aTagOffset,
1521 uint8_t aRecursionDepth);
1523 static uint32_t AdjustedSize(uint32_t aMaxSize, const uint8_t* aSource,
1524 const uint8_t* aSourceEnd) {
1525 MOZ_ASSERT(aMaxSize);
1526 MOZ_ASSERT(aSource);
1527 MOZ_ASSERT(aSourceEnd);
1528 MOZ_ASSERT(aSource <= aSourceEnd);
1530 return std::min(aMaxSize, uint32_t(aSourceEnd - aSource));
1533 NS_DECL_MOZISTORAGEFUNCTION
1536 // static
1537 nsresult UpgradeSchemaFrom17_0To18_0Helper::UpgradeKeyFunction::
1538 CopyAndUpgradeKeyBufferInternal(const uint8_t*& aSource,
1539 const uint8_t* aSourceEnd,
1540 uint8_t*& aDestination, uint8_t aTagOffset,
1541 uint8_t aRecursionDepth) {
1542 MOZ_ASSERT(!NS_IsMainThread());
1543 MOZ_ASSERT(!IsOnBackgroundThread());
1544 MOZ_ASSERT(aSource);
1545 MOZ_ASSERT(*aSource);
1546 MOZ_ASSERT(aSourceEnd);
1547 MOZ_ASSERT(aSource < aSourceEnd);
1548 MOZ_ASSERT(aDestination);
1549 MOZ_ASSERT(aTagOffset <= Key::kMaxArrayCollapse);
1551 static constexpr uint8_t kOldNumberTag = 0x1;
1552 static constexpr uint8_t kOldDateTag = 0x2;
1553 static constexpr uint8_t kOldStringTag = 0x3;
1554 static constexpr uint8_t kOldArrayTag = 0x4;
1555 static constexpr uint8_t kOldMaxType = kOldArrayTag;
1557 if (NS_WARN_IF(aRecursionDepth > Key::kMaxRecursionDepth)) {
1558 IDB_REPORT_INTERNAL_ERR();
1559 return NS_ERROR_FILE_CORRUPTED;
1562 const uint8_t sourceTag = *aSource - (aTagOffset * kOldMaxType);
1563 MOZ_ASSERT(sourceTag);
1565 if (NS_WARN_IF(sourceTag > kOldMaxType * Key::kMaxArrayCollapse)) {
1566 IDB_REPORT_INTERNAL_ERR();
1567 return NS_ERROR_FILE_CORRUPTED;
1570 if (sourceTag == kOldNumberTag || sourceTag == kOldDateTag) {
1571 // Write the new tag.
1572 *aDestination++ = (sourceTag == kOldNumberTag ? Key::eFloat : Key::eDate) +
1573 (aTagOffset * Key::eMaxType);
1574 aSource++;
1576 // Numbers and Dates are encoded as 64-bit integers, but trailing 0
1577 // bytes have been removed.
1578 const uint32_t byteCount =
1579 AdjustedSize(sizeof(uint64_t), aSource, aSourceEnd);
1581 aDestination = std::copy(aSource, aSource + byteCount, aDestination);
1582 aSource += byteCount;
1584 return NS_OK;
1587 if (sourceTag == kOldStringTag) {
1588 // Write the new tag.
1589 *aDestination++ = Key::eString + (aTagOffset * Key::eMaxType);
1590 aSource++;
1592 while (aSource < aSourceEnd) {
1593 const uint8_t byte = *aSource++;
1594 *aDestination++ = byte;
1596 if (!byte) {
1597 // Just copied the terminator.
1598 break;
1601 // Maybe copy one or two extra bytes if the byte is tagged and we have
1602 // enough source space.
1603 if (byte & 0x80) {
1604 const uint32_t byteCount =
1605 AdjustedSize((byte & 0x40) ? 2 : 1, aSource, aSourceEnd);
1607 aDestination = std::copy(aSource, aSource + byteCount, aDestination);
1608 aSource += byteCount;
1612 return NS_OK;
1615 if (NS_WARN_IF(sourceTag < kOldArrayTag)) {
1616 IDB_REPORT_INTERNAL_ERR();
1617 return NS_ERROR_FILE_CORRUPTED;
1620 aTagOffset++;
1622 if (aTagOffset == Key::kMaxArrayCollapse) {
1623 MOZ_ASSERT(sourceTag == kOldArrayTag);
1625 *aDestination++ = (aTagOffset * Key::eMaxType);
1626 aSource++;
1628 aTagOffset = 0;
1631 while (aSource < aSourceEnd &&
1632 (*aSource - (aTagOffset * kOldMaxType)) != Key::eTerminator) {
1633 nsresult rv = CopyAndUpgradeKeyBufferInternal(
1634 aSource, aSourceEnd, aDestination, aTagOffset, aRecursionDepth + 1);
1635 if (NS_WARN_IF(NS_FAILED(rv))) {
1636 return rv;
1639 aTagOffset = 0;
1642 if (aSource < aSourceEnd) {
1643 MOZ_ASSERT((*aSource - (aTagOffset * kOldMaxType)) == Key::eTerminator);
1644 *aDestination++ = Key::eTerminator + (aTagOffset * Key::eMaxType);
1645 aSource++;
1648 return NS_OK;
1651 NS_IMPL_ISUPPORTS(UpgradeSchemaFrom17_0To18_0Helper::UpgradeKeyFunction,
1652 mozIStorageFunction);
1654 NS_IMETHODIMP
1655 UpgradeSchemaFrom17_0To18_0Helper::UpgradeKeyFunction::OnFunctionCall(
1656 mozIStorageValueArray* aValues, nsIVariant** _retval) {
1657 MOZ_ASSERT(!NS_IsMainThread());
1658 MOZ_ASSERT(!IsOnBackgroundThread());
1659 MOZ_ASSERT(aValues);
1660 MOZ_ASSERT(_retval);
1662 #ifdef DEBUG
1664 uint32_t argCount;
1665 MOZ_ALWAYS_SUCCEEDS(aValues->GetNumEntries(&argCount));
1666 MOZ_ASSERT(argCount == 1);
1668 int32_t valueType;
1669 MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(0, &valueType));
1670 MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
1672 #endif
1674 // Dig the old key out of the values.
1675 const uint8_t* blobData;
1676 uint32_t blobDataLength;
1677 nsresult rv = aValues->GetSharedBlob(0, &blobDataLength, &blobData);
1678 if (NS_WARN_IF(NS_FAILED(rv))) {
1679 return rv;
1682 // Upgrading the key doesn't change the amount of space needed to hold it.
1683 UniqueFreePtr<uint8_t> upgradedBlobData(
1684 static_cast<uint8_t*>(malloc(blobDataLength)));
1685 if (NS_WARN_IF(!upgradedBlobData)) {
1686 IDB_REPORT_INTERNAL_ERR();
1687 return NS_ERROR_OUT_OF_MEMORY;
1690 rv = CopyAndUpgradeKeyBuffer(blobData, blobData + blobDataLength,
1691 upgradedBlobData.get());
1692 if (NS_WARN_IF(NS_FAILED(rv))) {
1693 return rv;
1696 // The upgraded key is the result of this function.
1697 std::pair<uint8_t*, int> data(upgradedBlobData.release(),
1698 int(blobDataLength));
1700 nsCOMPtr<nsIVariant> result = new mozilla::storage::AdoptedBlobVariant(data);
1702 result.forget(_retval);
1703 return NS_OK;
1706 // static
1707 nsresult UpgradeSchemaFrom17_0To18_0Helper::DoUpgrade(
1708 mozIStorageConnection& aConnection, const nsACString& aOrigin) {
1709 MOZ_ASSERT(!aOrigin.IsEmpty());
1711 // Register the |upgrade_key| function.
1712 RefPtr<UpgradeKeyFunction> updateFunction = new UpgradeKeyFunction();
1714 constexpr auto upgradeKeyFunctionName = "upgrade_key"_ns;
1716 nsresult rv =
1717 aConnection.CreateFunction(upgradeKeyFunctionName, 1, updateFunction);
1718 if (NS_WARN_IF(NS_FAILED(rv))) {
1719 return rv;
1722 // Register the |insert_idv| function.
1723 RefPtr<InsertIndexDataValuesFunction> insertIDVFunction =
1724 new InsertIndexDataValuesFunction();
1726 constexpr auto insertIDVFunctionName = "insert_idv"_ns;
1728 rv = aConnection.CreateFunction(insertIDVFunctionName, 4, insertIDVFunction);
1729 if (NS_WARN_IF(NS_FAILED(rv))) {
1730 MOZ_ALWAYS_SUCCEEDS(aConnection.RemoveFunction(upgradeKeyFunctionName));
1731 return rv;
1734 rv = DoUpgradeInternal(aConnection, aOrigin);
1736 MOZ_ALWAYS_SUCCEEDS(aConnection.RemoveFunction(upgradeKeyFunctionName));
1737 MOZ_ALWAYS_SUCCEEDS(aConnection.RemoveFunction(insertIDVFunctionName));
1739 if (NS_WARN_IF(NS_FAILED(rv))) {
1740 return rv;
1743 return NS_OK;
1746 // static
1747 nsresult UpgradeSchemaFrom17_0To18_0Helper::DoUpgradeInternal(
1748 mozIStorageConnection& aConnection, const nsACString& aOrigin) {
1749 MOZ_ASSERT(!aOrigin.IsEmpty());
1751 nsresult rv = aConnection.ExecuteSimpleSQL(
1752 // Drop these triggers to avoid unnecessary work during the upgrade
1753 // process.
1754 "DROP TRIGGER object_data_insert_trigger;"
1755 "DROP TRIGGER object_data_update_trigger;"
1756 "DROP TRIGGER object_data_delete_trigger;"_ns);
1757 if (NS_WARN_IF(NS_FAILED(rv))) {
1758 return rv;
1761 rv = aConnection.ExecuteSimpleSQL(
1762 // Drop these indexes before we do anything else to free disk space.
1763 "DROP INDEX index_data_object_data_id_index;"
1764 "DROP INDEX unique_index_data_object_data_id_index;"_ns);
1765 if (NS_WARN_IF(NS_FAILED(rv))) {
1766 return rv;
1769 // Create the new tables and triggers first.
1771 rv = aConnection.ExecuteSimpleSQL(
1772 // This will eventually become the |database| table.
1773 "CREATE TABLE database_upgrade "
1774 "( name TEXT PRIMARY KEY"
1775 ", origin TEXT NOT NULL"
1776 ", version INTEGER NOT NULL DEFAULT 0"
1777 ", last_vacuum_time INTEGER NOT NULL DEFAULT 0"
1778 ", last_analyze_time INTEGER NOT NULL DEFAULT 0"
1779 ", last_vacuum_size INTEGER NOT NULL DEFAULT 0"
1780 ") WITHOUT ROWID;"_ns);
1781 if (NS_WARN_IF(NS_FAILED(rv))) {
1782 return rv;
1785 rv = aConnection.ExecuteSimpleSQL(
1786 // This will eventually become the |object_store| table.
1787 "CREATE TABLE object_store_upgrade"
1788 "( id INTEGER PRIMARY KEY"
1789 ", auto_increment INTEGER NOT NULL DEFAULT 0"
1790 ", name TEXT NOT NULL"
1791 ", key_path TEXT"
1792 ");"_ns);
1793 if (NS_WARN_IF(NS_FAILED(rv))) {
1794 return rv;
1797 rv = aConnection.ExecuteSimpleSQL(
1798 // This will eventually become the |object_store_index| table.
1799 "CREATE TABLE object_store_index_upgrade"
1800 "( id INTEGER PRIMARY KEY"
1801 ", object_store_id INTEGER NOT NULL"
1802 ", name TEXT NOT NULL"
1803 ", key_path TEXT NOT NULL"
1804 ", unique_index INTEGER NOT NULL"
1805 ", multientry INTEGER NOT NULL"
1806 ", FOREIGN KEY (object_store_id) "
1807 "REFERENCES object_store(id) "
1808 ");"_ns);
1809 if (NS_WARN_IF(NS_FAILED(rv))) {
1810 return rv;
1813 rv = aConnection.ExecuteSimpleSQL(
1814 // This will eventually become the |object_data| table.
1815 "CREATE TABLE object_data_upgrade"
1816 "( object_store_id INTEGER NOT NULL"
1817 ", key BLOB NOT NULL"
1818 ", index_data_values BLOB DEFAULT NULL"
1819 ", file_ids TEXT"
1820 ", data BLOB NOT NULL"
1821 ", PRIMARY KEY (object_store_id, key)"
1822 ", FOREIGN KEY (object_store_id) "
1823 "REFERENCES object_store(id) "
1824 ") WITHOUT ROWID;"_ns);
1825 if (NS_WARN_IF(NS_FAILED(rv))) {
1826 return rv;
1829 rv = aConnection.ExecuteSimpleSQL(
1830 // This will eventually become the |index_data| table.
1831 "CREATE TABLE index_data_upgrade"
1832 "( index_id INTEGER NOT NULL"
1833 ", value BLOB NOT NULL"
1834 ", object_data_key BLOB NOT NULL"
1835 ", object_store_id INTEGER NOT NULL"
1836 ", PRIMARY KEY (index_id, value, object_data_key)"
1837 ", FOREIGN KEY (index_id) "
1838 "REFERENCES object_store_index(id) "
1839 ", FOREIGN KEY (object_store_id, object_data_key) "
1840 "REFERENCES object_data(object_store_id, key) "
1841 ") WITHOUT ROWID;"_ns);
1842 if (NS_WARN_IF(NS_FAILED(rv))) {
1843 return rv;
1846 rv = aConnection.ExecuteSimpleSQL(
1847 // This will eventually become the |unique_index_data| table.
1848 "CREATE TABLE unique_index_data_upgrade"
1849 "( index_id INTEGER NOT NULL"
1850 ", value BLOB NOT NULL"
1851 ", object_store_id INTEGER NOT NULL"
1852 ", object_data_key BLOB NOT NULL"
1853 ", PRIMARY KEY (index_id, value)"
1854 ", FOREIGN KEY (index_id) "
1855 "REFERENCES object_store_index(id) "
1856 ", FOREIGN KEY (object_store_id, object_data_key) "
1857 "REFERENCES object_data(object_store_id, key) "
1858 ") WITHOUT ROWID;"_ns);
1859 if (NS_WARN_IF(NS_FAILED(rv))) {
1860 return rv;
1863 rv = aConnection.ExecuteSimpleSQL(
1864 // Temporarily store |index_data_values| that we build during the upgrade
1865 // of the index tables. We will later move this to the |object_data|
1866 // table.
1867 "CREATE TEMPORARY TABLE temp_index_data_values "
1868 "( object_store_id INTEGER NOT NULL"
1869 ", key BLOB NOT NULL"
1870 ", index_data_values BLOB DEFAULT NULL"
1871 ", PRIMARY KEY (object_store_id, key)"
1872 ") WITHOUT ROWID;"_ns);
1873 if (NS_WARN_IF(NS_FAILED(rv))) {
1874 return rv;
1877 rv = aConnection.ExecuteSimpleSQL(
1878 // These two triggers help build the |index_data_values| blobs. The nested
1879 // SELECT statements help us achieve an "INSERT OR UPDATE"-like behavior.
1880 "CREATE TEMPORARY TRIGGER unique_index_data_upgrade_insert_trigger "
1881 "AFTER INSERT ON unique_index_data_upgrade "
1882 "BEGIN "
1883 "INSERT OR REPLACE INTO temp_index_data_values "
1884 "VALUES "
1885 "( NEW.object_store_id"
1886 ", NEW.object_data_key"
1887 ", insert_idv("
1888 "( SELECT index_data_values "
1889 "FROM temp_index_data_values "
1890 "WHERE object_store_id = NEW.object_store_id "
1891 "AND key = NEW.object_data_key "
1892 "), NEW.index_id"
1893 ", 1" /* unique */
1894 ", NEW.value"
1896 ");"
1897 "END;"_ns);
1898 if (NS_WARN_IF(NS_FAILED(rv))) {
1899 return rv;
1902 rv = aConnection.ExecuteSimpleSQL(
1903 "CREATE TEMPORARY TRIGGER index_data_upgrade_insert_trigger "
1904 "AFTER INSERT ON index_data_upgrade "
1905 "BEGIN "
1906 "INSERT OR REPLACE INTO temp_index_data_values "
1907 "VALUES "
1908 "( NEW.object_store_id"
1909 ", NEW.object_data_key"
1910 ", insert_idv("
1912 "SELECT index_data_values "
1913 "FROM temp_index_data_values "
1914 "WHERE object_store_id = NEW.object_store_id "
1915 "AND key = NEW.object_data_key "
1916 "), NEW.index_id"
1917 ", 0" /* not unique */
1918 ", NEW.value"
1920 ");"
1921 "END;"_ns);
1922 if (NS_WARN_IF(NS_FAILED(rv))) {
1923 return rv;
1926 // Update the |unique_index_data| table to change the column order, remove the
1927 // ON DELETE CASCADE clauses, and to apply the WITHOUT ROWID optimization.
1928 rv = aConnection.ExecuteSimpleSQL(
1929 // Insert all the data.
1930 "INSERT INTO unique_index_data_upgrade "
1931 "SELECT "
1932 "unique_index_data.index_id, "
1933 "upgrade_key(unique_index_data.value), "
1934 "object_data.object_store_id, "
1935 "upgrade_key(unique_index_data.object_data_key) "
1936 "FROM unique_index_data "
1937 "JOIN object_data "
1938 "ON unique_index_data.object_data_id = object_data.id;"_ns);
1939 if (NS_WARN_IF(NS_FAILED(rv))) {
1940 return rv;
1943 rv = aConnection.ExecuteSimpleSQL(
1944 // The trigger is no longer needed.
1945 "DROP TRIGGER unique_index_data_upgrade_insert_trigger;"_ns);
1946 if (NS_WARN_IF(NS_FAILED(rv))) {
1947 return rv;
1950 rv = aConnection.ExecuteSimpleSQL(
1951 // The old table is no longer needed.
1952 "DROP TABLE unique_index_data;"_ns);
1953 if (NS_WARN_IF(NS_FAILED(rv))) {
1954 return rv;
1957 rv = aConnection.ExecuteSimpleSQL(
1958 // Rename the table.
1959 "ALTER TABLE unique_index_data_upgrade "
1960 "RENAME TO unique_index_data;"_ns);
1961 if (NS_WARN_IF(NS_FAILED(rv))) {
1962 return rv;
1965 // Update the |index_data| table to change the column order, remove the ON
1966 // DELETE CASCADE clauses, and to apply the WITHOUT ROWID optimization.
1967 rv = aConnection.ExecuteSimpleSQL(
1968 // Insert all the data.
1969 "INSERT INTO index_data_upgrade "
1970 "SELECT "
1971 "index_data.index_id, "
1972 "upgrade_key(index_data.value), "
1973 "upgrade_key(index_data.object_data_key), "
1974 "object_data.object_store_id "
1975 "FROM index_data "
1976 "JOIN object_data "
1977 "ON index_data.object_data_id = object_data.id;"_ns);
1978 if (NS_WARN_IF(NS_FAILED(rv))) {
1979 return rv;
1982 rv = aConnection.ExecuteSimpleSQL(
1983 // The trigger is no longer needed.
1984 "DROP TRIGGER index_data_upgrade_insert_trigger;"_ns);
1985 if (NS_WARN_IF(NS_FAILED(rv))) {
1986 return rv;
1989 rv = aConnection.ExecuteSimpleSQL(
1990 // The old table is no longer needed.
1991 "DROP TABLE index_data;"_ns);
1992 if (NS_WARN_IF(NS_FAILED(rv))) {
1993 return rv;
1996 rv = aConnection.ExecuteSimpleSQL(
1997 // Rename the table.
1998 "ALTER TABLE index_data_upgrade "
1999 "RENAME TO index_data;"_ns);
2000 if (NS_WARN_IF(NS_FAILED(rv))) {
2001 return rv;
2004 // Update the |object_data| table to add the |index_data_values| column,
2005 // remove the ON DELETE CASCADE clause, and apply the WITHOUT ROWID
2006 // optimization.
2007 rv = aConnection.ExecuteSimpleSQL(
2008 // Insert all the data.
2009 "INSERT INTO object_data_upgrade "
2010 "SELECT "
2011 "object_data.object_store_id, "
2012 "upgrade_key(object_data.key_value), "
2013 "temp_index_data_values.index_data_values, "
2014 "object_data.file_ids, "
2015 "object_data.data "
2016 "FROM object_data "
2017 "LEFT JOIN temp_index_data_values "
2018 "ON object_data.object_store_id = "
2019 "temp_index_data_values.object_store_id "
2020 "AND upgrade_key(object_data.key_value) = "
2021 "temp_index_data_values.key;"_ns);
2022 if (NS_WARN_IF(NS_FAILED(rv))) {
2023 return rv;
2026 rv = aConnection.ExecuteSimpleSQL(
2027 // The temporary table is no longer needed.
2028 "DROP TABLE temp_index_data_values;"_ns);
2029 if (NS_WARN_IF(NS_FAILED(rv))) {
2030 return rv;
2033 rv = aConnection.ExecuteSimpleSQL(
2034 // The old table is no longer needed.
2035 "DROP TABLE object_data;"_ns);
2036 if (NS_WARN_IF(NS_FAILED(rv))) {
2037 return rv;
2040 rv = aConnection.ExecuteSimpleSQL(
2041 // Rename the table.
2042 "ALTER TABLE object_data_upgrade "
2043 "RENAME TO object_data;"_ns);
2044 if (NS_WARN_IF(NS_FAILED(rv))) {
2045 return rv;
2048 // Update the |object_store_index| table to remove the UNIQUE constraint and
2049 // the ON DELETE CASCADE clause.
2050 rv = aConnection.ExecuteSimpleSQL(
2051 "INSERT INTO object_store_index_upgrade "
2052 "SELECT * "
2053 "FROM object_store_index;"_ns);
2054 if (NS_WARN_IF(NS_FAILED(rv))) {
2055 return rv;
2058 rv = aConnection.ExecuteSimpleSQL("DROP TABLE object_store_index;"_ns);
2059 if (NS_WARN_IF(NS_FAILED(rv))) {
2060 return rv;
2063 rv = aConnection.ExecuteSimpleSQL(
2064 "ALTER TABLE object_store_index_upgrade "
2065 "RENAME TO object_store_index;"_ns);
2066 if (NS_WARN_IF(NS_FAILED(rv))) {
2067 return rv;
2070 // Update the |object_store| table to remove the UNIQUE constraint.
2071 rv = aConnection.ExecuteSimpleSQL(
2072 "INSERT INTO object_store_upgrade "
2073 "SELECT * "
2074 "FROM object_store;"_ns);
2075 if (NS_WARN_IF(NS_FAILED(rv))) {
2076 return rv;
2079 rv = aConnection.ExecuteSimpleSQL("DROP TABLE object_store;"_ns);
2080 if (NS_WARN_IF(NS_FAILED(rv))) {
2081 return rv;
2084 rv = aConnection.ExecuteSimpleSQL(
2085 "ALTER TABLE object_store_upgrade "
2086 "RENAME TO object_store;"_ns);
2087 if (NS_WARN_IF(NS_FAILED(rv))) {
2088 return rv;
2091 // Update the |database| table to include the origin, vacuum information, and
2092 // apply the WITHOUT ROWID optimization.
2093 nsCOMPtr<mozIStorageStatement> stmt;
2095 // The parameter names are not used, parameters are bound by index only
2096 // locally in the same function.
2097 rv = aConnection.CreateStatement(
2098 "INSERT INTO database_upgrade "
2099 "SELECT name, :origin, version, 0, 0, 0 "
2100 "FROM database;"_ns,
2101 getter_AddRefs(stmt));
2102 if (NS_WARN_IF(NS_FAILED(rv))) {
2103 return rv;
2106 rv = stmt->BindUTF8StringByIndex(0, aOrigin);
2107 if (NS_WARN_IF(NS_FAILED(rv))) {
2108 return rv;
2111 rv = stmt->Execute();
2112 if (NS_WARN_IF(NS_FAILED(rv))) {
2113 return rv;
2116 rv = aConnection.ExecuteSimpleSQL("DROP TABLE database;"_ns);
2117 if (NS_WARN_IF(NS_FAILED(rv))) {
2118 return rv;
2121 rv = aConnection.ExecuteSimpleSQL(
2122 "ALTER TABLE database_upgrade "
2123 "RENAME TO database;"_ns);
2124 if (NS_WARN_IF(NS_FAILED(rv))) {
2125 return rv;
2128 #ifdef DEBUG
2130 // Make sure there's only one entry in the |database| table.
2131 QM_TRY_INSPECT(const auto& stmt,
2132 quota::CreateAndExecuteSingleStepStatement(
2133 aConnection, "SELECT COUNT(*) FROM database;"_ns),
2134 QM_ASSERT_UNREACHABLE);
2136 int64_t count;
2137 MOZ_ASSERT(NS_SUCCEEDED(stmt->GetInt64(0, &count)));
2139 MOZ_ASSERT(count == 1);
2141 #endif
2143 // Recreate file table triggers.
2144 rv = aConnection.ExecuteSimpleSQL(
2145 "CREATE TRIGGER object_data_insert_trigger "
2146 "AFTER INSERT ON object_data "
2147 "WHEN NEW.file_ids IS NOT NULL "
2148 "BEGIN "
2149 "SELECT update_refcount(NULL, NEW.file_ids);"
2150 "END;"_ns);
2151 if (NS_WARN_IF(NS_FAILED(rv))) {
2152 return rv;
2155 rv = aConnection.ExecuteSimpleSQL(
2156 "CREATE TRIGGER object_data_update_trigger "
2157 "AFTER UPDATE OF file_ids ON object_data "
2158 "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
2159 "BEGIN "
2160 "SELECT update_refcount(OLD.file_ids, NEW.file_ids);"
2161 "END;"_ns);
2162 if (NS_WARN_IF(NS_FAILED(rv))) {
2163 return rv;
2166 rv = aConnection.ExecuteSimpleSQL(
2167 "CREATE TRIGGER object_data_delete_trigger "
2168 "AFTER DELETE ON object_data "
2169 "WHEN OLD.file_ids IS NOT NULL "
2170 "BEGIN "
2171 "SELECT update_refcount(OLD.file_ids, NULL);"
2172 "END;"_ns);
2173 if (NS_WARN_IF(NS_FAILED(rv))) {
2174 return rv;
2177 // Finally, turn on auto_vacuum mode. We use full auto_vacuum mode to reclaim
2178 // disk space on mobile devices (at the cost of some COMMIT speed), and
2179 // incremental auto_vacuum mode on desktop builds.
2180 rv = aConnection.ExecuteSimpleSQL(
2181 #ifdef IDB_MOBILE
2182 "PRAGMA auto_vacuum = FULL;"_ns
2183 #else
2184 "PRAGMA auto_vacuum = INCREMENTAL;"_ns
2185 #endif
2187 if (NS_WARN_IF(NS_FAILED(rv))) {
2188 return rv;
2191 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(18, 0));
2192 if (NS_WARN_IF(NS_FAILED(rv))) {
2193 return rv;
2196 return NS_OK;
2199 nsresult UpgradeSchemaFrom17_0To18_0(mozIStorageConnection& aConnection,
2200 const nsACString& aOrigin) {
2201 MOZ_ASSERT(!aOrigin.IsEmpty());
2203 AUTO_PROFILER_LABEL("UpgradeSchemaFrom17_0To18_0", DOM);
2205 return UpgradeSchemaFrom17_0To18_0Helper::DoUpgrade(aConnection, aOrigin);
2208 nsresult UpgradeSchemaFrom18_0To19_0(mozIStorageConnection& aConnection) {
2209 AssertIsOnIOThread();
2211 nsresult rv;
2212 AUTO_PROFILER_LABEL("UpgradeSchemaFrom18_0To19_0", DOM);
2214 rv = aConnection.ExecuteSimpleSQL(
2215 "ALTER TABLE object_store_index "
2216 "ADD COLUMN locale TEXT;"_ns);
2217 if (NS_WARN_IF(NS_FAILED(rv))) {
2218 return rv;
2221 rv = aConnection.ExecuteSimpleSQL(
2222 "ALTER TABLE object_store_index "
2223 "ADD COLUMN is_auto_locale BOOLEAN;"_ns);
2224 if (NS_WARN_IF(NS_FAILED(rv))) {
2225 return rv;
2228 rv = aConnection.ExecuteSimpleSQL(
2229 "ALTER TABLE index_data "
2230 "ADD COLUMN value_locale BLOB;"_ns);
2231 if (NS_WARN_IF(NS_FAILED(rv))) {
2232 return rv;
2235 rv = aConnection.ExecuteSimpleSQL(
2236 "ALTER TABLE unique_index_data "
2237 "ADD COLUMN value_locale BLOB;"_ns);
2238 if (NS_WARN_IF(NS_FAILED(rv))) {
2239 return rv;
2242 rv = aConnection.ExecuteSimpleSQL(
2243 "CREATE INDEX index_data_value_locale_index "
2244 "ON index_data (index_id, value_locale, object_data_key, value) "
2245 "WHERE value_locale IS NOT NULL;"_ns);
2246 if (NS_WARN_IF(NS_FAILED(rv))) {
2247 return rv;
2250 rv = aConnection.ExecuteSimpleSQL(
2251 "CREATE INDEX unique_index_data_value_locale_index "
2252 "ON unique_index_data (index_id, value_locale, object_data_key, value) "
2253 "WHERE value_locale IS NOT NULL;"_ns);
2254 if (NS_WARN_IF(NS_FAILED(rv))) {
2255 return rv;
2258 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(19, 0));
2259 if (NS_WARN_IF(NS_FAILED(rv))) {
2260 return rv;
2263 return NS_OK;
2266 class UpgradeFileIdsFunction final : public mozIStorageFunction {
2267 SafeRefPtr<DatabaseFileManager> mFileManager;
2269 public:
2270 UpgradeFileIdsFunction() { AssertIsOnIOThread(); }
2272 nsresult Init(nsIFile* aFMDirectory, mozIStorageConnection& aConnection);
2274 NS_DECL_ISUPPORTS
2276 private:
2277 ~UpgradeFileIdsFunction() {
2278 AssertIsOnIOThread();
2280 if (mFileManager) {
2281 mFileManager->Invalidate();
2285 NS_IMETHOD
2286 OnFunctionCall(mozIStorageValueArray* aArguments,
2287 nsIVariant** aResult) override;
2290 nsresult UpgradeSchemaFrom19_0To20_0(nsIFile* aFMDirectory,
2291 mozIStorageConnection& aConnection) {
2292 AssertIsOnIOThread();
2294 AUTO_PROFILER_LABEL("UpgradeSchemaFrom19_0To20_0", DOM);
2296 nsCOMPtr<mozIStorageStatement> stmt;
2297 nsresult rv = aConnection.CreateStatement(
2298 "SELECT count(*) "
2299 "FROM object_data "
2300 "WHERE file_ids IS NOT NULL"_ns,
2301 getter_AddRefs(stmt));
2302 if (NS_WARN_IF(NS_FAILED(rv))) {
2303 return rv;
2306 int64_t count;
2309 mozStorageStatementScoper scoper(stmt);
2311 QM_TRY_INSPECT(const bool& hasResult,
2312 MOZ_TO_RESULT_INVOKE(stmt, ExecuteStep));
2314 if (NS_WARN_IF(!hasResult)) {
2315 MOZ_ASSERT(false, "This should never be possible!");
2316 return NS_ERROR_FAILURE;
2319 count = stmt->AsInt64(0);
2320 if (NS_WARN_IF(count < 0)) {
2321 MOZ_ASSERT(false, "This should never be possible!");
2322 return NS_ERROR_FAILURE;
2326 if (count == 0) {
2327 // Nothing to upgrade.
2328 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(20, 0));
2329 if (NS_WARN_IF(NS_FAILED(rv))) {
2330 return rv;
2333 return NS_OK;
2336 RefPtr<UpgradeFileIdsFunction> function = new UpgradeFileIdsFunction();
2338 rv = function->Init(aFMDirectory, aConnection);
2339 if (NS_WARN_IF(NS_FAILED(rv))) {
2340 return rv;
2343 constexpr auto functionName = "upgrade"_ns;
2345 rv = aConnection.CreateFunction(functionName, 2, function);
2346 if (NS_WARN_IF(NS_FAILED(rv))) {
2347 return rv;
2350 // Disable update trigger.
2351 rv = aConnection.ExecuteSimpleSQL(
2352 "DROP TRIGGER object_data_update_trigger;"_ns);
2353 if (NS_WARN_IF(NS_FAILED(rv))) {
2354 return rv;
2357 rv = aConnection.ExecuteSimpleSQL(
2358 "UPDATE object_data "
2359 "SET file_ids = upgrade(file_ids, data) "
2360 "WHERE file_ids IS NOT NULL;"_ns);
2361 if (NS_WARN_IF(NS_FAILED(rv))) {
2362 return rv;
2365 // Enable update trigger.
2366 rv = aConnection.ExecuteSimpleSQL(
2367 "CREATE TRIGGER object_data_update_trigger "
2368 "AFTER UPDATE OF file_ids ON object_data "
2369 "FOR EACH ROW "
2370 "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
2371 "BEGIN "
2372 "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
2373 "END;"_ns);
2374 if (NS_WARN_IF(NS_FAILED(rv))) {
2375 return rv;
2378 rv = aConnection.RemoveFunction(functionName);
2379 if (NS_WARN_IF(NS_FAILED(rv))) {
2380 return rv;
2383 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(20, 0));
2384 if (NS_WARN_IF(NS_FAILED(rv))) {
2385 return rv;
2388 return NS_OK;
2391 class UpgradeIndexDataValuesFunction final : public mozIStorageFunction {
2392 public:
2393 UpgradeIndexDataValuesFunction() { AssertIsOnIOThread(); }
2395 NS_DECL_ISUPPORTS
2397 private:
2398 ~UpgradeIndexDataValuesFunction() { AssertIsOnIOThread(); }
2400 using IndexDataValuesArray = IndexDataValuesAutoArray;
2401 Result<IndexDataValuesArray, nsresult> ReadOldCompressedIDVFromBlob(
2402 Span<const uint8_t> aBlobData);
2404 NS_IMETHOD
2405 OnFunctionCall(mozIStorageValueArray* aArguments,
2406 nsIVariant** aResult) override;
2409 NS_IMPL_ISUPPORTS(UpgradeIndexDataValuesFunction, mozIStorageFunction)
2411 Result<UpgradeIndexDataValuesFunction::IndexDataValuesArray, nsresult>
2412 UpgradeIndexDataValuesFunction::ReadOldCompressedIDVFromBlob(
2413 const Span<const uint8_t> aBlobData) {
2414 MOZ_ASSERT(!NS_IsMainThread());
2415 MOZ_ASSERT(!IsOnBackgroundThread());
2417 IndexOrObjectStoreId indexId;
2418 bool unique;
2419 bool nextIndexIdAlreadyRead = false;
2421 IndexDataValuesArray result;
2422 for (auto remainder = aBlobData; !remainder.IsEmpty();) {
2423 if (!nextIndexIdAlreadyRead) {
2424 QM_TRY_UNWRAP((std::tie(indexId, unique, remainder)),
2425 ReadCompressedIndexId(remainder));
2427 nextIndexIdAlreadyRead = false;
2429 if (NS_WARN_IF(remainder.IsEmpty())) {
2430 IDB_REPORT_INTERNAL_ERR();
2431 return Err(NS_ERROR_FILE_CORRUPTED);
2434 // Read key buffer length.
2435 QM_TRY_INSPECT(
2436 (const auto& [keyBufferLength, remainderAfterKeyBufferLength]),
2437 ReadCompressedNumber(remainder));
2439 if (NS_WARN_IF(remainderAfterKeyBufferLength.IsEmpty()) ||
2440 NS_WARN_IF(keyBufferLength > uint64_t(UINT32_MAX)) ||
2441 NS_WARN_IF(keyBufferLength > remainderAfterKeyBufferLength.Length())) {
2442 IDB_REPORT_INTERNAL_ERR();
2443 return Err(NS_ERROR_FILE_CORRUPTED);
2446 const auto [keyBuffer, remainderAfterKeyBuffer] =
2447 remainderAfterKeyBufferLength.SplitAt(keyBufferLength);
2448 if (NS_WARN_IF(!result.EmplaceBack(fallible, indexId, unique,
2449 Key{nsCString{AsChars(keyBuffer)}}))) {
2450 IDB_REPORT_INTERNAL_ERR();
2451 return Err(NS_ERROR_OUT_OF_MEMORY);
2454 remainder = remainderAfterKeyBuffer;
2455 if (!remainder.IsEmpty()) {
2456 // Read either a sort key buffer length or an index id.
2457 QM_TRY_INSPECT((const auto& [maybeIndexId, remainderAfterIndexId]),
2458 ReadCompressedNumber(remainder));
2460 // Locale-aware indexes haven't been around long enough to have any users,
2461 // we can safely assume all sort key buffer lengths will be zero.
2462 // XXX This duplicates logic from ReadCompressedIndexId.
2463 if (maybeIndexId != 0) {
2464 unique = maybeIndexId % 2 == 1;
2465 indexId = maybeIndexId / 2;
2466 nextIndexIdAlreadyRead = true;
2469 remainder = remainderAfterIndexId;
2472 result.Sort();
2474 return result;
2477 NS_IMETHODIMP
2478 UpgradeIndexDataValuesFunction::OnFunctionCall(
2479 mozIStorageValueArray* aArguments, nsIVariant** aResult) {
2480 MOZ_ASSERT(aArguments);
2481 MOZ_ASSERT(aResult);
2483 AUTO_PROFILER_LABEL("UpgradeIndexDataValuesFunction::OnFunctionCall", DOM);
2485 uint32_t argc;
2486 nsresult rv = aArguments->GetNumEntries(&argc);
2487 if (NS_WARN_IF(NS_FAILED(rv))) {
2488 return rv;
2491 if (argc != 1) {
2492 NS_WARNING("Don't call me with the wrong number of arguments!");
2493 return NS_ERROR_UNEXPECTED;
2496 int32_t type;
2497 rv = aArguments->GetTypeOfIndex(0, &type);
2498 if (NS_WARN_IF(NS_FAILED(rv))) {
2499 return rv;
2502 if (type != mozIStorageStatement::VALUE_TYPE_BLOB) {
2503 NS_WARNING("Don't call me with the wrong type of arguments!");
2504 return NS_ERROR_UNEXPECTED;
2507 const uint8_t* oldBlob;
2508 uint32_t oldBlobLength;
2509 rv = aArguments->GetSharedBlob(0, &oldBlobLength, &oldBlob);
2510 if (NS_WARN_IF(NS_FAILED(rv))) {
2511 return rv;
2514 QM_TRY_INSPECT(const auto& oldIdv,
2515 ReadOldCompressedIDVFromBlob(Span(oldBlob, oldBlobLength)));
2517 QM_TRY_UNWRAP((auto [newIdv, newIdvLength]),
2518 MakeCompressedIndexDataValues(oldIdv));
2520 nsCOMPtr<nsIVariant> result = new storage::AdoptedBlobVariant(
2521 std::pair(newIdv.release(), newIdvLength));
2523 result.forget(aResult);
2524 return NS_OK;
2527 nsresult UpgradeSchemaFrom20_0To21_0(mozIStorageConnection& aConnection) {
2528 // This should have been part of the 18 to 19 upgrade, where we changed the
2529 // layout of the index_data_values blobs but didn't upgrade the existing data.
2530 // See bug 1202788.
2532 AssertIsOnIOThread();
2534 AUTO_PROFILER_LABEL("UpgradeSchemaFrom20_0To21_0", DOM);
2536 RefPtr<UpgradeIndexDataValuesFunction> function =
2537 new UpgradeIndexDataValuesFunction();
2539 constexpr auto functionName = "upgrade_idv"_ns;
2541 nsresult rv = aConnection.CreateFunction(functionName, 1, function);
2542 if (NS_WARN_IF(NS_FAILED(rv))) {
2543 return rv;
2546 rv = aConnection.ExecuteSimpleSQL(
2547 "UPDATE object_data "
2548 "SET index_data_values = upgrade_idv(index_data_values) "
2549 "WHERE index_data_values IS NOT NULL;"_ns);
2550 if (NS_WARN_IF(NS_FAILED(rv))) {
2551 return rv;
2554 rv = aConnection.RemoveFunction(functionName);
2555 if (NS_WARN_IF(NS_FAILED(rv))) {
2556 return rv;
2559 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(21, 0));
2560 if (NS_WARN_IF(NS_FAILED(rv))) {
2561 return rv;
2564 return NS_OK;
2567 nsresult UpgradeSchemaFrom21_0To22_0(mozIStorageConnection& aConnection) {
2568 // The only change between 21 and 22 was a different structured clone format,
2569 // but it's backwards-compatible.
2570 nsresult rv = aConnection.SetSchemaVersion(MakeSchemaVersion(22, 0));
2571 if (NS_WARN_IF(NS_FAILED(rv))) {
2572 return rv;
2575 return NS_OK;
2578 nsresult UpgradeSchemaFrom22_0To23_0(mozIStorageConnection& aConnection,
2579 const nsACString& aOrigin) {
2580 AssertIsOnIOThread();
2582 MOZ_ASSERT(!aOrigin.IsEmpty());
2584 AUTO_PROFILER_LABEL("UpgradeSchemaFrom22_0To23_0", DOM);
2586 nsCOMPtr<mozIStorageStatement> stmt;
2587 // The parameter names are not used, parameters are bound by index only
2588 // locally in the same function.
2589 nsresult rv = aConnection.CreateStatement(
2590 "UPDATE database SET origin = :origin;"_ns, getter_AddRefs(stmt));
2591 if (NS_WARN_IF(NS_FAILED(rv))) {
2592 return rv;
2595 rv = stmt->BindUTF8StringByIndex(0, aOrigin);
2596 if (NS_WARN_IF(NS_FAILED(rv))) {
2597 return rv;
2600 rv = stmt->Execute();
2601 if (NS_WARN_IF(NS_FAILED(rv))) {
2602 return rv;
2605 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(23, 0));
2606 if (NS_WARN_IF(NS_FAILED(rv))) {
2607 return rv;
2610 return NS_OK;
2613 nsresult UpgradeSchemaFrom23_0To24_0(mozIStorageConnection& aConnection) {
2614 // The only change between 23 and 24 was a different structured clone format,
2615 // but it's backwards-compatible.
2616 nsresult rv = aConnection.SetSchemaVersion(MakeSchemaVersion(24, 0));
2617 if (NS_WARN_IF(NS_FAILED(rv))) {
2618 return rv;
2621 return NS_OK;
2624 nsresult UpgradeSchemaFrom24_0To25_0(mozIStorageConnection& aConnection) {
2625 // The changes between 24 and 25 were an upgraded snappy library, a different
2626 // structured clone format and a different file_ds format. But everything is
2627 // backwards-compatible.
2628 nsresult rv = aConnection.SetSchemaVersion(MakeSchemaVersion(25, 0));
2629 if (NS_WARN_IF(NS_FAILED(rv))) {
2630 return rv;
2633 return NS_OK;
2636 class StripObsoleteOriginAttributesFunction final : public mozIStorageFunction {
2637 public:
2638 NS_DECL_ISUPPORTS
2640 private:
2641 ~StripObsoleteOriginAttributesFunction() = default;
2643 NS_IMETHOD
2644 OnFunctionCall(mozIStorageValueArray* aArguments,
2645 nsIVariant** aResult) override {
2646 MOZ_ASSERT(aArguments);
2647 MOZ_ASSERT(aResult);
2649 AUTO_PROFILER_LABEL("StripObsoleteOriginAttributesFunction::OnFunctionCall",
2650 DOM);
2652 #ifdef DEBUG
2654 uint32_t argCount;
2655 MOZ_ALWAYS_SUCCEEDS(aArguments->GetNumEntries(&argCount));
2656 MOZ_ASSERT(argCount == 1);
2658 int32_t type;
2659 MOZ_ALWAYS_SUCCEEDS(aArguments->GetTypeOfIndex(0, &type));
2660 MOZ_ASSERT(type == mozIStorageValueArray::VALUE_TYPE_TEXT);
2662 #endif
2664 nsCString origin;
2665 nsresult rv = aArguments->GetUTF8String(0, origin);
2666 if (NS_WARN_IF(NS_FAILED(rv))) {
2667 return rv;
2670 // Deserialize and re-serialize to automatically drop any obsolete origin
2671 // attributes.
2672 OriginAttributes oa;
2674 nsCString originNoSuffix;
2675 bool ok = oa.PopulateFromOrigin(origin, originNoSuffix);
2676 if (NS_WARN_IF(!ok)) {
2677 return NS_ERROR_FAILURE;
2680 nsCString suffix;
2681 oa.CreateSuffix(suffix);
2683 nsCOMPtr<nsIVariant> result =
2684 new mozilla::storage::UTF8TextVariant(originNoSuffix + suffix);
2686 result.forget(aResult);
2687 return NS_OK;
2691 nsresult UpgradeSchemaFrom25_0To26_0(mozIStorageConnection& aConnection) {
2692 AssertIsOnIOThread();
2694 AUTO_PROFILER_LABEL("UpgradeSchemaFrom25_0To26_0", DOM);
2696 constexpr auto functionName = "strip_obsolete_attributes"_ns;
2698 nsCOMPtr<mozIStorageFunction> stripObsoleteAttributes =
2699 new StripObsoleteOriginAttributesFunction();
2701 nsresult rv = aConnection.CreateFunction(functionName,
2702 /* aNumArguments */ 1,
2703 stripObsoleteAttributes);
2704 if (NS_WARN_IF(NS_FAILED(rv))) {
2705 return rv;
2708 rv = aConnection.ExecuteSimpleSQL(
2709 "UPDATE DATABASE "
2710 "SET origin = strip_obsolete_attributes(origin) "
2711 "WHERE origin LIKE '%^%';"_ns);
2712 if (NS_WARN_IF(NS_FAILED(rv))) {
2713 return rv;
2716 rv = aConnection.RemoveFunction(functionName);
2717 if (NS_WARN_IF(NS_FAILED(rv))) {
2718 return rv;
2721 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(26, 0));
2722 if (NS_WARN_IF(NS_FAILED(rv))) {
2723 return rv;
2726 return NS_OK;
2729 NS_IMPL_ISUPPORTS(CompressDataBlobsFunction, mozIStorageFunction)
2730 NS_IMPL_ISUPPORTS(EncodeKeysFunction, mozIStorageFunction)
2731 NS_IMPL_ISUPPORTS(StripObsoleteOriginAttributesFunction, mozIStorageFunction);
2733 class DeserializeUpgradeValueHelper final : public Runnable {
2734 public:
2735 explicit DeserializeUpgradeValueHelper(
2736 StructuredCloneReadInfoParent& aCloneReadInfo)
2737 : Runnable("DeserializeUpgradeValueHelper"),
2738 mMonitor("DeserializeUpgradeValueHelper::mMonitor"),
2739 mCloneReadInfo(aCloneReadInfo),
2740 mStatus(NS_ERROR_FAILURE) {}
2742 nsresult DispatchAndWait(nsAString& aFileIds) {
2743 // We don't need to go to the main-thread and use the sandbox.
2744 if (!mCloneReadInfo.Data().Size()) {
2745 PopulateFileIds(aFileIds);
2746 return NS_OK;
2749 // The operation will continue on the main-thread.
2751 MOZ_ASSERT(!(mCloneReadInfo.Data().Size() % sizeof(uint64_t)));
2753 MonitorAutoLock lock(mMonitor);
2755 RefPtr<Runnable> self = this;
2756 const nsresult rv =
2757 SchedulerGroup::Dispatch(TaskCategory::Other, self.forget());
2758 if (NS_WARN_IF(NS_FAILED(rv))) {
2759 return rv;
2762 lock.Wait();
2764 if (NS_FAILED(mStatus)) {
2765 return mStatus;
2768 PopulateFileIds(aFileIds);
2769 return NS_OK;
2772 NS_IMETHOD
2773 Run() override {
2774 MOZ_ASSERT(NS_IsMainThread());
2776 AutoJSAPI jsapi;
2777 jsapi.Init();
2778 JSContext* cx = jsapi.cx();
2780 JS::Rooted<JSObject*> global(cx, GetSandbox(cx));
2781 if (NS_WARN_IF(!global)) {
2782 OperationCompleted(NS_ERROR_FAILURE);
2783 return NS_OK;
2786 const JSAutoRealm ar(cx, global);
2788 JS::Rooted<JS::Value> value(cx);
2789 const nsresult rv = DeserializeUpgradeValue(cx, &value);
2790 if (NS_WARN_IF(NS_FAILED(rv))) {
2791 OperationCompleted(rv);
2792 return NS_OK;
2795 OperationCompleted(NS_OK);
2796 return NS_OK;
2799 private:
2800 nsresult DeserializeUpgradeValue(JSContext* aCx,
2801 JS::MutableHandle<JS::Value> aValue) {
2802 static const JSStructuredCloneCallbacks callbacks = {
2803 StructuredCloneReadCallback<StructuredCloneReadInfoParent>,
2804 nullptr,
2805 nullptr,
2806 nullptr,
2807 nullptr,
2808 nullptr,
2809 nullptr,
2810 nullptr};
2812 if (!JS_ReadStructuredClone(
2813 aCx, mCloneReadInfo.Data(), JS_STRUCTURED_CLONE_VERSION,
2814 JS::StructuredCloneScope::DifferentProcessForIndexedDB, aValue,
2815 JS::CloneDataPolicy(), &callbacks, &mCloneReadInfo)) {
2816 return NS_ERROR_DOM_DATA_CLONE_ERR;
2819 return NS_OK;
2822 void PopulateFileIds(nsAString& aFileIds) {
2823 for (uint32_t count = mCloneReadInfo.Files().Length(), index = 0;
2824 index < count; index++) {
2825 const StructuredCloneFileParent& file = mCloneReadInfo.Files()[index];
2827 const int64_t id = file.FileInfo().Id();
2829 if (index) {
2830 aFileIds.Append(' ');
2832 aFileIds.AppendInt(file.Type() == StructuredCloneFileBase::eBlob ? id
2833 : -id);
2837 void OperationCompleted(nsresult aStatus) {
2838 mStatus = aStatus;
2840 MonitorAutoLock lock(mMonitor);
2841 lock.Notify();
2844 Monitor mMonitor;
2845 StructuredCloneReadInfoParent& mCloneReadInfo;
2846 nsresult mStatus;
2849 nsresult DeserializeUpgradeValueToFileIds(
2850 StructuredCloneReadInfoParent& aCloneReadInfo, nsAString& aFileIds) {
2851 MOZ_ASSERT(!NS_IsMainThread());
2853 const RefPtr<DeserializeUpgradeValueHelper> helper =
2854 new DeserializeUpgradeValueHelper(aCloneReadInfo);
2855 return helper->DispatchAndWait(aFileIds);
2858 nsresult UpgradeFileIdsFunction::Init(nsIFile* aFMDirectory,
2859 mozIStorageConnection& aConnection) {
2860 // This DatabaseFileManager doesn't need real origin info, etc. The only
2861 // purpose is to store file ids without adding more complexity or code
2862 // duplication.
2863 auto fileManager = MakeSafeRefPtr<DatabaseFileManager>(
2864 PERSISTENCE_TYPE_INVALID, quota::OriginMetadata{}, u""_ns, false);
2866 nsresult rv = fileManager->Init(aFMDirectory, aConnection);
2867 if (NS_WARN_IF(NS_FAILED(rv))) {
2868 return rv;
2871 mFileManager = std::move(fileManager);
2872 return NS_OK;
2875 NS_IMPL_ISUPPORTS(UpgradeFileIdsFunction, mozIStorageFunction)
2877 NS_IMETHODIMP
2878 UpgradeFileIdsFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
2879 nsIVariant** aResult) {
2880 MOZ_ASSERT(aArguments);
2881 MOZ_ASSERT(aResult);
2882 MOZ_ASSERT(mFileManager);
2884 AUTO_PROFILER_LABEL("UpgradeFileIdsFunction::OnFunctionCall", DOM);
2886 uint32_t argc;
2887 nsresult rv = aArguments->GetNumEntries(&argc);
2888 if (NS_WARN_IF(NS_FAILED(rv))) {
2889 return rv;
2892 if (argc != 2) {
2893 NS_WARNING("Don't call me with the wrong number of arguments!");
2894 return NS_ERROR_UNEXPECTED;
2897 QM_TRY_UNWRAP(auto cloneInfo,
2898 GetStructuredCloneReadInfoFromValueArray(
2899 aArguments, 1, 0, *mFileManager, Nothing{}));
2901 nsAutoString fileIds;
2902 // XXX does this really need non-const cloneInfo?
2903 rv = DeserializeUpgradeValueToFileIds(cloneInfo, fileIds);
2904 if (NS_WARN_IF(NS_FAILED(rv))) {
2905 return NS_ERROR_DOM_DATA_CLONE_ERR;
2908 nsCOMPtr<nsIVariant> result = new mozilla::storage::TextVariant(fileIds);
2910 result.forget(aResult);
2911 return NS_OK;
2914 } // namespace
2916 Result<bool, nsresult> MaybeUpgradeSchema(mozIStorageConnection& aConnection,
2917 const int32_t aSchemaVersion,
2918 nsIFile& aFMDirectory,
2919 const nsACString& aOrigin) {
2920 bool vacuumNeeded = false;
2921 int32_t schemaVersion = aSchemaVersion;
2923 // This logic needs to change next time we change the schema!
2924 static_assert(kSQLiteSchemaVersion == int32_t((26 << 4) + 0),
2925 "Upgrade function needed due to schema version increase.");
2927 while (schemaVersion != kSQLiteSchemaVersion) {
2928 switch (schemaVersion) {
2929 case 4:
2930 QM_TRY(UpgradeSchemaFrom4To5(aConnection));
2931 break;
2932 case 5:
2933 QM_TRY(UpgradeSchemaFrom5To6(aConnection));
2934 break;
2935 case 6:
2936 QM_TRY(UpgradeSchemaFrom6To7(aConnection));
2937 break;
2938 case 7:
2939 QM_TRY(UpgradeSchemaFrom7To8(aConnection));
2940 break;
2941 case 8:
2942 QM_TRY(UpgradeSchemaFrom8To9_0(aConnection));
2943 vacuumNeeded = true;
2944 break;
2945 case MakeSchemaVersion(9, 0):
2946 QM_TRY(UpgradeSchemaFrom9_0To10_0(aConnection));
2947 break;
2948 case MakeSchemaVersion(10, 0):
2949 QM_TRY(UpgradeSchemaFrom10_0To11_0(aConnection));
2950 break;
2951 case MakeSchemaVersion(11, 0):
2952 QM_TRY(UpgradeSchemaFrom11_0To12_0(aConnection));
2953 break;
2954 case MakeSchemaVersion(12, 0):
2955 QM_TRY(UpgradeSchemaFrom12_0To13_0(aConnection, &vacuumNeeded));
2956 break;
2957 case MakeSchemaVersion(13, 0):
2958 QM_TRY(UpgradeSchemaFrom13_0To14_0(aConnection));
2959 break;
2960 case MakeSchemaVersion(14, 0):
2961 QM_TRY(UpgradeSchemaFrom14_0To15_0(aConnection));
2962 break;
2963 case MakeSchemaVersion(15, 0):
2964 QM_TRY(UpgradeSchemaFrom15_0To16_0(aConnection));
2965 break;
2966 case MakeSchemaVersion(16, 0):
2967 QM_TRY(UpgradeSchemaFrom16_0To17_0(aConnection));
2968 break;
2969 case MakeSchemaVersion(17, 0):
2970 QM_TRY(UpgradeSchemaFrom17_0To18_0(aConnection, aOrigin));
2971 vacuumNeeded = true;
2972 break;
2973 case MakeSchemaVersion(18, 0):
2974 QM_TRY(UpgradeSchemaFrom18_0To19_0(aConnection));
2975 break;
2976 case MakeSchemaVersion(19, 0):
2977 QM_TRY(UpgradeSchemaFrom19_0To20_0(&aFMDirectory, aConnection));
2978 break;
2979 case MakeSchemaVersion(20, 0):
2980 QM_TRY(UpgradeSchemaFrom20_0To21_0(aConnection));
2981 break;
2982 case MakeSchemaVersion(21, 0):
2983 QM_TRY(UpgradeSchemaFrom21_0To22_0(aConnection));
2984 break;
2985 case MakeSchemaVersion(22, 0):
2986 QM_TRY(UpgradeSchemaFrom22_0To23_0(aConnection, aOrigin));
2987 break;
2988 case MakeSchemaVersion(23, 0):
2989 QM_TRY(UpgradeSchemaFrom23_0To24_0(aConnection));
2990 break;
2991 case MakeSchemaVersion(24, 0):
2992 QM_TRY(UpgradeSchemaFrom24_0To25_0(aConnection));
2993 break;
2994 case MakeSchemaVersion(25, 0):
2995 QM_TRY(UpgradeSchemaFrom25_0To26_0(aConnection));
2996 break;
2997 default:
2998 QM_FAIL(Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR), []() {
2999 IDB_WARNING(
3000 "Unable to open IndexedDB database, no upgrade path is "
3001 "available!");
3005 QM_TRY_UNWRAP(schemaVersion,
3006 MOZ_TO_RESULT_INVOKE(aConnection, GetSchemaVersion));
3009 MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion);
3011 return vacuumNeeded;
3014 } // namespace mozilla::dom::indexedDB
3016 #undef IDB_MOBILE