Bug 1842773 - Part 5: Add ArrayBuffer.prototype.{maxByteLength,resizable} getters...
[gecko.git] / dom / indexedDB / SchemaUpgrades.cpp
blob60663907406db535a0e511e63a924561404eaee5
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/UniquePtr.h"
46 #include "mozilla/dom/ScriptSettings.h"
47 #include "mozilla/dom/indexedDB/IDBResult.h"
48 #include "mozilla/dom/indexedDB/Key.h"
49 #include "mozilla/dom/quota/Assertions.h"
50 #include "mozilla/dom/quota/PersistenceType.h"
51 #include "mozilla/dom/quota/QuotaCommon.h"
52 #include "mozilla/dom/quota/ResultExtensions.h"
53 #include "mozilla/fallible.h"
54 #include "mozilla/ipc/BackgroundParent.h"
55 #include "mozilla/mozalloc.h"
56 #include "mozilla/ProfilerLabels.h"
57 #include "mozilla/storage/Variant.h"
58 #include "nsCOMPtr.h"
59 #include "nsDebug.h"
60 #include "nsError.h"
61 #include "nsISupports.h"
62 #include "nsIVariant.h"
63 #include "nsLiteralString.h"
64 #include "nsString.h"
65 #include "nsTArray.h"
66 #include "nsTLiteralString.h"
67 #include "nsTStringRepr.h"
68 #include "nsThreadUtils.h"
69 #include "nscore.h"
70 #include "snappy/snappy.h"
72 struct JSContext;
73 class JSObject;
75 #if defined(MOZ_WIDGET_ANDROID)
76 # define IDB_MOBILE
77 #endif
79 namespace mozilla::dom::indexedDB {
81 using mozilla::ipc::IsOnBackgroundThread;
82 using quota::AssertIsOnIOThread;
83 using quota::PERSISTENCE_TYPE_INVALID;
85 namespace {
87 nsresult UpgradeSchemaFrom4To5(mozIStorageConnection& aConnection) {
88 AssertIsOnIOThread();
90 AUTO_PROFILER_LABEL("UpgradeSchemaFrom4To5", DOM);
92 nsresult rv;
94 // All we changed is the type of the version column, so lets try to
95 // convert that to an integer, and if we fail, set it to 0.
96 nsCOMPtr<mozIStorageStatement> stmt;
97 rv = aConnection.CreateStatement(
98 "SELECT name, version, dataVersion "
99 "FROM database"_ns,
100 getter_AddRefs(stmt));
101 if (NS_WARN_IF(NS_FAILED(rv))) {
102 return rv;
105 nsString name;
106 int32_t intVersion;
107 int64_t dataVersion;
110 mozStorageStatementScoper scoper(stmt);
112 QM_TRY_INSPECT(const bool& hasResults,
113 MOZ_TO_RESULT_INVOKE_MEMBER(stmt, ExecuteStep));
115 if (NS_WARN_IF(!hasResults)) {
116 return NS_ERROR_FAILURE;
119 nsString version;
120 rv = stmt->GetString(1, version);
121 if (NS_WARN_IF(NS_FAILED(rv))) {
122 return rv;
125 intVersion = version.ToInteger(&rv);
126 if (NS_FAILED(rv)) {
127 intVersion = 0;
130 rv = stmt->GetString(0, name);
131 if (NS_WARN_IF(NS_FAILED(rv))) {
132 return rv;
135 rv = stmt->GetInt64(2, &dataVersion);
136 if (NS_WARN_IF(NS_FAILED(rv))) {
137 return rv;
141 rv = aConnection.ExecuteSimpleSQL("DROP TABLE database"_ns);
142 if (NS_WARN_IF(NS_FAILED(rv))) {
143 return rv;
146 rv = aConnection.ExecuteSimpleSQL(
147 "CREATE TABLE database ("
148 "name TEXT NOT NULL, "
149 "version INTEGER NOT NULL DEFAULT 0, "
150 "dataVersion INTEGER NOT NULL"
151 ");"_ns);
152 if (NS_WARN_IF(NS_FAILED(rv))) {
153 return rv;
156 // The parameter names are not used, parameters are bound by index only
157 // locally in the same function.
158 rv = aConnection.CreateStatement(
159 "INSERT INTO database (name, version, dataVersion) "
160 "VALUES (:name, :version, :dataVersion)"_ns,
161 getter_AddRefs(stmt));
162 if (NS_WARN_IF(NS_FAILED(rv))) {
163 return rv;
167 mozStorageStatementScoper scoper(stmt);
169 rv = stmt->BindStringByIndex(0, name);
170 if (NS_WARN_IF(NS_FAILED(rv))) {
171 return rv;
174 rv = stmt->BindInt32ByIndex(1, intVersion);
175 if (NS_WARN_IF(NS_FAILED(rv))) {
176 return rv;
179 rv = stmt->BindInt64ByIndex(2, dataVersion);
180 if (NS_WARN_IF(NS_FAILED(rv))) {
181 return rv;
184 rv = stmt->Execute();
185 if (NS_WARN_IF(NS_FAILED(rv))) {
186 return rv;
190 rv = aConnection.SetSchemaVersion(5);
191 if (NS_WARN_IF(NS_FAILED(rv))) {
192 return rv;
195 return NS_OK;
198 nsresult UpgradeSchemaFrom5To6(mozIStorageConnection& aConnection) {
199 AssertIsOnIOThread();
201 AUTO_PROFILER_LABEL("UpgradeSchemaFrom5To6", DOM);
203 // First, drop all the indexes we're no longer going to use.
204 nsresult rv = aConnection.ExecuteSimpleSQL("DROP INDEX key_index;"_ns);
205 if (NS_WARN_IF(NS_FAILED(rv))) {
206 return rv;
209 rv = aConnection.ExecuteSimpleSQL("DROP INDEX ai_key_index;"_ns);
210 if (NS_WARN_IF(NS_FAILED(rv))) {
211 return rv;
214 rv = aConnection.ExecuteSimpleSQL("DROP INDEX value_index;"_ns);
215 if (NS_WARN_IF(NS_FAILED(rv))) {
216 return rv;
219 rv = aConnection.ExecuteSimpleSQL("DROP INDEX ai_value_index;"_ns);
220 if (NS_WARN_IF(NS_FAILED(rv))) {
221 return rv;
224 // Now, reorder the columns of object_data to put the blob data last. We do
225 // this by copying into a temporary table, dropping the original, then copying
226 // back into a newly created table.
227 rv = aConnection.ExecuteSimpleSQL(
228 "CREATE TEMPORARY TABLE temp_upgrade ("
229 "id INTEGER PRIMARY KEY, "
230 "object_store_id, "
231 "key_value, "
232 "data "
233 ");"_ns);
234 if (NS_WARN_IF(NS_FAILED(rv))) {
235 return rv;
238 rv = aConnection.ExecuteSimpleSQL(
239 "INSERT INTO temp_upgrade "
240 "SELECT id, object_store_id, key_value, data "
241 "FROM object_data;"_ns);
242 if (NS_WARN_IF(NS_FAILED(rv))) {
243 return rv;
246 rv = aConnection.ExecuteSimpleSQL("DROP TABLE object_data;"_ns);
247 if (NS_WARN_IF(NS_FAILED(rv))) {
248 return rv;
251 rv = aConnection.ExecuteSimpleSQL(
252 "CREATE TABLE object_data ("
253 "id INTEGER PRIMARY KEY, "
254 "object_store_id INTEGER NOT NULL, "
255 "key_value DEFAULT NULL, "
256 "data BLOB NOT NULL, "
257 "UNIQUE (object_store_id, key_value), "
258 "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
259 "CASCADE"
260 ");"_ns);
261 if (NS_WARN_IF(NS_FAILED(rv))) {
262 return rv;
265 rv = aConnection.ExecuteSimpleSQL(
266 "INSERT INTO object_data "
267 "SELECT id, object_store_id, key_value, data "
268 "FROM temp_upgrade;"_ns);
269 if (NS_WARN_IF(NS_FAILED(rv))) {
270 return rv;
273 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
274 if (NS_WARN_IF(NS_FAILED(rv))) {
275 return rv;
278 // We need to add a unique constraint to our ai_object_data table. Copy all
279 // the data out of it using a temporary table as before.
280 rv = aConnection.ExecuteSimpleSQL(
281 "CREATE TEMPORARY TABLE temp_upgrade ("
282 "id INTEGER PRIMARY KEY, "
283 "object_store_id, "
284 "data "
285 ");"_ns);
286 if (NS_WARN_IF(NS_FAILED(rv))) {
287 return rv;
290 rv = aConnection.ExecuteSimpleSQL(
291 "INSERT INTO temp_upgrade "
292 "SELECT id, object_store_id, data "
293 "FROM ai_object_data;"_ns);
294 if (NS_WARN_IF(NS_FAILED(rv))) {
295 return rv;
298 rv = aConnection.ExecuteSimpleSQL("DROP TABLE ai_object_data;"_ns);
299 if (NS_WARN_IF(NS_FAILED(rv))) {
300 return rv;
303 rv = aConnection.ExecuteSimpleSQL(
304 "CREATE TABLE ai_object_data ("
305 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
306 "object_store_id INTEGER NOT NULL, "
307 "data BLOB NOT NULL, "
308 "UNIQUE (object_store_id, id), "
309 "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
310 "CASCADE"
311 ");"_ns);
312 if (NS_WARN_IF(NS_FAILED(rv))) {
313 return rv;
316 rv = aConnection.ExecuteSimpleSQL(
317 "INSERT INTO ai_object_data "
318 "SELECT id, object_store_id, data "
319 "FROM temp_upgrade;"_ns);
320 if (NS_WARN_IF(NS_FAILED(rv))) {
321 return rv;
324 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
325 if (NS_WARN_IF(NS_FAILED(rv))) {
326 return rv;
329 // Fix up the index_data table. We're reordering the columns as well as
330 // changing the primary key from being a simple id to being a composite.
331 rv = aConnection.ExecuteSimpleSQL(
332 "CREATE TEMPORARY TABLE temp_upgrade ("
333 "index_id, "
334 "value, "
335 "object_data_key, "
336 "object_data_id "
337 ");"_ns);
338 if (NS_WARN_IF(NS_FAILED(rv))) {
339 return rv;
342 rv = aConnection.ExecuteSimpleSQL(
343 "INSERT INTO temp_upgrade "
344 "SELECT index_id, value, object_data_key, object_data_id "
345 "FROM index_data;"_ns);
346 if (NS_WARN_IF(NS_FAILED(rv))) {
347 return rv;
350 rv = aConnection.ExecuteSimpleSQL("DROP TABLE index_data;"_ns);
351 if (NS_WARN_IF(NS_FAILED(rv))) {
352 return rv;
355 rv = aConnection.ExecuteSimpleSQL(
356 "CREATE TABLE index_data ("
357 "index_id INTEGER NOT NULL, "
358 "value NOT NULL, "
359 "object_data_key NOT NULL, "
360 "object_data_id INTEGER NOT NULL, "
361 "PRIMARY KEY (index_id, value, object_data_key), "
362 "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
363 "CASCADE, "
364 "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
365 "CASCADE"
366 ");"_ns);
367 if (NS_WARN_IF(NS_FAILED(rv))) {
368 return rv;
371 rv = aConnection.ExecuteSimpleSQL(
372 "INSERT OR IGNORE INTO index_data "
373 "SELECT index_id, value, object_data_key, object_data_id "
374 "FROM temp_upgrade;"_ns);
375 if (NS_WARN_IF(NS_FAILED(rv))) {
376 return rv;
379 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
380 if (NS_WARN_IF(NS_FAILED(rv))) {
381 return rv;
384 rv = aConnection.ExecuteSimpleSQL(
385 "CREATE INDEX index_data_object_data_id_index "
386 "ON index_data (object_data_id);"_ns);
387 if (NS_WARN_IF(NS_FAILED(rv))) {
388 return rv;
391 // Fix up the unique_index_data table. We're reordering the columns as well as
392 // changing the primary key from being a simple id to being a composite.
393 rv = aConnection.ExecuteSimpleSQL(
394 "CREATE TEMPORARY TABLE temp_upgrade ("
395 "index_id, "
396 "value, "
397 "object_data_key, "
398 "object_data_id "
399 ");"_ns);
400 if (NS_WARN_IF(NS_FAILED(rv))) {
401 return rv;
404 rv = aConnection.ExecuteSimpleSQL(
405 "INSERT INTO temp_upgrade "
406 "SELECT index_id, value, object_data_key, object_data_id "
407 "FROM unique_index_data;"_ns);
408 if (NS_WARN_IF(NS_FAILED(rv))) {
409 return rv;
412 rv = aConnection.ExecuteSimpleSQL("DROP TABLE unique_index_data;"_ns);
413 if (NS_WARN_IF(NS_FAILED(rv))) {
414 return rv;
417 rv = aConnection.ExecuteSimpleSQL(
418 "CREATE TABLE unique_index_data ("
419 "index_id INTEGER NOT NULL, "
420 "value NOT NULL, "
421 "object_data_key NOT NULL, "
422 "object_data_id INTEGER NOT NULL, "
423 "PRIMARY KEY (index_id, value, object_data_key), "
424 "UNIQUE (index_id, value), "
425 "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
426 "CASCADE "
427 "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
428 "CASCADE"
429 ");"_ns);
430 if (NS_WARN_IF(NS_FAILED(rv))) {
431 return rv;
434 rv = aConnection.ExecuteSimpleSQL(
435 "INSERT INTO unique_index_data "
436 "SELECT index_id, value, object_data_key, object_data_id "
437 "FROM temp_upgrade;"_ns);
438 if (NS_WARN_IF(NS_FAILED(rv))) {
439 return rv;
442 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
443 if (NS_WARN_IF(NS_FAILED(rv))) {
444 return rv;
447 rv = aConnection.ExecuteSimpleSQL(
448 "CREATE INDEX unique_index_data_object_data_id_index "
449 "ON unique_index_data (object_data_id);"_ns);
450 if (NS_WARN_IF(NS_FAILED(rv))) {
451 return rv;
454 // Fix up the ai_index_data table. We're reordering the columns as well as
455 // changing the primary key from being a simple id to being a composite.
456 rv = aConnection.ExecuteSimpleSQL(
457 "CREATE TEMPORARY TABLE temp_upgrade ("
458 "index_id, "
459 "value, "
460 "ai_object_data_id "
461 ");"_ns);
462 if (NS_WARN_IF(NS_FAILED(rv))) {
463 return rv;
466 rv = aConnection.ExecuteSimpleSQL(
467 "INSERT INTO temp_upgrade "
468 "SELECT index_id, value, ai_object_data_id "
469 "FROM ai_index_data;"_ns);
470 if (NS_WARN_IF(NS_FAILED(rv))) {
471 return rv;
474 rv = aConnection.ExecuteSimpleSQL("DROP TABLE ai_index_data;"_ns);
475 if (NS_WARN_IF(NS_FAILED(rv))) {
476 return rv;
479 rv = aConnection.ExecuteSimpleSQL(
480 "CREATE TABLE ai_index_data ("
481 "index_id INTEGER NOT NULL, "
482 "value NOT NULL, "
483 "ai_object_data_id INTEGER NOT NULL, "
484 "PRIMARY KEY (index_id, value, ai_object_data_id), "
485 "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
486 "CASCADE, "
487 "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
488 "CASCADE"
489 ");"_ns);
490 if (NS_WARN_IF(NS_FAILED(rv))) {
491 return rv;
494 rv = aConnection.ExecuteSimpleSQL(
495 "INSERT OR IGNORE INTO ai_index_data "
496 "SELECT index_id, value, ai_object_data_id "
497 "FROM temp_upgrade;"_ns);
498 if (NS_WARN_IF(NS_FAILED(rv))) {
499 return rv;
502 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
503 if (NS_WARN_IF(NS_FAILED(rv))) {
504 return rv;
507 rv = aConnection.ExecuteSimpleSQL(
508 "CREATE INDEX ai_index_data_ai_object_data_id_index "
509 "ON ai_index_data (ai_object_data_id);"_ns);
510 if (NS_WARN_IF(NS_FAILED(rv))) {
511 return rv;
514 // Fix up the ai_unique_index_data table. We're reordering the columns as well
515 // as changing the primary key from being a simple id to being a composite.
516 rv = aConnection.ExecuteSimpleSQL(
517 "CREATE TEMPORARY TABLE temp_upgrade ("
518 "index_id, "
519 "value, "
520 "ai_object_data_id "
521 ");"_ns);
522 if (NS_WARN_IF(NS_FAILED(rv))) {
523 return rv;
526 rv = aConnection.ExecuteSimpleSQL(
527 "INSERT INTO temp_upgrade "
528 "SELECT index_id, value, ai_object_data_id "
529 "FROM ai_unique_index_data;"_ns);
530 if (NS_WARN_IF(NS_FAILED(rv))) {
531 return rv;
534 rv = aConnection.ExecuteSimpleSQL("DROP TABLE ai_unique_index_data;"_ns);
535 if (NS_WARN_IF(NS_FAILED(rv))) {
536 return rv;
539 rv = aConnection.ExecuteSimpleSQL(
540 "CREATE TABLE ai_unique_index_data ("
541 "index_id INTEGER NOT NULL, "
542 "value NOT NULL, "
543 "ai_object_data_id INTEGER NOT NULL, "
544 "UNIQUE (index_id, value), "
545 "PRIMARY KEY (index_id, value, ai_object_data_id), "
546 "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
547 "CASCADE, "
548 "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
549 "CASCADE"
550 ");"_ns);
551 if (NS_WARN_IF(NS_FAILED(rv))) {
552 return rv;
555 rv = aConnection.ExecuteSimpleSQL(
556 "INSERT INTO ai_unique_index_data "
557 "SELECT index_id, value, ai_object_data_id "
558 "FROM temp_upgrade;"_ns);
559 if (NS_WARN_IF(NS_FAILED(rv))) {
560 return rv;
563 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
564 if (NS_WARN_IF(NS_FAILED(rv))) {
565 return rv;
568 rv = aConnection.ExecuteSimpleSQL(
569 "CREATE INDEX ai_unique_index_data_ai_object_data_id_index "
570 "ON ai_unique_index_data (ai_object_data_id);"_ns);
571 if (NS_WARN_IF(NS_FAILED(rv))) {
572 return rv;
575 rv = aConnection.SetSchemaVersion(6);
576 if (NS_WARN_IF(NS_FAILED(rv))) {
577 return rv;
580 return NS_OK;
583 nsresult UpgradeSchemaFrom6To7(mozIStorageConnection& aConnection) {
584 AssertIsOnIOThread();
586 AUTO_PROFILER_LABEL("UpgradeSchemaFrom6To7", DOM);
588 nsresult rv = aConnection.ExecuteSimpleSQL(
589 "CREATE TEMPORARY TABLE temp_upgrade ("
590 "id, "
591 "name, "
592 "key_path, "
593 "auto_increment"
594 ");"_ns);
595 if (NS_WARN_IF(NS_FAILED(rv))) {
596 return rv;
599 rv = aConnection.ExecuteSimpleSQL(
600 "INSERT INTO temp_upgrade "
601 "SELECT id, name, key_path, auto_increment "
602 "FROM object_store;"_ns);
603 if (NS_WARN_IF(NS_FAILED(rv))) {
604 return rv;
607 rv = aConnection.ExecuteSimpleSQL("DROP TABLE object_store;"_ns);
608 if (NS_WARN_IF(NS_FAILED(rv))) {
609 return rv;
612 rv = aConnection.ExecuteSimpleSQL(
613 "CREATE TABLE object_store ("
614 "id INTEGER PRIMARY KEY, "
615 "auto_increment INTEGER NOT NULL DEFAULT 0, "
616 "name TEXT NOT NULL, "
617 "key_path TEXT, "
618 "UNIQUE (name)"
619 ");"_ns);
620 if (NS_WARN_IF(NS_FAILED(rv))) {
621 return rv;
624 rv = aConnection.ExecuteSimpleSQL(
625 "INSERT INTO object_store "
626 "SELECT id, auto_increment, name, nullif(key_path, '') "
627 "FROM temp_upgrade;"_ns);
628 if (NS_WARN_IF(NS_FAILED(rv))) {
629 return rv;
632 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
633 if (NS_WARN_IF(NS_FAILED(rv))) {
634 return rv;
637 rv = aConnection.SetSchemaVersion(7);
638 if (NS_WARN_IF(NS_FAILED(rv))) {
639 return rv;
642 return NS_OK;
645 nsresult UpgradeSchemaFrom7To8(mozIStorageConnection& aConnection) {
646 AssertIsOnIOThread();
648 AUTO_PROFILER_LABEL("UpgradeSchemaFrom7To8", DOM);
650 nsresult rv = aConnection.ExecuteSimpleSQL(
651 "CREATE TEMPORARY TABLE temp_upgrade ("
652 "id, "
653 "object_store_id, "
654 "name, "
655 "key_path, "
656 "unique_index, "
657 "object_store_autoincrement"
658 ");"_ns);
659 if (NS_WARN_IF(NS_FAILED(rv))) {
660 return rv;
663 rv = aConnection.ExecuteSimpleSQL(
664 "INSERT INTO temp_upgrade "
665 "SELECT id, object_store_id, name, key_path, "
666 "unique_index, object_store_autoincrement "
667 "FROM object_store_index;"_ns);
668 if (NS_WARN_IF(NS_FAILED(rv))) {
669 return rv;
672 rv = aConnection.ExecuteSimpleSQL("DROP TABLE object_store_index;"_ns);
673 if (NS_WARN_IF(NS_FAILED(rv))) {
674 return rv;
677 rv = aConnection.ExecuteSimpleSQL(
678 "CREATE TABLE object_store_index ("
679 "id INTEGER, "
680 "object_store_id INTEGER NOT NULL, "
681 "name TEXT NOT NULL, "
682 "key_path TEXT NOT NULL, "
683 "unique_index INTEGER NOT NULL, "
684 "multientry INTEGER NOT NULL, "
685 "object_store_autoincrement INTERGER NOT NULL, "
686 "PRIMARY KEY (id), "
687 "UNIQUE (object_store_id, name), "
688 "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
689 "CASCADE"
690 ");"_ns);
691 if (NS_WARN_IF(NS_FAILED(rv))) {
692 return rv;
695 rv = aConnection.ExecuteSimpleSQL(
696 "INSERT INTO object_store_index "
697 "SELECT id, object_store_id, name, key_path, "
698 "unique_index, 0, object_store_autoincrement "
699 "FROM temp_upgrade;"_ns);
700 if (NS_WARN_IF(NS_FAILED(rv))) {
701 return rv;
704 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
705 if (NS_WARN_IF(NS_FAILED(rv))) {
706 return rv;
709 rv = aConnection.SetSchemaVersion(8);
710 if (NS_WARN_IF(NS_FAILED(rv))) {
711 return rv;
714 return NS_OK;
717 class CompressDataBlobsFunction final : public mozIStorageFunction {
718 public:
719 NS_DECL_ISUPPORTS
721 private:
722 ~CompressDataBlobsFunction() = default;
724 NS_IMETHOD
725 OnFunctionCall(mozIStorageValueArray* aArguments,
726 nsIVariant** aResult) override {
727 MOZ_ASSERT(aArguments);
728 MOZ_ASSERT(aResult);
730 AUTO_PROFILER_LABEL("CompressDataBlobsFunction::OnFunctionCall", DOM);
732 uint32_t argc;
733 nsresult rv = aArguments->GetNumEntries(&argc);
734 if (NS_WARN_IF(NS_FAILED(rv))) {
735 return rv;
738 if (argc != 1) {
739 NS_WARNING("Don't call me with the wrong number of arguments!");
740 return NS_ERROR_UNEXPECTED;
743 int32_t type;
744 rv = aArguments->GetTypeOfIndex(0, &type);
745 if (NS_WARN_IF(NS_FAILED(rv))) {
746 return rv;
749 if (type != mozIStorageStatement::VALUE_TYPE_BLOB) {
750 NS_WARNING("Don't call me with the wrong type of arguments!");
751 return NS_ERROR_UNEXPECTED;
754 const uint8_t* uncompressed;
755 uint32_t uncompressedLength;
756 rv = aArguments->GetSharedBlob(0, &uncompressedLength, &uncompressed);
757 if (NS_WARN_IF(NS_FAILED(rv))) {
758 return rv;
761 size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
762 UniqueFreePtr<uint8_t> compressed(
763 static_cast<uint8_t*>(malloc(compressedLength)));
764 if (NS_WARN_IF(!compressed)) {
765 return NS_ERROR_OUT_OF_MEMORY;
768 snappy::RawCompress(
769 reinterpret_cast<const char*>(uncompressed), uncompressedLength,
770 reinterpret_cast<char*>(compressed.get()), &compressedLength);
772 std::pair<uint8_t*, int> data(compressed.release(), int(compressedLength));
774 nsCOMPtr<nsIVariant> result =
775 new mozilla::storage::AdoptedBlobVariant(data);
777 result.forget(aResult);
778 return NS_OK;
782 nsresult UpgradeSchemaFrom8To9_0(mozIStorageConnection& aConnection) {
783 AssertIsOnIOThread();
785 AUTO_PROFILER_LABEL("UpgradeSchemaFrom8To9_0", DOM);
787 // We no longer use the dataVersion column.
788 nsresult rv =
789 aConnection.ExecuteSimpleSQL("UPDATE database SET dataVersion = 0;"_ns);
790 if (NS_WARN_IF(NS_FAILED(rv))) {
791 return rv;
794 nsCOMPtr<mozIStorageFunction> compressor = new CompressDataBlobsFunction();
796 constexpr auto compressorName = "compress"_ns;
798 rv = aConnection.CreateFunction(compressorName, 1, compressor);
799 if (NS_WARN_IF(NS_FAILED(rv))) {
800 return rv;
803 // Turn off foreign key constraints before we do anything here.
804 rv = aConnection.ExecuteSimpleSQL(
805 "UPDATE object_data SET data = compress(data);"_ns);
806 if (NS_WARN_IF(NS_FAILED(rv))) {
807 return rv;
810 rv = aConnection.ExecuteSimpleSQL(
811 "UPDATE ai_object_data SET data = compress(data);"_ns);
812 if (NS_WARN_IF(NS_FAILED(rv))) {
813 return rv;
816 rv = aConnection.RemoveFunction(compressorName);
817 if (NS_WARN_IF(NS_FAILED(rv))) {
818 return rv;
821 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(9, 0));
822 if (NS_WARN_IF(NS_FAILED(rv))) {
823 return rv;
826 return NS_OK;
829 nsresult UpgradeSchemaFrom9_0To10_0(mozIStorageConnection& aConnection) {
830 AssertIsOnIOThread();
832 AUTO_PROFILER_LABEL("UpgradeSchemaFrom9_0To10_0", DOM);
834 nsresult rv = aConnection.ExecuteSimpleSQL(
835 "ALTER TABLE object_data ADD COLUMN file_ids TEXT;"_ns);
836 if (NS_WARN_IF(NS_FAILED(rv))) {
837 return rv;
840 rv = aConnection.ExecuteSimpleSQL(
841 "ALTER TABLE ai_object_data ADD COLUMN file_ids TEXT;"_ns);
842 if (NS_WARN_IF(NS_FAILED(rv))) {
843 return rv;
846 rv = CreateFileTables(aConnection);
847 if (NS_WARN_IF(NS_FAILED(rv))) {
848 return rv;
851 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(10, 0));
852 if (NS_WARN_IF(NS_FAILED(rv))) {
853 return rv;
856 return NS_OK;
859 nsresult UpgradeSchemaFrom10_0To11_0(mozIStorageConnection& aConnection) {
860 AssertIsOnIOThread();
862 AUTO_PROFILER_LABEL("UpgradeSchemaFrom10_0To11_0", DOM);
864 nsresult rv = aConnection.ExecuteSimpleSQL(
865 "CREATE TEMPORARY TABLE temp_upgrade ("
866 "id, "
867 "object_store_id, "
868 "name, "
869 "key_path, "
870 "unique_index, "
871 "multientry"
872 ");"_ns);
873 if (NS_WARN_IF(NS_FAILED(rv))) {
874 return rv;
877 rv = aConnection.ExecuteSimpleSQL(
878 "INSERT INTO temp_upgrade "
879 "SELECT id, object_store_id, name, key_path, "
880 "unique_index, multientry "
881 "FROM object_store_index;"_ns);
882 if (NS_WARN_IF(NS_FAILED(rv))) {
883 return rv;
886 rv = aConnection.ExecuteSimpleSQL("DROP TABLE object_store_index;"_ns);
887 if (NS_WARN_IF(NS_FAILED(rv))) {
888 return rv;
891 rv = aConnection.ExecuteSimpleSQL(
892 "CREATE TABLE object_store_index ("
893 "id INTEGER PRIMARY KEY, "
894 "object_store_id INTEGER NOT NULL, "
895 "name TEXT NOT NULL, "
896 "key_path TEXT NOT NULL, "
897 "unique_index INTEGER NOT NULL, "
898 "multientry INTEGER NOT NULL, "
899 "UNIQUE (object_store_id, name), "
900 "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
901 "CASCADE"
902 ");"_ns);
903 if (NS_WARN_IF(NS_FAILED(rv))) {
904 return rv;
907 rv = aConnection.ExecuteSimpleSQL(
908 "INSERT INTO object_store_index "
909 "SELECT id, object_store_id, name, key_path, "
910 "unique_index, multientry "
911 "FROM temp_upgrade;"_ns);
912 if (NS_WARN_IF(NS_FAILED(rv))) {
913 return rv;
916 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
917 if (NS_WARN_IF(NS_FAILED(rv))) {
918 return rv;
921 rv = aConnection.ExecuteSimpleSQL(
922 "DROP TRIGGER object_data_insert_trigger;"_ns);
923 if (NS_WARN_IF(NS_FAILED(rv))) {
924 return rv;
927 rv = aConnection.ExecuteSimpleSQL(
928 "INSERT INTO object_data (object_store_id, key_value, data, file_ids) "
929 "SELECT object_store_id, id, data, file_ids "
930 "FROM ai_object_data;"_ns);
931 if (NS_WARN_IF(NS_FAILED(rv))) {
932 return rv;
935 rv = aConnection.ExecuteSimpleSQL(
936 "CREATE TRIGGER object_data_insert_trigger "
937 "AFTER INSERT ON object_data "
938 "FOR EACH ROW "
939 "WHEN NEW.file_ids IS NOT NULL "
940 "BEGIN "
941 "SELECT update_refcount(NULL, NEW.file_ids); "
942 "END;"_ns);
943 if (NS_WARN_IF(NS_FAILED(rv))) {
944 return rv;
947 rv = aConnection.ExecuteSimpleSQL(
948 "INSERT INTO index_data (index_id, value, object_data_key, "
949 "object_data_id) "
950 "SELECT ai_index_data.index_id, ai_index_data.value, "
951 "ai_index_data.ai_object_data_id, object_data.id "
952 "FROM ai_index_data "
953 "INNER JOIN object_store_index ON "
954 "object_store_index.id = ai_index_data.index_id "
955 "INNER JOIN object_data ON "
956 "object_data.object_store_id = object_store_index.object_store_id AND "
957 "object_data.key_value = ai_index_data.ai_object_data_id;"_ns);
958 if (NS_WARN_IF(NS_FAILED(rv))) {
959 return rv;
962 rv = aConnection.ExecuteSimpleSQL(
963 "INSERT INTO unique_index_data (index_id, value, object_data_key, "
964 "object_data_id) "
965 "SELECT ai_unique_index_data.index_id, ai_unique_index_data.value, "
966 "ai_unique_index_data.ai_object_data_id, object_data.id "
967 "FROM ai_unique_index_data "
968 "INNER JOIN object_store_index ON "
969 "object_store_index.id = ai_unique_index_data.index_id "
970 "INNER JOIN object_data ON "
971 "object_data.object_store_id = object_store_index.object_store_id AND "
972 "object_data.key_value = ai_unique_index_data.ai_object_data_id;"_ns);
973 if (NS_WARN_IF(NS_FAILED(rv))) {
974 return rv;
977 rv = aConnection.ExecuteSimpleSQL(
978 "UPDATE object_store "
979 "SET auto_increment = (SELECT max(id) FROM ai_object_data) + 1 "
980 "WHERE auto_increment;"_ns);
981 if (NS_WARN_IF(NS_FAILED(rv))) {
982 return rv;
985 rv = aConnection.ExecuteSimpleSQL("DROP TABLE ai_unique_index_data;"_ns);
986 if (NS_WARN_IF(NS_FAILED(rv))) {
987 return rv;
990 rv = aConnection.ExecuteSimpleSQL("DROP TABLE ai_index_data;"_ns);
991 if (NS_WARN_IF(NS_FAILED(rv))) {
992 return rv;
995 rv = aConnection.ExecuteSimpleSQL("DROP TABLE ai_object_data;"_ns);
996 if (NS_WARN_IF(NS_FAILED(rv))) {
997 return rv;
1000 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(11, 0));
1001 if (NS_WARN_IF(NS_FAILED(rv))) {
1002 return rv;
1005 return NS_OK;
1008 class EncodeKeysFunction final : public mozIStorageFunction {
1009 public:
1010 NS_DECL_ISUPPORTS
1012 private:
1013 ~EncodeKeysFunction() = default;
1015 NS_IMETHOD
1016 OnFunctionCall(mozIStorageValueArray* aArguments,
1017 nsIVariant** aResult) override {
1018 MOZ_ASSERT(aArguments);
1019 MOZ_ASSERT(aResult);
1021 AUTO_PROFILER_LABEL("EncodeKeysFunction::OnFunctionCall", DOM);
1023 uint32_t argc;
1024 nsresult rv = aArguments->GetNumEntries(&argc);
1025 if (NS_WARN_IF(NS_FAILED(rv))) {
1026 return rv;
1029 if (argc != 1) {
1030 NS_WARNING("Don't call me with the wrong number of arguments!");
1031 return NS_ERROR_UNEXPECTED;
1034 int32_t type;
1035 rv = aArguments->GetTypeOfIndex(0, &type);
1036 if (NS_WARN_IF(NS_FAILED(rv))) {
1037 return rv;
1040 QM_TRY_INSPECT(
1041 const auto& key, ([type, aArguments]() -> Result<Key, nsresult> {
1042 switch (type) {
1043 case mozIStorageStatement::VALUE_TYPE_INTEGER: {
1044 int64_t intKey;
1045 aArguments->GetInt64(0, &intKey);
1047 Key key;
1048 QM_TRY(key.SetFromInteger(intKey));
1050 return key;
1052 case mozIStorageStatement::VALUE_TYPE_TEXT: {
1053 nsString stringKey;
1054 aArguments->GetString(0, stringKey);
1056 Key key;
1057 QM_TRY(key.SetFromString(stringKey));
1059 return key;
1061 default:
1062 NS_WARNING("Don't call me with the wrong type of arguments!");
1063 return Err(NS_ERROR_UNEXPECTED);
1065 }()));
1067 const nsCString& buffer = key.GetBuffer();
1069 std::pair<const void*, int> data(static_cast<const void*>(buffer.get()),
1070 int(buffer.Length()));
1072 nsCOMPtr<nsIVariant> result = new mozilla::storage::BlobVariant(data);
1074 result.forget(aResult);
1075 return NS_OK;
1079 nsresult UpgradeSchemaFrom11_0To12_0(mozIStorageConnection& aConnection) {
1080 AssertIsOnIOThread();
1082 AUTO_PROFILER_LABEL("UpgradeSchemaFrom11_0To12_0", DOM);
1084 constexpr auto encoderName = "encode"_ns;
1086 nsCOMPtr<mozIStorageFunction> encoder = new EncodeKeysFunction();
1088 nsresult rv = aConnection.CreateFunction(encoderName, 1, encoder);
1089 if (NS_WARN_IF(NS_FAILED(rv))) {
1090 return rv;
1093 rv = aConnection.ExecuteSimpleSQL(
1094 "CREATE TEMPORARY TABLE temp_upgrade ("
1095 "id INTEGER PRIMARY KEY, "
1096 "object_store_id, "
1097 "key_value, "
1098 "data, "
1099 "file_ids "
1100 ");"_ns);
1101 if (NS_WARN_IF(NS_FAILED(rv))) {
1102 return rv;
1105 rv = aConnection.ExecuteSimpleSQL(
1106 "INSERT INTO temp_upgrade "
1107 "SELECT id, object_store_id, encode(key_value), data, file_ids "
1108 "FROM object_data;"_ns);
1109 if (NS_WARN_IF(NS_FAILED(rv))) {
1110 return rv;
1113 rv = aConnection.ExecuteSimpleSQL("DROP TABLE object_data;"_ns);
1114 if (NS_WARN_IF(NS_FAILED(rv))) {
1115 return rv;
1118 rv = aConnection.ExecuteSimpleSQL(
1119 "CREATE TABLE object_data ("
1120 "id INTEGER PRIMARY KEY, "
1121 "object_store_id INTEGER NOT NULL, "
1122 "key_value BLOB DEFAULT NULL, "
1123 "file_ids TEXT, "
1124 "data BLOB NOT NULL, "
1125 "UNIQUE (object_store_id, key_value), "
1126 "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
1127 "CASCADE"
1128 ");"_ns);
1129 if (NS_WARN_IF(NS_FAILED(rv))) {
1130 return rv;
1133 rv = aConnection.ExecuteSimpleSQL(
1134 "INSERT INTO object_data "
1135 "SELECT id, object_store_id, key_value, file_ids, data "
1136 "FROM temp_upgrade;"_ns);
1137 if (NS_WARN_IF(NS_FAILED(rv))) {
1138 return rv;
1141 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
1142 if (NS_WARN_IF(NS_FAILED(rv))) {
1143 return rv;
1146 rv = aConnection.ExecuteSimpleSQL(
1147 "CREATE TRIGGER object_data_insert_trigger "
1148 "AFTER INSERT ON object_data "
1149 "FOR EACH ROW "
1150 "WHEN NEW.file_ids IS NOT NULL "
1151 "BEGIN "
1152 "SELECT update_refcount(NULL, NEW.file_ids); "
1153 "END;"_ns);
1154 if (NS_WARN_IF(NS_FAILED(rv))) {
1155 return rv;
1158 rv = aConnection.ExecuteSimpleSQL(
1159 "CREATE TRIGGER object_data_update_trigger "
1160 "AFTER UPDATE OF file_ids ON object_data "
1161 "FOR EACH ROW "
1162 "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
1163 "BEGIN "
1164 "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
1165 "END;"_ns);
1166 if (NS_WARN_IF(NS_FAILED(rv))) {
1167 return rv;
1170 rv = aConnection.ExecuteSimpleSQL(
1171 "CREATE TRIGGER object_data_delete_trigger "
1172 "AFTER DELETE ON object_data "
1173 "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL "
1174 "BEGIN "
1175 "SELECT update_refcount(OLD.file_ids, NULL); "
1176 "END;"_ns);
1177 if (NS_WARN_IF(NS_FAILED(rv))) {
1178 return rv;
1181 rv = aConnection.ExecuteSimpleSQL(
1182 "CREATE TEMPORARY TABLE temp_upgrade ("
1183 "index_id, "
1184 "value, "
1185 "object_data_key, "
1186 "object_data_id "
1187 ");"_ns);
1188 if (NS_WARN_IF(NS_FAILED(rv))) {
1189 return rv;
1192 rv = aConnection.ExecuteSimpleSQL(
1193 "INSERT INTO temp_upgrade "
1194 "SELECT index_id, encode(value), encode(object_data_key), object_data_id "
1195 "FROM index_data;"_ns);
1196 if (NS_WARN_IF(NS_FAILED(rv))) {
1197 return rv;
1200 rv = aConnection.ExecuteSimpleSQL("DROP TABLE index_data;"_ns);
1201 if (NS_WARN_IF(NS_FAILED(rv))) {
1202 return rv;
1205 rv = aConnection.ExecuteSimpleSQL(
1206 "CREATE TABLE index_data ("
1207 "index_id INTEGER NOT NULL, "
1208 "value BLOB NOT NULL, "
1209 "object_data_key BLOB NOT NULL, "
1210 "object_data_id INTEGER NOT NULL, "
1211 "PRIMARY KEY (index_id, value, object_data_key), "
1212 "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
1213 "CASCADE, "
1214 "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
1215 "CASCADE"
1216 ");"_ns);
1217 if (NS_WARN_IF(NS_FAILED(rv))) {
1218 return rv;
1221 rv = aConnection.ExecuteSimpleSQL(
1222 "INSERT INTO index_data "
1223 "SELECT index_id, value, object_data_key, object_data_id "
1224 "FROM temp_upgrade;"_ns);
1225 if (NS_WARN_IF(NS_FAILED(rv))) {
1226 return rv;
1229 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
1230 if (NS_WARN_IF(NS_FAILED(rv))) {
1231 return rv;
1234 rv = aConnection.ExecuteSimpleSQL(
1235 "CREATE INDEX index_data_object_data_id_index "
1236 "ON index_data (object_data_id);"_ns);
1237 if (NS_WARN_IF(NS_FAILED(rv))) {
1238 return rv;
1241 rv = aConnection.ExecuteSimpleSQL(
1242 "CREATE TEMPORARY TABLE temp_upgrade ("
1243 "index_id, "
1244 "value, "
1245 "object_data_key, "
1246 "object_data_id "
1247 ");"_ns);
1248 if (NS_WARN_IF(NS_FAILED(rv))) {
1249 return rv;
1252 rv = aConnection.ExecuteSimpleSQL(
1253 "INSERT INTO temp_upgrade "
1254 "SELECT index_id, encode(value), encode(object_data_key), object_data_id "
1255 "FROM unique_index_data;"_ns);
1256 if (NS_WARN_IF(NS_FAILED(rv))) {
1257 return rv;
1260 rv = aConnection.ExecuteSimpleSQL("DROP TABLE unique_index_data;"_ns);
1261 if (NS_WARN_IF(NS_FAILED(rv))) {
1262 return rv;
1265 rv = aConnection.ExecuteSimpleSQL(
1266 "CREATE TABLE unique_index_data ("
1267 "index_id INTEGER NOT NULL, "
1268 "value BLOB NOT NULL, "
1269 "object_data_key BLOB NOT NULL, "
1270 "object_data_id INTEGER NOT NULL, "
1271 "PRIMARY KEY (index_id, value, object_data_key), "
1272 "UNIQUE (index_id, value), "
1273 "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
1274 "CASCADE "
1275 "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
1276 "CASCADE"
1277 ");"_ns);
1278 if (NS_WARN_IF(NS_FAILED(rv))) {
1279 return rv;
1282 rv = aConnection.ExecuteSimpleSQL(
1283 "INSERT INTO unique_index_data "
1284 "SELECT index_id, value, object_data_key, object_data_id "
1285 "FROM temp_upgrade;"_ns);
1286 if (NS_WARN_IF(NS_FAILED(rv))) {
1287 return rv;
1290 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns);
1291 if (NS_WARN_IF(NS_FAILED(rv))) {
1292 return rv;
1295 rv = aConnection.ExecuteSimpleSQL(
1296 "CREATE INDEX unique_index_data_object_data_id_index "
1297 "ON unique_index_data (object_data_id);"_ns);
1298 if (NS_WARN_IF(NS_FAILED(rv))) {
1299 return rv;
1302 rv = aConnection.RemoveFunction(encoderName);
1303 if (NS_WARN_IF(NS_FAILED(rv))) {
1304 return rv;
1307 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(12, 0));
1308 if (NS_WARN_IF(NS_FAILED(rv))) {
1309 return rv;
1312 return NS_OK;
1315 nsresult UpgradeSchemaFrom12_0To13_0(mozIStorageConnection& aConnection,
1316 bool* aVacuumNeeded) {
1317 AssertIsOnIOThread();
1319 AUTO_PROFILER_LABEL("UpgradeSchemaFrom12_0To13_0", DOM);
1321 nsresult rv;
1323 #ifdef IDB_MOBILE
1324 int32_t defaultPageSize;
1325 rv = aConnection.GetDefaultPageSize(&defaultPageSize);
1326 if (NS_WARN_IF(NS_FAILED(rv))) {
1327 return rv;
1330 // Enable auto_vacuum mode and update the page size to the platform default.
1331 nsAutoCString upgradeQuery("PRAGMA auto_vacuum = FULL; PRAGMA page_size = ");
1332 upgradeQuery.AppendInt(defaultPageSize);
1334 rv = aConnection.ExecuteSimpleSQL(upgradeQuery);
1335 if (NS_WARN_IF(NS_FAILED(rv))) {
1336 return rv;
1339 *aVacuumNeeded = true;
1340 #endif
1342 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(13, 0));
1343 if (NS_WARN_IF(NS_FAILED(rv))) {
1344 return rv;
1347 return NS_OK;
1350 nsresult UpgradeSchemaFrom13_0To14_0(mozIStorageConnection& aConnection) {
1351 AssertIsOnIOThread();
1353 // The only change between 13 and 14 was a different structured
1354 // clone format, but it's backwards-compatible.
1355 nsresult rv = aConnection.SetSchemaVersion(MakeSchemaVersion(14, 0));
1356 if (NS_WARN_IF(NS_FAILED(rv))) {
1357 return rv;
1360 return NS_OK;
1363 nsresult UpgradeSchemaFrom14_0To15_0(mozIStorageConnection& aConnection) {
1364 // The only change between 14 and 15 was a different structured
1365 // clone format, but it's backwards-compatible.
1366 nsresult rv = aConnection.SetSchemaVersion(MakeSchemaVersion(15, 0));
1367 if (NS_WARN_IF(NS_FAILED(rv))) {
1368 return rv;
1371 return NS_OK;
1374 nsresult UpgradeSchemaFrom15_0To16_0(mozIStorageConnection& aConnection) {
1375 // The only change between 15 and 16 was a different structured
1376 // clone format, but it's backwards-compatible.
1377 nsresult rv = aConnection.SetSchemaVersion(MakeSchemaVersion(16, 0));
1378 if (NS_WARN_IF(NS_FAILED(rv))) {
1379 return rv;
1382 return NS_OK;
1385 nsresult UpgradeSchemaFrom16_0To17_0(mozIStorageConnection& aConnection) {
1386 // The only change between 16 and 17 was a different structured
1387 // clone format, but it's backwards-compatible.
1388 nsresult rv = aConnection.SetSchemaVersion(MakeSchemaVersion(17, 0));
1389 if (NS_WARN_IF(NS_FAILED(rv))) {
1390 return rv;
1393 return NS_OK;
1396 class UpgradeSchemaFrom17_0To18_0Helper final {
1397 class InsertIndexDataValuesFunction;
1398 class UpgradeKeyFunction;
1400 public:
1401 static nsresult DoUpgrade(mozIStorageConnection& aConnection,
1402 const nsACString& aOrigin);
1404 private:
1405 static nsresult DoUpgradeInternal(mozIStorageConnection& aConnection,
1406 const nsACString& aOrigin);
1408 UpgradeSchemaFrom17_0To18_0Helper() = delete;
1409 ~UpgradeSchemaFrom17_0To18_0Helper() = delete;
1412 class UpgradeSchemaFrom17_0To18_0Helper::InsertIndexDataValuesFunction final
1413 : public mozIStorageFunction {
1414 public:
1415 InsertIndexDataValuesFunction() = default;
1417 NS_DECL_ISUPPORTS
1419 private:
1420 ~InsertIndexDataValuesFunction() = default;
1422 NS_DECL_MOZISTORAGEFUNCTION
1425 NS_IMPL_ISUPPORTS(
1426 UpgradeSchemaFrom17_0To18_0Helper::InsertIndexDataValuesFunction,
1427 mozIStorageFunction);
1429 NS_IMETHODIMP
1430 UpgradeSchemaFrom17_0To18_0Helper::InsertIndexDataValuesFunction::
1431 OnFunctionCall(mozIStorageValueArray* aValues, nsIVariant** _retval) {
1432 MOZ_ASSERT(!NS_IsMainThread());
1433 MOZ_ASSERT(!IsOnBackgroundThread());
1434 MOZ_ASSERT(aValues);
1435 MOZ_ASSERT(_retval);
1437 #ifdef DEBUG
1439 uint32_t argCount;
1440 MOZ_ALWAYS_SUCCEEDS(aValues->GetNumEntries(&argCount));
1441 MOZ_ASSERT(argCount == 4);
1443 int32_t valueType;
1444 MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(0, &valueType));
1445 MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_NULL ||
1446 valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
1448 MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(1, &valueType));
1449 MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_INTEGER);
1451 MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(2, &valueType));
1452 MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_INTEGER);
1454 MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(3, &valueType));
1455 MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
1457 #endif
1459 // Read out the previous value. It may be NULL, in which case we'll just end
1460 // up with an empty array.
1461 QM_TRY_UNWRAP(auto indexValues, ReadCompressedIndexDataValues(*aValues, 0));
1463 IndexOrObjectStoreId indexId;
1464 nsresult rv = aValues->GetInt64(1, &indexId);
1465 if (NS_WARN_IF(NS_FAILED(rv))) {
1466 return rv;
1469 int32_t unique;
1470 rv = aValues->GetInt32(2, &unique);
1471 if (NS_WARN_IF(NS_FAILED(rv))) {
1472 return rv;
1475 Key value;
1476 rv = value.SetFromValueArray(aValues, 3);
1477 if (NS_WARN_IF(NS_FAILED(rv))) {
1478 return rv;
1481 // Update the array with the new addition.
1482 if (NS_WARN_IF(!indexValues.InsertElementSorted(
1483 IndexDataValue(indexId, !!unique, value), fallible))) {
1484 IDB_REPORT_INTERNAL_ERR();
1485 return NS_ERROR_OUT_OF_MEMORY;
1488 // Compress the array.
1489 QM_TRY_UNWRAP((auto [indexValuesBlob, indexValuesBlobLength]),
1490 MakeCompressedIndexDataValues(indexValues));
1492 // The compressed blob is the result of this function.
1493 nsCOMPtr<nsIVariant> result = new storage::AdoptedBlobVariant(
1494 std::pair(indexValuesBlob.release(), indexValuesBlobLength));
1496 result.forget(_retval);
1497 return NS_OK;
1500 class UpgradeSchemaFrom17_0To18_0Helper::UpgradeKeyFunction final
1501 : public mozIStorageFunction {
1502 public:
1503 UpgradeKeyFunction() = default;
1505 static nsresult CopyAndUpgradeKeyBuffer(const uint8_t* aSource,
1506 const uint8_t* aSourceEnd,
1507 uint8_t* aDestination) {
1508 return CopyAndUpgradeKeyBufferInternal(aSource, aSourceEnd, aDestination,
1509 0 /* aTagOffset */,
1510 0 /* aRecursionDepth */);
1513 NS_DECL_ISUPPORTS
1515 private:
1516 ~UpgradeKeyFunction() = default;
1518 static nsresult CopyAndUpgradeKeyBufferInternal(const uint8_t*& aSource,
1519 const uint8_t* aSourceEnd,
1520 uint8_t*& aDestination,
1521 uint8_t aTagOffset,
1522 uint8_t aRecursionDepth);
1524 static uint32_t AdjustedSize(uint32_t aMaxSize, const uint8_t* aSource,
1525 const uint8_t* aSourceEnd) {
1526 MOZ_ASSERT(aMaxSize);
1527 MOZ_ASSERT(aSource);
1528 MOZ_ASSERT(aSourceEnd);
1529 MOZ_ASSERT(aSource <= aSourceEnd);
1531 return std::min(aMaxSize, uint32_t(aSourceEnd - aSource));
1534 NS_DECL_MOZISTORAGEFUNCTION
1537 // static
1538 nsresult UpgradeSchemaFrom17_0To18_0Helper::UpgradeKeyFunction::
1539 CopyAndUpgradeKeyBufferInternal(const uint8_t*& aSource,
1540 const uint8_t* aSourceEnd,
1541 uint8_t*& aDestination, uint8_t aTagOffset,
1542 uint8_t aRecursionDepth) {
1543 MOZ_ASSERT(!NS_IsMainThread());
1544 MOZ_ASSERT(!IsOnBackgroundThread());
1545 MOZ_ASSERT(aSource);
1546 MOZ_ASSERT(*aSource);
1547 MOZ_ASSERT(aSourceEnd);
1548 MOZ_ASSERT(aSource < aSourceEnd);
1549 MOZ_ASSERT(aDestination);
1550 MOZ_ASSERT(aTagOffset <= Key::kMaxArrayCollapse);
1552 static constexpr uint8_t kOldNumberTag = 0x1;
1553 static constexpr uint8_t kOldDateTag = 0x2;
1554 static constexpr uint8_t kOldStringTag = 0x3;
1555 static constexpr uint8_t kOldArrayTag = 0x4;
1556 static constexpr uint8_t kOldMaxType = kOldArrayTag;
1558 if (NS_WARN_IF(aRecursionDepth > Key::kMaxRecursionDepth)) {
1559 IDB_REPORT_INTERNAL_ERR();
1560 return NS_ERROR_FILE_CORRUPTED;
1563 const uint8_t sourceTag = *aSource - (aTagOffset * kOldMaxType);
1564 MOZ_ASSERT(sourceTag);
1566 if (NS_WARN_IF(sourceTag > kOldMaxType * Key::kMaxArrayCollapse)) {
1567 IDB_REPORT_INTERNAL_ERR();
1568 return NS_ERROR_FILE_CORRUPTED;
1571 if (sourceTag == kOldNumberTag || sourceTag == kOldDateTag) {
1572 // Write the new tag.
1573 *aDestination++ = (sourceTag == kOldNumberTag ? Key::eFloat : Key::eDate) +
1574 (aTagOffset * Key::eMaxType);
1575 aSource++;
1577 // Numbers and Dates are encoded as 64-bit integers, but trailing 0
1578 // bytes have been removed.
1579 const uint32_t byteCount =
1580 AdjustedSize(sizeof(uint64_t), aSource, aSourceEnd);
1582 aDestination = std::copy(aSource, aSource + byteCount, aDestination);
1583 aSource += byteCount;
1585 return NS_OK;
1588 if (sourceTag == kOldStringTag) {
1589 // Write the new tag.
1590 *aDestination++ = Key::eString + (aTagOffset * Key::eMaxType);
1591 aSource++;
1593 while (aSource < aSourceEnd) {
1594 const uint8_t byte = *aSource++;
1595 *aDestination++ = byte;
1597 if (!byte) {
1598 // Just copied the terminator.
1599 break;
1602 // Maybe copy one or two extra bytes if the byte is tagged and we have
1603 // enough source space.
1604 if (byte & 0x80) {
1605 const uint32_t byteCount =
1606 AdjustedSize((byte & 0x40) ? 2 : 1, aSource, aSourceEnd);
1608 aDestination = std::copy(aSource, aSource + byteCount, aDestination);
1609 aSource += byteCount;
1613 return NS_OK;
1616 if (NS_WARN_IF(sourceTag < kOldArrayTag)) {
1617 IDB_REPORT_INTERNAL_ERR();
1618 return NS_ERROR_FILE_CORRUPTED;
1621 aTagOffset++;
1623 if (aTagOffset == Key::kMaxArrayCollapse) {
1624 MOZ_ASSERT(sourceTag == kOldArrayTag);
1626 *aDestination++ = (aTagOffset * Key::eMaxType);
1627 aSource++;
1629 aTagOffset = 0;
1632 while (aSource < aSourceEnd &&
1633 (*aSource - (aTagOffset * kOldMaxType)) != Key::eTerminator) {
1634 nsresult rv = CopyAndUpgradeKeyBufferInternal(
1635 aSource, aSourceEnd, aDestination, aTagOffset, aRecursionDepth + 1);
1636 if (NS_WARN_IF(NS_FAILED(rv))) {
1637 return rv;
1640 aTagOffset = 0;
1643 if (aSource < aSourceEnd) {
1644 MOZ_ASSERT((*aSource - (aTagOffset * kOldMaxType)) == Key::eTerminator);
1645 *aDestination++ = Key::eTerminator + (aTagOffset * Key::eMaxType);
1646 aSource++;
1649 return NS_OK;
1652 NS_IMPL_ISUPPORTS(UpgradeSchemaFrom17_0To18_0Helper::UpgradeKeyFunction,
1653 mozIStorageFunction);
1655 NS_IMETHODIMP
1656 UpgradeSchemaFrom17_0To18_0Helper::UpgradeKeyFunction::OnFunctionCall(
1657 mozIStorageValueArray* aValues, nsIVariant** _retval) {
1658 MOZ_ASSERT(!NS_IsMainThread());
1659 MOZ_ASSERT(!IsOnBackgroundThread());
1660 MOZ_ASSERT(aValues);
1661 MOZ_ASSERT(_retval);
1663 #ifdef DEBUG
1665 uint32_t argCount;
1666 MOZ_ALWAYS_SUCCEEDS(aValues->GetNumEntries(&argCount));
1667 MOZ_ASSERT(argCount == 1);
1669 int32_t valueType;
1670 MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(0, &valueType));
1671 MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
1673 #endif
1675 // Dig the old key out of the values.
1676 const uint8_t* blobData;
1677 uint32_t blobDataLength;
1678 nsresult rv = aValues->GetSharedBlob(0, &blobDataLength, &blobData);
1679 if (NS_WARN_IF(NS_FAILED(rv))) {
1680 return rv;
1683 // Upgrading the key doesn't change the amount of space needed to hold it.
1684 UniqueFreePtr<uint8_t> upgradedBlobData(
1685 static_cast<uint8_t*>(malloc(blobDataLength)));
1686 if (NS_WARN_IF(!upgradedBlobData)) {
1687 IDB_REPORT_INTERNAL_ERR();
1688 return NS_ERROR_OUT_OF_MEMORY;
1691 rv = CopyAndUpgradeKeyBuffer(blobData, blobData + blobDataLength,
1692 upgradedBlobData.get());
1693 if (NS_WARN_IF(NS_FAILED(rv))) {
1694 return rv;
1697 // The upgraded key is the result of this function.
1698 std::pair<uint8_t*, int> data(upgradedBlobData.release(),
1699 int(blobDataLength));
1701 nsCOMPtr<nsIVariant> result = new mozilla::storage::AdoptedBlobVariant(data);
1703 result.forget(_retval);
1704 return NS_OK;
1707 // static
1708 nsresult UpgradeSchemaFrom17_0To18_0Helper::DoUpgrade(
1709 mozIStorageConnection& aConnection, const nsACString& aOrigin) {
1710 MOZ_ASSERT(!aOrigin.IsEmpty());
1712 // Register the |upgrade_key| function.
1713 RefPtr<UpgradeKeyFunction> updateFunction = new UpgradeKeyFunction();
1715 constexpr auto upgradeKeyFunctionName = "upgrade_key"_ns;
1717 nsresult rv =
1718 aConnection.CreateFunction(upgradeKeyFunctionName, 1, updateFunction);
1719 if (NS_WARN_IF(NS_FAILED(rv))) {
1720 return rv;
1723 // Register the |insert_idv| function.
1724 RefPtr<InsertIndexDataValuesFunction> insertIDVFunction =
1725 new InsertIndexDataValuesFunction();
1727 constexpr auto insertIDVFunctionName = "insert_idv"_ns;
1729 rv = aConnection.CreateFunction(insertIDVFunctionName, 4, insertIDVFunction);
1730 if (NS_WARN_IF(NS_FAILED(rv))) {
1731 MOZ_ALWAYS_SUCCEEDS(aConnection.RemoveFunction(upgradeKeyFunctionName));
1732 return rv;
1735 rv = DoUpgradeInternal(aConnection, aOrigin);
1737 MOZ_ALWAYS_SUCCEEDS(aConnection.RemoveFunction(upgradeKeyFunctionName));
1738 MOZ_ALWAYS_SUCCEEDS(aConnection.RemoveFunction(insertIDVFunctionName));
1740 if (NS_WARN_IF(NS_FAILED(rv))) {
1741 return rv;
1744 return NS_OK;
1747 // static
1748 nsresult UpgradeSchemaFrom17_0To18_0Helper::DoUpgradeInternal(
1749 mozIStorageConnection& aConnection, const nsACString& aOrigin) {
1750 MOZ_ASSERT(!aOrigin.IsEmpty());
1752 nsresult rv = aConnection.ExecuteSimpleSQL(
1753 // Drop these triggers to avoid unnecessary work during the upgrade
1754 // process.
1755 "DROP TRIGGER object_data_insert_trigger;"
1756 "DROP TRIGGER object_data_update_trigger;"
1757 "DROP TRIGGER object_data_delete_trigger;"_ns);
1758 if (NS_WARN_IF(NS_FAILED(rv))) {
1759 return rv;
1762 rv = aConnection.ExecuteSimpleSQL(
1763 // Drop these indexes before we do anything else to free disk space.
1764 "DROP INDEX index_data_object_data_id_index;"
1765 "DROP INDEX unique_index_data_object_data_id_index;"_ns);
1766 if (NS_WARN_IF(NS_FAILED(rv))) {
1767 return rv;
1770 // Create the new tables and triggers first.
1772 rv = aConnection.ExecuteSimpleSQL(
1773 // This will eventually become the |database| table.
1774 "CREATE TABLE database_upgrade "
1775 "( name TEXT PRIMARY KEY"
1776 ", origin TEXT NOT NULL"
1777 ", version INTEGER NOT NULL DEFAULT 0"
1778 ", last_vacuum_time INTEGER NOT NULL DEFAULT 0"
1779 ", last_analyze_time INTEGER NOT NULL DEFAULT 0"
1780 ", last_vacuum_size INTEGER NOT NULL DEFAULT 0"
1781 ") WITHOUT ROWID;"_ns);
1782 if (NS_WARN_IF(NS_FAILED(rv))) {
1783 return rv;
1786 rv = aConnection.ExecuteSimpleSQL(
1787 // This will eventually become the |object_store| table.
1788 "CREATE TABLE object_store_upgrade"
1789 "( id INTEGER PRIMARY KEY"
1790 ", auto_increment INTEGER NOT NULL DEFAULT 0"
1791 ", name TEXT NOT NULL"
1792 ", key_path TEXT"
1793 ");"_ns);
1794 if (NS_WARN_IF(NS_FAILED(rv))) {
1795 return rv;
1798 rv = aConnection.ExecuteSimpleSQL(
1799 // This will eventually become the |object_store_index| table.
1800 "CREATE TABLE object_store_index_upgrade"
1801 "( id INTEGER PRIMARY KEY"
1802 ", object_store_id INTEGER NOT NULL"
1803 ", name TEXT NOT NULL"
1804 ", key_path TEXT NOT NULL"
1805 ", unique_index INTEGER NOT NULL"
1806 ", multientry INTEGER NOT NULL"
1807 ", FOREIGN KEY (object_store_id) "
1808 "REFERENCES object_store(id) "
1809 ");"_ns);
1810 if (NS_WARN_IF(NS_FAILED(rv))) {
1811 return rv;
1814 rv = aConnection.ExecuteSimpleSQL(
1815 // This will eventually become the |object_data| table.
1816 "CREATE TABLE object_data_upgrade"
1817 "( object_store_id INTEGER NOT NULL"
1818 ", key BLOB NOT NULL"
1819 ", index_data_values BLOB DEFAULT NULL"
1820 ", file_ids TEXT"
1821 ", data BLOB NOT NULL"
1822 ", PRIMARY KEY (object_store_id, key)"
1823 ", FOREIGN KEY (object_store_id) "
1824 "REFERENCES object_store(id) "
1825 ") WITHOUT ROWID;"_ns);
1826 if (NS_WARN_IF(NS_FAILED(rv))) {
1827 return rv;
1830 rv = aConnection.ExecuteSimpleSQL(
1831 // This will eventually become the |index_data| table.
1832 "CREATE TABLE index_data_upgrade"
1833 "( index_id INTEGER NOT NULL"
1834 ", value BLOB NOT NULL"
1835 ", object_data_key BLOB NOT NULL"
1836 ", object_store_id INTEGER NOT NULL"
1837 ", PRIMARY KEY (index_id, value, object_data_key)"
1838 ", FOREIGN KEY (index_id) "
1839 "REFERENCES object_store_index(id) "
1840 ", FOREIGN KEY (object_store_id, object_data_key) "
1841 "REFERENCES object_data(object_store_id, key) "
1842 ") WITHOUT ROWID;"_ns);
1843 if (NS_WARN_IF(NS_FAILED(rv))) {
1844 return rv;
1847 rv = aConnection.ExecuteSimpleSQL(
1848 // This will eventually become the |unique_index_data| table.
1849 "CREATE TABLE unique_index_data_upgrade"
1850 "( index_id INTEGER NOT NULL"
1851 ", value BLOB NOT NULL"
1852 ", object_store_id INTEGER NOT NULL"
1853 ", object_data_key BLOB NOT NULL"
1854 ", PRIMARY KEY (index_id, value)"
1855 ", FOREIGN KEY (index_id) "
1856 "REFERENCES object_store_index(id) "
1857 ", FOREIGN KEY (object_store_id, object_data_key) "
1858 "REFERENCES object_data(object_store_id, key) "
1859 ") WITHOUT ROWID;"_ns);
1860 if (NS_WARN_IF(NS_FAILED(rv))) {
1861 return rv;
1864 rv = aConnection.ExecuteSimpleSQL(
1865 // Temporarily store |index_data_values| that we build during the upgrade
1866 // of the index tables. We will later move this to the |object_data|
1867 // table.
1868 "CREATE TEMPORARY TABLE temp_index_data_values "
1869 "( object_store_id INTEGER NOT NULL"
1870 ", key BLOB NOT NULL"
1871 ", index_data_values BLOB DEFAULT NULL"
1872 ", PRIMARY KEY (object_store_id, key)"
1873 ") WITHOUT ROWID;"_ns);
1874 if (NS_WARN_IF(NS_FAILED(rv))) {
1875 return rv;
1878 rv = aConnection.ExecuteSimpleSQL(
1879 // These two triggers help build the |index_data_values| blobs. The nested
1880 // SELECT statements help us achieve an "INSERT OR UPDATE"-like behavior.
1881 "CREATE TEMPORARY TRIGGER unique_index_data_upgrade_insert_trigger "
1882 "AFTER INSERT ON unique_index_data_upgrade "
1883 "BEGIN "
1884 "INSERT OR REPLACE INTO temp_index_data_values "
1885 "VALUES "
1886 "( NEW.object_store_id"
1887 ", NEW.object_data_key"
1888 ", insert_idv("
1889 "( SELECT index_data_values "
1890 "FROM temp_index_data_values "
1891 "WHERE object_store_id = NEW.object_store_id "
1892 "AND key = NEW.object_data_key "
1893 "), NEW.index_id"
1894 ", 1" /* unique */
1895 ", NEW.value"
1897 ");"
1898 "END;"_ns);
1899 if (NS_WARN_IF(NS_FAILED(rv))) {
1900 return rv;
1903 rv = aConnection.ExecuteSimpleSQL(
1904 "CREATE TEMPORARY TRIGGER index_data_upgrade_insert_trigger "
1905 "AFTER INSERT ON index_data_upgrade "
1906 "BEGIN "
1907 "INSERT OR REPLACE INTO temp_index_data_values "
1908 "VALUES "
1909 "( NEW.object_store_id"
1910 ", NEW.object_data_key"
1911 ", insert_idv("
1913 "SELECT index_data_values "
1914 "FROM temp_index_data_values "
1915 "WHERE object_store_id = NEW.object_store_id "
1916 "AND key = NEW.object_data_key "
1917 "), NEW.index_id"
1918 ", 0" /* not unique */
1919 ", NEW.value"
1921 ");"
1922 "END;"_ns);
1923 if (NS_WARN_IF(NS_FAILED(rv))) {
1924 return rv;
1927 // Update the |unique_index_data| table to change the column order, remove the
1928 // ON DELETE CASCADE clauses, and to apply the WITHOUT ROWID optimization.
1929 rv = aConnection.ExecuteSimpleSQL(
1930 // Insert all the data.
1931 "INSERT INTO unique_index_data_upgrade "
1932 "SELECT "
1933 "unique_index_data.index_id, "
1934 "upgrade_key(unique_index_data.value), "
1935 "object_data.object_store_id, "
1936 "upgrade_key(unique_index_data.object_data_key) "
1937 "FROM unique_index_data "
1938 "JOIN object_data "
1939 "ON unique_index_data.object_data_id = object_data.id;"_ns);
1940 if (NS_WARN_IF(NS_FAILED(rv))) {
1941 return rv;
1944 rv = aConnection.ExecuteSimpleSQL(
1945 // The trigger is no longer needed.
1946 "DROP TRIGGER unique_index_data_upgrade_insert_trigger;"_ns);
1947 if (NS_WARN_IF(NS_FAILED(rv))) {
1948 return rv;
1951 rv = aConnection.ExecuteSimpleSQL(
1952 // The old table is no longer needed.
1953 "DROP TABLE unique_index_data;"_ns);
1954 if (NS_WARN_IF(NS_FAILED(rv))) {
1955 return rv;
1958 rv = aConnection.ExecuteSimpleSQL(
1959 // Rename the table.
1960 "ALTER TABLE unique_index_data_upgrade "
1961 "RENAME TO unique_index_data;"_ns);
1962 if (NS_WARN_IF(NS_FAILED(rv))) {
1963 return rv;
1966 // Update the |index_data| table to change the column order, remove the ON
1967 // DELETE CASCADE clauses, and to apply the WITHOUT ROWID optimization.
1968 rv = aConnection.ExecuteSimpleSQL(
1969 // Insert all the data.
1970 "INSERT INTO index_data_upgrade "
1971 "SELECT "
1972 "index_data.index_id, "
1973 "upgrade_key(index_data.value), "
1974 "upgrade_key(index_data.object_data_key), "
1975 "object_data.object_store_id "
1976 "FROM index_data "
1977 "JOIN object_data "
1978 "ON index_data.object_data_id = object_data.id;"_ns);
1979 if (NS_WARN_IF(NS_FAILED(rv))) {
1980 return rv;
1983 rv = aConnection.ExecuteSimpleSQL(
1984 // The trigger is no longer needed.
1985 "DROP TRIGGER index_data_upgrade_insert_trigger;"_ns);
1986 if (NS_WARN_IF(NS_FAILED(rv))) {
1987 return rv;
1990 rv = aConnection.ExecuteSimpleSQL(
1991 // The old table is no longer needed.
1992 "DROP TABLE index_data;"_ns);
1993 if (NS_WARN_IF(NS_FAILED(rv))) {
1994 return rv;
1997 rv = aConnection.ExecuteSimpleSQL(
1998 // Rename the table.
1999 "ALTER TABLE index_data_upgrade "
2000 "RENAME TO index_data;"_ns);
2001 if (NS_WARN_IF(NS_FAILED(rv))) {
2002 return rv;
2005 // Update the |object_data| table to add the |index_data_values| column,
2006 // remove the ON DELETE CASCADE clause, and apply the WITHOUT ROWID
2007 // optimization.
2008 rv = aConnection.ExecuteSimpleSQL(
2009 // Insert all the data.
2010 "INSERT INTO object_data_upgrade "
2011 "SELECT "
2012 "object_data.object_store_id, "
2013 "upgrade_key(object_data.key_value), "
2014 "temp_index_data_values.index_data_values, "
2015 "object_data.file_ids, "
2016 "object_data.data "
2017 "FROM object_data "
2018 "LEFT JOIN temp_index_data_values "
2019 "ON object_data.object_store_id = "
2020 "temp_index_data_values.object_store_id "
2021 "AND upgrade_key(object_data.key_value) = "
2022 "temp_index_data_values.key;"_ns);
2023 if (NS_WARN_IF(NS_FAILED(rv))) {
2024 return rv;
2027 rv = aConnection.ExecuteSimpleSQL(
2028 // The temporary table is no longer needed.
2029 "DROP TABLE temp_index_data_values;"_ns);
2030 if (NS_WARN_IF(NS_FAILED(rv))) {
2031 return rv;
2034 rv = aConnection.ExecuteSimpleSQL(
2035 // The old table is no longer needed.
2036 "DROP TABLE object_data;"_ns);
2037 if (NS_WARN_IF(NS_FAILED(rv))) {
2038 return rv;
2041 rv = aConnection.ExecuteSimpleSQL(
2042 // Rename the table.
2043 "ALTER TABLE object_data_upgrade "
2044 "RENAME TO object_data;"_ns);
2045 if (NS_WARN_IF(NS_FAILED(rv))) {
2046 return rv;
2049 // Update the |object_store_index| table to remove the UNIQUE constraint and
2050 // the ON DELETE CASCADE clause.
2051 rv = aConnection.ExecuteSimpleSQL(
2052 "INSERT INTO object_store_index_upgrade "
2053 "SELECT * "
2054 "FROM object_store_index;"_ns);
2055 if (NS_WARN_IF(NS_FAILED(rv))) {
2056 return rv;
2059 rv = aConnection.ExecuteSimpleSQL("DROP TABLE object_store_index;"_ns);
2060 if (NS_WARN_IF(NS_FAILED(rv))) {
2061 return rv;
2064 rv = aConnection.ExecuteSimpleSQL(
2065 "ALTER TABLE object_store_index_upgrade "
2066 "RENAME TO object_store_index;"_ns);
2067 if (NS_WARN_IF(NS_FAILED(rv))) {
2068 return rv;
2071 // Update the |object_store| table to remove the UNIQUE constraint.
2072 rv = aConnection.ExecuteSimpleSQL(
2073 "INSERT INTO object_store_upgrade "
2074 "SELECT * "
2075 "FROM object_store;"_ns);
2076 if (NS_WARN_IF(NS_FAILED(rv))) {
2077 return rv;
2080 rv = aConnection.ExecuteSimpleSQL("DROP TABLE object_store;"_ns);
2081 if (NS_WARN_IF(NS_FAILED(rv))) {
2082 return rv;
2085 rv = aConnection.ExecuteSimpleSQL(
2086 "ALTER TABLE object_store_upgrade "
2087 "RENAME TO object_store;"_ns);
2088 if (NS_WARN_IF(NS_FAILED(rv))) {
2089 return rv;
2092 // Update the |database| table to include the origin, vacuum information, and
2093 // apply the WITHOUT ROWID optimization.
2094 nsCOMPtr<mozIStorageStatement> stmt;
2096 // The parameter names are not used, parameters are bound by index only
2097 // locally in the same function.
2098 rv = aConnection.CreateStatement(
2099 "INSERT INTO database_upgrade "
2100 "SELECT name, :origin, version, 0, 0, 0 "
2101 "FROM database;"_ns,
2102 getter_AddRefs(stmt));
2103 if (NS_WARN_IF(NS_FAILED(rv))) {
2104 return rv;
2107 rv = stmt->BindUTF8StringByIndex(0, aOrigin);
2108 if (NS_WARN_IF(NS_FAILED(rv))) {
2109 return rv;
2112 rv = stmt->Execute();
2113 if (NS_WARN_IF(NS_FAILED(rv))) {
2114 return rv;
2117 rv = aConnection.ExecuteSimpleSQL("DROP TABLE database;"_ns);
2118 if (NS_WARN_IF(NS_FAILED(rv))) {
2119 return rv;
2122 rv = aConnection.ExecuteSimpleSQL(
2123 "ALTER TABLE database_upgrade "
2124 "RENAME TO database;"_ns);
2125 if (NS_WARN_IF(NS_FAILED(rv))) {
2126 return rv;
2129 #ifdef DEBUG
2131 // Make sure there's only one entry in the |database| table.
2132 QM_TRY_INSPECT(const auto& stmt,
2133 quota::CreateAndExecuteSingleStepStatement(
2134 aConnection, "SELECT COUNT(*) FROM database;"_ns),
2135 QM_ASSERT_UNREACHABLE);
2137 int64_t count;
2138 MOZ_ASSERT(NS_SUCCEEDED(stmt->GetInt64(0, &count)));
2140 MOZ_ASSERT(count == 1);
2142 #endif
2144 // Recreate file table triggers.
2145 rv = aConnection.ExecuteSimpleSQL(
2146 "CREATE TRIGGER object_data_insert_trigger "
2147 "AFTER INSERT ON object_data "
2148 "WHEN NEW.file_ids IS NOT NULL "
2149 "BEGIN "
2150 "SELECT update_refcount(NULL, NEW.file_ids);"
2151 "END;"_ns);
2152 if (NS_WARN_IF(NS_FAILED(rv))) {
2153 return rv;
2156 rv = aConnection.ExecuteSimpleSQL(
2157 "CREATE TRIGGER object_data_update_trigger "
2158 "AFTER UPDATE OF file_ids ON object_data "
2159 "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
2160 "BEGIN "
2161 "SELECT update_refcount(OLD.file_ids, NEW.file_ids);"
2162 "END;"_ns);
2163 if (NS_WARN_IF(NS_FAILED(rv))) {
2164 return rv;
2167 rv = aConnection.ExecuteSimpleSQL(
2168 "CREATE TRIGGER object_data_delete_trigger "
2169 "AFTER DELETE ON object_data "
2170 "WHEN OLD.file_ids IS NOT NULL "
2171 "BEGIN "
2172 "SELECT update_refcount(OLD.file_ids, NULL);"
2173 "END;"_ns);
2174 if (NS_WARN_IF(NS_FAILED(rv))) {
2175 return rv;
2178 // Finally, turn on auto_vacuum mode. We use full auto_vacuum mode to reclaim
2179 // disk space on mobile devices (at the cost of some COMMIT speed), and
2180 // incremental auto_vacuum mode on desktop builds.
2181 rv = aConnection.ExecuteSimpleSQL(
2182 #ifdef IDB_MOBILE
2183 "PRAGMA auto_vacuum = FULL;"_ns
2184 #else
2185 "PRAGMA auto_vacuum = INCREMENTAL;"_ns
2186 #endif
2188 if (NS_WARN_IF(NS_FAILED(rv))) {
2189 return rv;
2192 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(18, 0));
2193 if (NS_WARN_IF(NS_FAILED(rv))) {
2194 return rv;
2197 return NS_OK;
2200 nsresult UpgradeSchemaFrom17_0To18_0(mozIStorageConnection& aConnection,
2201 const nsACString& aOrigin) {
2202 MOZ_ASSERT(!aOrigin.IsEmpty());
2204 AUTO_PROFILER_LABEL("UpgradeSchemaFrom17_0To18_0", DOM);
2206 return UpgradeSchemaFrom17_0To18_0Helper::DoUpgrade(aConnection, aOrigin);
2209 nsresult UpgradeSchemaFrom18_0To19_0(mozIStorageConnection& aConnection) {
2210 AssertIsOnIOThread();
2212 nsresult rv;
2213 AUTO_PROFILER_LABEL("UpgradeSchemaFrom18_0To19_0", DOM);
2215 rv = aConnection.ExecuteSimpleSQL(
2216 "ALTER TABLE object_store_index "
2217 "ADD COLUMN locale TEXT;"_ns);
2218 if (NS_WARN_IF(NS_FAILED(rv))) {
2219 return rv;
2222 rv = aConnection.ExecuteSimpleSQL(
2223 "ALTER TABLE object_store_index "
2224 "ADD COLUMN is_auto_locale BOOLEAN;"_ns);
2225 if (NS_WARN_IF(NS_FAILED(rv))) {
2226 return rv;
2229 rv = aConnection.ExecuteSimpleSQL(
2230 "ALTER TABLE index_data "
2231 "ADD COLUMN value_locale BLOB;"_ns);
2232 if (NS_WARN_IF(NS_FAILED(rv))) {
2233 return rv;
2236 rv = aConnection.ExecuteSimpleSQL(
2237 "ALTER TABLE unique_index_data "
2238 "ADD COLUMN value_locale BLOB;"_ns);
2239 if (NS_WARN_IF(NS_FAILED(rv))) {
2240 return rv;
2243 rv = aConnection.ExecuteSimpleSQL(
2244 "CREATE INDEX index_data_value_locale_index "
2245 "ON index_data (index_id, value_locale, object_data_key, value) "
2246 "WHERE value_locale IS NOT NULL;"_ns);
2247 if (NS_WARN_IF(NS_FAILED(rv))) {
2248 return rv;
2251 rv = aConnection.ExecuteSimpleSQL(
2252 "CREATE INDEX unique_index_data_value_locale_index "
2253 "ON unique_index_data (index_id, value_locale, object_data_key, value) "
2254 "WHERE value_locale IS NOT NULL;"_ns);
2255 if (NS_WARN_IF(NS_FAILED(rv))) {
2256 return rv;
2259 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(19, 0));
2260 if (NS_WARN_IF(NS_FAILED(rv))) {
2261 return rv;
2264 return NS_OK;
2267 class UpgradeFileIdsFunction final : public mozIStorageFunction {
2268 SafeRefPtr<DatabaseFileManager> mFileManager;
2270 public:
2271 UpgradeFileIdsFunction() { AssertIsOnIOThread(); }
2273 nsresult Init(nsIFile* aFMDirectory, mozIStorageConnection& aConnection);
2275 NS_DECL_ISUPPORTS
2277 private:
2278 ~UpgradeFileIdsFunction() {
2279 AssertIsOnIOThread();
2281 if (mFileManager) {
2282 mFileManager->Invalidate();
2286 NS_IMETHOD
2287 OnFunctionCall(mozIStorageValueArray* aArguments,
2288 nsIVariant** aResult) override;
2291 nsresult UpgradeSchemaFrom19_0To20_0(nsIFile* aFMDirectory,
2292 mozIStorageConnection& aConnection) {
2293 AssertIsOnIOThread();
2295 AUTO_PROFILER_LABEL("UpgradeSchemaFrom19_0To20_0", DOM);
2297 nsCOMPtr<mozIStorageStatement> stmt;
2298 nsresult rv = aConnection.CreateStatement(
2299 "SELECT count(*) "
2300 "FROM object_data "
2301 "WHERE file_ids IS NOT NULL"_ns,
2302 getter_AddRefs(stmt));
2303 if (NS_WARN_IF(NS_FAILED(rv))) {
2304 return rv;
2307 int64_t count;
2310 mozStorageStatementScoper scoper(stmt);
2312 QM_TRY_INSPECT(const bool& hasResult,
2313 MOZ_TO_RESULT_INVOKE_MEMBER(stmt, ExecuteStep));
2315 if (NS_WARN_IF(!hasResult)) {
2316 MOZ_ASSERT(false, "This should never be possible!");
2317 return NS_ERROR_FAILURE;
2320 count = stmt->AsInt64(0);
2321 if (NS_WARN_IF(count < 0)) {
2322 MOZ_ASSERT(false, "This should never be possible!");
2323 return NS_ERROR_FAILURE;
2327 if (count == 0) {
2328 // Nothing to upgrade.
2329 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(20, 0));
2330 if (NS_WARN_IF(NS_FAILED(rv))) {
2331 return rv;
2334 return NS_OK;
2337 RefPtr<UpgradeFileIdsFunction> function = new UpgradeFileIdsFunction();
2339 rv = function->Init(aFMDirectory, aConnection);
2340 if (NS_WARN_IF(NS_FAILED(rv))) {
2341 return rv;
2344 constexpr auto functionName = "upgrade"_ns;
2346 rv = aConnection.CreateFunction(functionName, 2, function);
2347 if (NS_WARN_IF(NS_FAILED(rv))) {
2348 return rv;
2351 // Disable update trigger.
2352 rv = aConnection.ExecuteSimpleSQL(
2353 "DROP TRIGGER object_data_update_trigger;"_ns);
2354 if (NS_WARN_IF(NS_FAILED(rv))) {
2355 return rv;
2358 rv = aConnection.ExecuteSimpleSQL(
2359 "UPDATE object_data "
2360 "SET file_ids = upgrade(file_ids, data) "
2361 "WHERE file_ids IS NOT NULL;"_ns);
2362 if (NS_WARN_IF(NS_FAILED(rv))) {
2363 return rv;
2366 // Enable update trigger.
2367 rv = aConnection.ExecuteSimpleSQL(
2368 "CREATE TRIGGER object_data_update_trigger "
2369 "AFTER UPDATE OF file_ids ON object_data "
2370 "FOR EACH ROW "
2371 "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
2372 "BEGIN "
2373 "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
2374 "END;"_ns);
2375 if (NS_WARN_IF(NS_FAILED(rv))) {
2376 return rv;
2379 rv = aConnection.RemoveFunction(functionName);
2380 if (NS_WARN_IF(NS_FAILED(rv))) {
2381 return rv;
2384 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(20, 0));
2385 if (NS_WARN_IF(NS_FAILED(rv))) {
2386 return rv;
2389 return NS_OK;
2392 class UpgradeIndexDataValuesFunction final : public mozIStorageFunction {
2393 public:
2394 UpgradeIndexDataValuesFunction() { AssertIsOnIOThread(); }
2396 NS_DECL_ISUPPORTS
2398 private:
2399 ~UpgradeIndexDataValuesFunction() { AssertIsOnIOThread(); }
2401 using IndexDataValuesArray = IndexDataValuesAutoArray;
2402 Result<IndexDataValuesArray, nsresult> ReadOldCompressedIDVFromBlob(
2403 Span<const uint8_t> aBlobData);
2405 NS_IMETHOD
2406 OnFunctionCall(mozIStorageValueArray* aArguments,
2407 nsIVariant** aResult) override;
2410 NS_IMPL_ISUPPORTS(UpgradeIndexDataValuesFunction, mozIStorageFunction)
2412 Result<UpgradeIndexDataValuesFunction::IndexDataValuesArray, nsresult>
2413 UpgradeIndexDataValuesFunction::ReadOldCompressedIDVFromBlob(
2414 const Span<const uint8_t> aBlobData) {
2415 MOZ_ASSERT(!NS_IsMainThread());
2416 MOZ_ASSERT(!IsOnBackgroundThread());
2418 IndexOrObjectStoreId indexId;
2419 bool unique;
2420 bool nextIndexIdAlreadyRead = false;
2422 IndexDataValuesArray result;
2423 for (auto remainder = aBlobData; !remainder.IsEmpty();) {
2424 if (!nextIndexIdAlreadyRead) {
2425 QM_TRY_UNWRAP((std::tie(indexId, unique, remainder)),
2426 ReadCompressedIndexId(remainder));
2428 nextIndexIdAlreadyRead = false;
2430 if (NS_WARN_IF(remainder.IsEmpty())) {
2431 IDB_REPORT_INTERNAL_ERR();
2432 return Err(NS_ERROR_FILE_CORRUPTED);
2435 // Read key buffer length.
2436 QM_TRY_INSPECT(
2437 (const auto& [keyBufferLength, remainderAfterKeyBufferLength]),
2438 ReadCompressedNumber(remainder));
2440 if (NS_WARN_IF(remainderAfterKeyBufferLength.IsEmpty()) ||
2441 NS_WARN_IF(keyBufferLength > uint64_t(UINT32_MAX)) ||
2442 NS_WARN_IF(keyBufferLength > remainderAfterKeyBufferLength.Length())) {
2443 IDB_REPORT_INTERNAL_ERR();
2444 return Err(NS_ERROR_FILE_CORRUPTED);
2447 const auto [keyBuffer, remainderAfterKeyBuffer] =
2448 remainderAfterKeyBufferLength.SplitAt(keyBufferLength);
2449 if (NS_WARN_IF(!result.EmplaceBack(fallible, indexId, unique,
2450 Key{nsCString{AsChars(keyBuffer)}}))) {
2451 IDB_REPORT_INTERNAL_ERR();
2452 return Err(NS_ERROR_OUT_OF_MEMORY);
2455 remainder = remainderAfterKeyBuffer;
2456 if (!remainder.IsEmpty()) {
2457 // Read either a sort key buffer length or an index id.
2458 QM_TRY_INSPECT((const auto& [maybeIndexId, remainderAfterIndexId]),
2459 ReadCompressedNumber(remainder));
2461 // Locale-aware indexes haven't been around long enough to have any users,
2462 // we can safely assume all sort key buffer lengths will be zero.
2463 // XXX This duplicates logic from ReadCompressedIndexId.
2464 if (maybeIndexId != 0) {
2465 unique = maybeIndexId % 2 == 1;
2466 indexId = maybeIndexId / 2;
2467 nextIndexIdAlreadyRead = true;
2470 remainder = remainderAfterIndexId;
2473 result.Sort();
2475 return result;
2478 NS_IMETHODIMP
2479 UpgradeIndexDataValuesFunction::OnFunctionCall(
2480 mozIStorageValueArray* aArguments, nsIVariant** aResult) {
2481 MOZ_ASSERT(aArguments);
2482 MOZ_ASSERT(aResult);
2484 AUTO_PROFILER_LABEL("UpgradeIndexDataValuesFunction::OnFunctionCall", DOM);
2486 uint32_t argc;
2487 nsresult rv = aArguments->GetNumEntries(&argc);
2488 if (NS_WARN_IF(NS_FAILED(rv))) {
2489 return rv;
2492 if (argc != 1) {
2493 NS_WARNING("Don't call me with the wrong number of arguments!");
2494 return NS_ERROR_UNEXPECTED;
2497 int32_t type;
2498 rv = aArguments->GetTypeOfIndex(0, &type);
2499 if (NS_WARN_IF(NS_FAILED(rv))) {
2500 return rv;
2503 if (type != mozIStorageStatement::VALUE_TYPE_BLOB) {
2504 NS_WARNING("Don't call me with the wrong type of arguments!");
2505 return NS_ERROR_UNEXPECTED;
2508 const uint8_t* oldBlob;
2509 uint32_t oldBlobLength;
2510 rv = aArguments->GetSharedBlob(0, &oldBlobLength, &oldBlob);
2511 if (NS_WARN_IF(NS_FAILED(rv))) {
2512 return rv;
2515 QM_TRY_INSPECT(const auto& oldIdv,
2516 ReadOldCompressedIDVFromBlob(Span(oldBlob, oldBlobLength)));
2518 QM_TRY_UNWRAP((auto [newIdv, newIdvLength]),
2519 MakeCompressedIndexDataValues(oldIdv));
2521 nsCOMPtr<nsIVariant> result = new storage::AdoptedBlobVariant(
2522 std::pair(newIdv.release(), newIdvLength));
2524 result.forget(aResult);
2525 return NS_OK;
2528 nsresult UpgradeSchemaFrom20_0To21_0(mozIStorageConnection& aConnection) {
2529 // This should have been part of the 18 to 19 upgrade, where we changed the
2530 // layout of the index_data_values blobs but didn't upgrade the existing data.
2531 // See bug 1202788.
2533 AssertIsOnIOThread();
2535 AUTO_PROFILER_LABEL("UpgradeSchemaFrom20_0To21_0", DOM);
2537 RefPtr<UpgradeIndexDataValuesFunction> function =
2538 new UpgradeIndexDataValuesFunction();
2540 constexpr auto functionName = "upgrade_idv"_ns;
2542 nsresult rv = aConnection.CreateFunction(functionName, 1, function);
2543 if (NS_WARN_IF(NS_FAILED(rv))) {
2544 return rv;
2547 rv = aConnection.ExecuteSimpleSQL(
2548 "UPDATE object_data "
2549 "SET index_data_values = upgrade_idv(index_data_values) "
2550 "WHERE index_data_values IS NOT NULL;"_ns);
2551 if (NS_WARN_IF(NS_FAILED(rv))) {
2552 return rv;
2555 rv = aConnection.RemoveFunction(functionName);
2556 if (NS_WARN_IF(NS_FAILED(rv))) {
2557 return rv;
2560 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(21, 0));
2561 if (NS_WARN_IF(NS_FAILED(rv))) {
2562 return rv;
2565 return NS_OK;
2568 nsresult UpgradeSchemaFrom21_0To22_0(mozIStorageConnection& aConnection) {
2569 // The only change between 21 and 22 was a different structured clone format,
2570 // but it's backwards-compatible.
2571 nsresult rv = aConnection.SetSchemaVersion(MakeSchemaVersion(22, 0));
2572 if (NS_WARN_IF(NS_FAILED(rv))) {
2573 return rv;
2576 return NS_OK;
2579 nsresult UpgradeSchemaFrom22_0To23_0(mozIStorageConnection& aConnection,
2580 const nsACString& aOrigin) {
2581 AssertIsOnIOThread();
2583 MOZ_ASSERT(!aOrigin.IsEmpty());
2585 AUTO_PROFILER_LABEL("UpgradeSchemaFrom22_0To23_0", DOM);
2587 nsCOMPtr<mozIStorageStatement> stmt;
2588 // The parameter names are not used, parameters are bound by index only
2589 // locally in the same function.
2590 nsresult rv = aConnection.CreateStatement(
2591 "UPDATE database SET origin = :origin;"_ns, getter_AddRefs(stmt));
2592 if (NS_WARN_IF(NS_FAILED(rv))) {
2593 return rv;
2596 rv = stmt->BindUTF8StringByIndex(0, aOrigin);
2597 if (NS_WARN_IF(NS_FAILED(rv))) {
2598 return rv;
2601 rv = stmt->Execute();
2602 if (NS_WARN_IF(NS_FAILED(rv))) {
2603 return rv;
2606 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(23, 0));
2607 if (NS_WARN_IF(NS_FAILED(rv))) {
2608 return rv;
2611 return NS_OK;
2614 nsresult UpgradeSchemaFrom23_0To24_0(mozIStorageConnection& aConnection) {
2615 // The only change between 23 and 24 was a different structured clone format,
2616 // but it's backwards-compatible.
2617 nsresult rv = aConnection.SetSchemaVersion(MakeSchemaVersion(24, 0));
2618 if (NS_WARN_IF(NS_FAILED(rv))) {
2619 return rv;
2622 return NS_OK;
2625 nsresult UpgradeSchemaFrom24_0To25_0(mozIStorageConnection& aConnection) {
2626 // The changes between 24 and 25 were an upgraded snappy library, a different
2627 // structured clone format and a different file_ds format. But everything is
2628 // backwards-compatible.
2629 nsresult rv = aConnection.SetSchemaVersion(MakeSchemaVersion(25, 0));
2630 if (NS_WARN_IF(NS_FAILED(rv))) {
2631 return rv;
2634 return NS_OK;
2637 class StripObsoleteOriginAttributesFunction final : public mozIStorageFunction {
2638 public:
2639 NS_DECL_ISUPPORTS
2641 private:
2642 ~StripObsoleteOriginAttributesFunction() = default;
2644 NS_IMETHOD
2645 OnFunctionCall(mozIStorageValueArray* aArguments,
2646 nsIVariant** aResult) override {
2647 MOZ_ASSERT(aArguments);
2648 MOZ_ASSERT(aResult);
2650 AUTO_PROFILER_LABEL("StripObsoleteOriginAttributesFunction::OnFunctionCall",
2651 DOM);
2653 #ifdef DEBUG
2655 uint32_t argCount;
2656 MOZ_ALWAYS_SUCCEEDS(aArguments->GetNumEntries(&argCount));
2657 MOZ_ASSERT(argCount == 1);
2659 int32_t type;
2660 MOZ_ALWAYS_SUCCEEDS(aArguments->GetTypeOfIndex(0, &type));
2661 MOZ_ASSERT(type == mozIStorageValueArray::VALUE_TYPE_TEXT);
2663 #endif
2665 nsCString origin;
2666 nsresult rv = aArguments->GetUTF8String(0, origin);
2667 if (NS_WARN_IF(NS_FAILED(rv))) {
2668 return rv;
2671 // Deserialize and re-serialize to automatically drop any obsolete origin
2672 // attributes.
2673 OriginAttributes oa;
2675 nsCString originNoSuffix;
2676 bool ok = oa.PopulateFromOrigin(origin, originNoSuffix);
2677 if (NS_WARN_IF(!ok)) {
2678 return NS_ERROR_FAILURE;
2681 nsCString suffix;
2682 oa.CreateSuffix(suffix);
2684 nsCOMPtr<nsIVariant> result =
2685 new mozilla::storage::UTF8TextVariant(originNoSuffix + suffix);
2687 result.forget(aResult);
2688 return NS_OK;
2692 nsresult UpgradeSchemaFrom25_0To26_0(mozIStorageConnection& aConnection) {
2693 AssertIsOnIOThread();
2695 AUTO_PROFILER_LABEL("UpgradeSchemaFrom25_0To26_0", DOM);
2697 constexpr auto functionName = "strip_obsolete_attributes"_ns;
2699 nsCOMPtr<mozIStorageFunction> stripObsoleteAttributes =
2700 new StripObsoleteOriginAttributesFunction();
2702 nsresult rv = aConnection.CreateFunction(functionName,
2703 /* aNumArguments */ 1,
2704 stripObsoleteAttributes);
2705 if (NS_WARN_IF(NS_FAILED(rv))) {
2706 return rv;
2709 rv = aConnection.ExecuteSimpleSQL(
2710 "UPDATE DATABASE "
2711 "SET origin = strip_obsolete_attributes(origin) "
2712 "WHERE origin LIKE '%^%';"_ns);
2713 if (NS_WARN_IF(NS_FAILED(rv))) {
2714 return rv;
2717 rv = aConnection.RemoveFunction(functionName);
2718 if (NS_WARN_IF(NS_FAILED(rv))) {
2719 return rv;
2722 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(26, 0));
2723 if (NS_WARN_IF(NS_FAILED(rv))) {
2724 return rv;
2727 return NS_OK;
2730 NS_IMPL_ISUPPORTS(CompressDataBlobsFunction, mozIStorageFunction)
2731 NS_IMPL_ISUPPORTS(EncodeKeysFunction, mozIStorageFunction)
2732 NS_IMPL_ISUPPORTS(StripObsoleteOriginAttributesFunction, mozIStorageFunction);
2734 class DeserializeUpgradeValueHelper final : public Runnable {
2735 public:
2736 explicit DeserializeUpgradeValueHelper(
2737 StructuredCloneReadInfoParent& aCloneReadInfo)
2738 : Runnable("DeserializeUpgradeValueHelper"),
2739 mMonitor("DeserializeUpgradeValueHelper::mMonitor"),
2740 mCloneReadInfo(aCloneReadInfo),
2741 mStatus(NS_ERROR_FAILURE) {}
2743 nsresult DispatchAndWait(nsAString& aFileIds) {
2744 // We don't need to go to the main-thread and use the sandbox.
2745 if (!mCloneReadInfo.Data().Size()) {
2746 PopulateFileIds(aFileIds);
2747 return NS_OK;
2750 // The operation will continue on the main-thread.
2752 MOZ_ASSERT(!(mCloneReadInfo.Data().Size() % sizeof(uint64_t)));
2754 MonitorAutoLock lock(mMonitor);
2756 RefPtr<Runnable> self = this;
2757 const nsresult rv = SchedulerGroup::Dispatch(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 MOZ_UNANNOTATED;
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, ""_ns, false,
2865 false);
2867 nsresult rv = fileManager->Init(aFMDirectory, aConnection);
2868 if (NS_WARN_IF(NS_FAILED(rv))) {
2869 return rv;
2872 mFileManager = std::move(fileManager);
2873 return NS_OK;
2876 NS_IMPL_ISUPPORTS(UpgradeFileIdsFunction, mozIStorageFunction)
2878 NS_IMETHODIMP
2879 UpgradeFileIdsFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
2880 nsIVariant** aResult) {
2881 MOZ_ASSERT(aArguments);
2882 MOZ_ASSERT(aResult);
2883 MOZ_ASSERT(mFileManager);
2885 AUTO_PROFILER_LABEL("UpgradeFileIdsFunction::OnFunctionCall", DOM);
2887 uint32_t argc;
2888 nsresult rv = aArguments->GetNumEntries(&argc);
2889 if (NS_WARN_IF(NS_FAILED(rv))) {
2890 return rv;
2893 if (argc != 2) {
2894 NS_WARNING("Don't call me with the wrong number of arguments!");
2895 return NS_ERROR_UNEXPECTED;
2898 QM_TRY_UNWRAP(auto cloneInfo, GetStructuredCloneReadInfoFromValueArray(
2899 aArguments, 1, 0, *mFileManager));
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(MOZ_TO_RESULT(UpgradeSchemaFrom4To5(aConnection)));
2931 break;
2932 case 5:
2933 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom5To6(aConnection)));
2934 break;
2935 case 6:
2936 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom6To7(aConnection)));
2937 break;
2938 case 7:
2939 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom7To8(aConnection)));
2940 break;
2941 case 8:
2942 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom8To9_0(aConnection)));
2943 vacuumNeeded = true;
2944 break;
2945 case MakeSchemaVersion(9, 0):
2946 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom9_0To10_0(aConnection)));
2947 break;
2948 case MakeSchemaVersion(10, 0):
2949 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom10_0To11_0(aConnection)));
2950 break;
2951 case MakeSchemaVersion(11, 0):
2952 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom11_0To12_0(aConnection)));
2953 break;
2954 case MakeSchemaVersion(12, 0):
2955 QM_TRY(MOZ_TO_RESULT(
2956 UpgradeSchemaFrom12_0To13_0(aConnection, &vacuumNeeded)));
2957 break;
2958 case MakeSchemaVersion(13, 0):
2959 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom13_0To14_0(aConnection)));
2960 break;
2961 case MakeSchemaVersion(14, 0):
2962 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom14_0To15_0(aConnection)));
2963 break;
2964 case MakeSchemaVersion(15, 0):
2965 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom15_0To16_0(aConnection)));
2966 break;
2967 case MakeSchemaVersion(16, 0):
2968 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom16_0To17_0(aConnection)));
2969 break;
2970 case MakeSchemaVersion(17, 0):
2971 QM_TRY(
2972 MOZ_TO_RESULT(UpgradeSchemaFrom17_0To18_0(aConnection, aOrigin)));
2973 vacuumNeeded = true;
2974 break;
2975 case MakeSchemaVersion(18, 0):
2976 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom18_0To19_0(aConnection)));
2977 break;
2978 case MakeSchemaVersion(19, 0):
2979 QM_TRY(MOZ_TO_RESULT(
2980 UpgradeSchemaFrom19_0To20_0(&aFMDirectory, aConnection)));
2981 break;
2982 case MakeSchemaVersion(20, 0):
2983 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom20_0To21_0(aConnection)));
2984 break;
2985 case MakeSchemaVersion(21, 0):
2986 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom21_0To22_0(aConnection)));
2987 break;
2988 case MakeSchemaVersion(22, 0):
2989 QM_TRY(
2990 MOZ_TO_RESULT(UpgradeSchemaFrom22_0To23_0(aConnection, aOrigin)));
2991 break;
2992 case MakeSchemaVersion(23, 0):
2993 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom23_0To24_0(aConnection)));
2994 break;
2995 case MakeSchemaVersion(24, 0):
2996 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom24_0To25_0(aConnection)));
2997 break;
2998 case MakeSchemaVersion(25, 0):
2999 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom25_0To26_0(aConnection)));
3000 break;
3001 default:
3002 QM_FAIL(Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR), []() {
3003 IDB_WARNING(
3004 "Unable to open IndexedDB database, no upgrade path is "
3005 "available!");
3009 QM_TRY_UNWRAP(schemaVersion,
3010 MOZ_TO_RESULT_INVOKE_MEMBER(aConnection, GetSchemaVersion));
3013 MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion);
3015 return vacuumNeeded;
3018 } // namespace mozilla::dom::indexedDB
3020 #undef IDB_MOBILE