SchemaUpgrades.cpp (86433B)
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/. */ 6 7 #include "SchemaUpgrades.h" 8 9 // local includes 10 #include "ActorsParentCommon.h" 11 #include "DBSchema.h" 12 #include "DatabaseFileInfo.h" 13 #include "DatabaseFileManager.h" 14 #include "IndexedDBCommon.h" 15 #include "IndexedDatabase.h" 16 #include "IndexedDatabaseInlines.h" 17 #include "ReportInternalError.h" 18 19 // global includes 20 #include <stdlib.h> 21 22 #include <algorithm> 23 #include <tuple> 24 #include <utility> 25 26 #include "ErrorList.h" 27 #include "MainThreadUtils.h" 28 #include "SafeRefPtr.h" 29 #include "js/RootingAPI.h" 30 #include "js/StructuredClone.h" 31 #include "js/Value.h" 32 #include "jsapi.h" 33 #include "mozIStorageConnection.h" 34 #include "mozIStorageFunction.h" 35 #include "mozIStorageStatement.h" 36 #include "mozIStorageValueArray.h" 37 #include "mozStorageHelper.h" 38 #include "mozilla/Assertions.h" 39 #include "mozilla/ErrorResult.h" 40 #include "mozilla/Monitor.h" 41 #include "mozilla/OriginAttributes.h" 42 #include "mozilla/ProfilerLabels.h" 43 #include "mozilla/RefPtr.h" 44 #include "mozilla/SchedulerGroup.h" 45 #include "mozilla/Span.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/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" 70 71 struct JSContext; 72 class JSObject; 73 74 #if defined(MOZ_WIDGET_ANDROID) 75 # define IDB_MOBILE 76 #endif 77 78 namespace mozilla::dom::indexedDB { 79 80 using mozilla::ipc::IsOnBackgroundThread; 81 using quota::AssertIsOnIOThread; 82 using quota::PERSISTENCE_TYPE_INVALID; 83 84 namespace { 85 86 nsresult UpgradeSchemaFrom4To5(mozIStorageConnection& aConnection) { 87 AssertIsOnIOThread(); 88 89 AUTO_PROFILER_LABEL("UpgradeSchemaFrom4To5", DOM); 90 91 nsresult rv; 92 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; 102 } 103 104 nsString name; 105 int32_t intVersion; 106 int64_t dataVersion; 107 108 { 109 mozStorageStatementScoper scoper(stmt); 110 111 QM_TRY_INSPECT(const bool& hasResults, 112 MOZ_TO_RESULT_INVOKE_MEMBER(stmt, ExecuteStep)); 113 114 if (NS_WARN_IF(!hasResults)) { 115 return NS_ERROR_FAILURE; 116 } 117 118 nsString version; 119 rv = stmt->GetString(1, version); 120 if (NS_WARN_IF(NS_FAILED(rv))) { 121 return rv; 122 } 123 124 intVersion = version.ToInteger(&rv); 125 if (NS_FAILED(rv)) { 126 intVersion = 0; 127 } 128 129 rv = stmt->GetString(0, name); 130 if (NS_WARN_IF(NS_FAILED(rv))) { 131 return rv; 132 } 133 134 rv = stmt->GetInt64(2, &dataVersion); 135 if (NS_WARN_IF(NS_FAILED(rv))) { 136 return rv; 137 } 138 } 139 140 rv = aConnection.ExecuteSimpleSQL("DROP TABLE database"_ns); 141 if (NS_WARN_IF(NS_FAILED(rv))) { 142 return rv; 143 } 144 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; 153 } 154 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; 163 } 164 165 { 166 mozStorageStatementScoper scoper(stmt); 167 168 rv = stmt->BindStringByIndex(0, name); 169 if (NS_WARN_IF(NS_FAILED(rv))) { 170 return rv; 171 } 172 173 rv = stmt->BindInt32ByIndex(1, intVersion); 174 if (NS_WARN_IF(NS_FAILED(rv))) { 175 return rv; 176 } 177 178 rv = stmt->BindInt64ByIndex(2, dataVersion); 179 if (NS_WARN_IF(NS_FAILED(rv))) { 180 return rv; 181 } 182 183 rv = stmt->Execute(); 184 if (NS_WARN_IF(NS_FAILED(rv))) { 185 return rv; 186 } 187 } 188 189 rv = aConnection.SetSchemaVersion(5); 190 if (NS_WARN_IF(NS_FAILED(rv))) { 191 return rv; 192 } 193 194 return NS_OK; 195 } 196 197 nsresult UpgradeSchemaFrom5To6(mozIStorageConnection& aConnection) { 198 AssertIsOnIOThread(); 199 200 AUTO_PROFILER_LABEL("UpgradeSchemaFrom5To6", DOM); 201 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; 206 } 207 208 rv = aConnection.ExecuteSimpleSQL("DROP INDEX ai_key_index;"_ns); 209 if (NS_WARN_IF(NS_FAILED(rv))) { 210 return rv; 211 } 212 213 rv = aConnection.ExecuteSimpleSQL("DROP INDEX value_index;"_ns); 214 if (NS_WARN_IF(NS_FAILED(rv))) { 215 return rv; 216 } 217 218 rv = aConnection.ExecuteSimpleSQL("DROP INDEX ai_value_index;"_ns); 219 if (NS_WARN_IF(NS_FAILED(rv))) { 220 return rv; 221 } 222 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; 235 } 236 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; 243 } 244 245 rv = aConnection.ExecuteSimpleSQL("DROP TABLE object_data;"_ns); 246 if (NS_WARN_IF(NS_FAILED(rv))) { 247 return rv; 248 } 249 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; 262 } 263 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; 270 } 271 272 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns); 273 if (NS_WARN_IF(NS_FAILED(rv))) { 274 return rv; 275 } 276 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; 287 } 288 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; 295 } 296 297 rv = aConnection.ExecuteSimpleSQL("DROP TABLE ai_object_data;"_ns); 298 if (NS_WARN_IF(NS_FAILED(rv))) { 299 return rv; 300 } 301 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; 313 } 314 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; 321 } 322 323 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns); 324 if (NS_WARN_IF(NS_FAILED(rv))) { 325 return rv; 326 } 327 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; 339 } 340 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; 347 } 348 349 rv = aConnection.ExecuteSimpleSQL("DROP TABLE index_data;"_ns); 350 if (NS_WARN_IF(NS_FAILED(rv))) { 351 return rv; 352 } 353 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; 368 } 369 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; 376 } 377 378 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns); 379 if (NS_WARN_IF(NS_FAILED(rv))) { 380 return rv; 381 } 382 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; 388 } 389 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; 401 } 402 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; 409 } 410 411 rv = aConnection.ExecuteSimpleSQL("DROP TABLE unique_index_data;"_ns); 412 if (NS_WARN_IF(NS_FAILED(rv))) { 413 return rv; 414 } 415 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; 431 } 432 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; 439 } 440 441 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns); 442 if (NS_WARN_IF(NS_FAILED(rv))) { 443 return rv; 444 } 445 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; 451 } 452 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; 463 } 464 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; 471 } 472 473 rv = aConnection.ExecuteSimpleSQL("DROP TABLE ai_index_data;"_ns); 474 if (NS_WARN_IF(NS_FAILED(rv))) { 475 return rv; 476 } 477 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; 491 } 492 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; 499 } 500 501 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns); 502 if (NS_WARN_IF(NS_FAILED(rv))) { 503 return rv; 504 } 505 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; 511 } 512 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; 523 } 524 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; 531 } 532 533 rv = aConnection.ExecuteSimpleSQL("DROP TABLE ai_unique_index_data;"_ns); 534 if (NS_WARN_IF(NS_FAILED(rv))) { 535 return rv; 536 } 537 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; 552 } 553 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; 560 } 561 562 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns); 563 if (NS_WARN_IF(NS_FAILED(rv))) { 564 return rv; 565 } 566 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; 572 } 573 574 rv = aConnection.SetSchemaVersion(6); 575 if (NS_WARN_IF(NS_FAILED(rv))) { 576 return rv; 577 } 578 579 return NS_OK; 580 } 581 582 nsresult UpgradeSchemaFrom6To7(mozIStorageConnection& aConnection) { 583 AssertIsOnIOThread(); 584 585 AUTO_PROFILER_LABEL("UpgradeSchemaFrom6To7", DOM); 586 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; 596 } 597 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; 604 } 605 606 rv = aConnection.ExecuteSimpleSQL("DROP TABLE object_store;"_ns); 607 if (NS_WARN_IF(NS_FAILED(rv))) { 608 return rv; 609 } 610 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; 621 } 622 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; 629 } 630 631 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns); 632 if (NS_WARN_IF(NS_FAILED(rv))) { 633 return rv; 634 } 635 636 rv = aConnection.SetSchemaVersion(7); 637 if (NS_WARN_IF(NS_FAILED(rv))) { 638 return rv; 639 } 640 641 return NS_OK; 642 } 643 644 nsresult UpgradeSchemaFrom7To8(mozIStorageConnection& aConnection) { 645 AssertIsOnIOThread(); 646 647 AUTO_PROFILER_LABEL("UpgradeSchemaFrom7To8", DOM); 648 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; 660 } 661 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; 669 } 670 671 rv = aConnection.ExecuteSimpleSQL("DROP TABLE object_store_index;"_ns); 672 if (NS_WARN_IF(NS_FAILED(rv))) { 673 return rv; 674 } 675 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; 692 } 693 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; 701 } 702 703 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns); 704 if (NS_WARN_IF(NS_FAILED(rv))) { 705 return rv; 706 } 707 708 rv = aConnection.SetSchemaVersion(8); 709 if (NS_WARN_IF(NS_FAILED(rv))) { 710 return rv; 711 } 712 713 return NS_OK; 714 } 715 716 class CompressDataBlobsFunction final : public mozIStorageFunction { 717 public: 718 NS_DECL_ISUPPORTS 719 720 private: 721 ~CompressDataBlobsFunction() = default; 722 723 NS_IMETHOD 724 OnFunctionCall(mozIStorageValueArray* aArguments, 725 nsIVariant** aResult) override { 726 MOZ_ASSERT(aArguments); 727 MOZ_ASSERT(aResult); 728 729 AUTO_PROFILER_LABEL("CompressDataBlobsFunction::OnFunctionCall", DOM); 730 731 uint32_t argc; 732 nsresult rv = aArguments->GetNumEntries(&argc); 733 if (NS_WARN_IF(NS_FAILED(rv))) { 734 return rv; 735 } 736 737 if (argc != 1) { 738 NS_WARNING("Don't call me with the wrong number of arguments!"); 739 return NS_ERROR_UNEXPECTED; 740 } 741 742 int32_t type; 743 rv = aArguments->GetTypeOfIndex(0, &type); 744 if (NS_WARN_IF(NS_FAILED(rv))) { 745 return rv; 746 } 747 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; 751 } 752 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; 758 } 759 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; 765 } 766 767 snappy::RawCompress( 768 reinterpret_cast<const char*>(uncompressed), uncompressedLength, 769 reinterpret_cast<char*>(compressed.get()), &compressedLength); 770 771 std::pair<uint8_t*, int> data(compressed.release(), int(compressedLength)); 772 773 nsCOMPtr<nsIVariant> result = 774 new mozilla::storage::AdoptedBlobVariant(data); 775 776 result.forget(aResult); 777 return NS_OK; 778 } 779 }; 780 781 nsresult UpgradeSchemaFrom8To9_0(mozIStorageConnection& aConnection) { 782 AssertIsOnIOThread(); 783 784 AUTO_PROFILER_LABEL("UpgradeSchemaFrom8To9_0", DOM); 785 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; 791 } 792 793 nsCOMPtr<mozIStorageFunction> compressor = new CompressDataBlobsFunction(); 794 795 constexpr auto compressorName = "compress"_ns; 796 797 rv = aConnection.CreateFunction(compressorName, 1, compressor); 798 if (NS_WARN_IF(NS_FAILED(rv))) { 799 return rv; 800 } 801 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; 807 } 808 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; 813 } 814 815 rv = aConnection.RemoveFunction(compressorName); 816 if (NS_WARN_IF(NS_FAILED(rv))) { 817 return rv; 818 } 819 820 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(9, 0)); 821 if (NS_WARN_IF(NS_FAILED(rv))) { 822 return rv; 823 } 824 825 return NS_OK; 826 } 827 828 nsresult UpgradeSchemaFrom9_0To10_0(mozIStorageConnection& aConnection) { 829 AssertIsOnIOThread(); 830 831 AUTO_PROFILER_LABEL("UpgradeSchemaFrom9_0To10_0", DOM); 832 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; 837 } 838 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; 843 } 844 845 rv = CreateFileTables(aConnection); 846 if (NS_WARN_IF(NS_FAILED(rv))) { 847 return rv; 848 } 849 850 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(10, 0)); 851 if (NS_WARN_IF(NS_FAILED(rv))) { 852 return rv; 853 } 854 855 return NS_OK; 856 } 857 858 nsresult UpgradeSchemaFrom10_0To11_0(mozIStorageConnection& aConnection) { 859 AssertIsOnIOThread(); 860 861 AUTO_PROFILER_LABEL("UpgradeSchemaFrom10_0To11_0", DOM); 862 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; 874 } 875 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; 883 } 884 885 rv = aConnection.ExecuteSimpleSQL("DROP TABLE object_store_index;"_ns); 886 if (NS_WARN_IF(NS_FAILED(rv))) { 887 return rv; 888 } 889 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; 904 } 905 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; 913 } 914 915 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns); 916 if (NS_WARN_IF(NS_FAILED(rv))) { 917 return rv; 918 } 919 920 rv = aConnection.ExecuteSimpleSQL( 921 "DROP TRIGGER object_data_insert_trigger;"_ns); 922 if (NS_WARN_IF(NS_FAILED(rv))) { 923 return rv; 924 } 925 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; 932 } 933 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; 944 } 945 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; 959 } 960 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; 974 } 975 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; 982 } 983 984 rv = aConnection.ExecuteSimpleSQL("DROP TABLE ai_unique_index_data;"_ns); 985 if (NS_WARN_IF(NS_FAILED(rv))) { 986 return rv; 987 } 988 989 rv = aConnection.ExecuteSimpleSQL("DROP TABLE ai_index_data;"_ns); 990 if (NS_WARN_IF(NS_FAILED(rv))) { 991 return rv; 992 } 993 994 rv = aConnection.ExecuteSimpleSQL("DROP TABLE ai_object_data;"_ns); 995 if (NS_WARN_IF(NS_FAILED(rv))) { 996 return rv; 997 } 998 999 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(11, 0)); 1000 if (NS_WARN_IF(NS_FAILED(rv))) { 1001 return rv; 1002 } 1003 1004 return NS_OK; 1005 } 1006 1007 class EncodeKeysFunction final : public mozIStorageFunction { 1008 public: 1009 NS_DECL_ISUPPORTS 1010 1011 private: 1012 ~EncodeKeysFunction() = default; 1013 1014 NS_IMETHOD 1015 OnFunctionCall(mozIStorageValueArray* aArguments, 1016 nsIVariant** aResult) override { 1017 MOZ_ASSERT(aArguments); 1018 MOZ_ASSERT(aResult); 1019 1020 AUTO_PROFILER_LABEL("EncodeKeysFunction::OnFunctionCall", DOM); 1021 1022 uint32_t argc; 1023 nsresult rv = aArguments->GetNumEntries(&argc); 1024 if (NS_WARN_IF(NS_FAILED(rv))) { 1025 return rv; 1026 } 1027 1028 if (argc != 1) { 1029 NS_WARNING("Don't call me with the wrong number of arguments!"); 1030 return NS_ERROR_UNEXPECTED; 1031 } 1032 1033 int32_t type; 1034 rv = aArguments->GetTypeOfIndex(0, &type); 1035 if (NS_WARN_IF(NS_FAILED(rv))) { 1036 return rv; 1037 } 1038 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); 1045 1046 Key key; 1047 QM_TRY(key.SetFromInteger(intKey)); 1048 1049 return key; 1050 } 1051 case mozIStorageStatement::VALUE_TYPE_TEXT: { 1052 nsString stringKey; 1053 aArguments->GetString(0, stringKey); 1054 1055 Key key; 1056 QM_TRY(key.SetFromString(stringKey)); 1057 1058 return key; 1059 } 1060 default: 1061 NS_WARNING("Don't call me with the wrong type of arguments!"); 1062 return Err(NS_ERROR_UNEXPECTED); 1063 } 1064 }())); 1065 1066 const nsCString& buffer = key.GetBuffer(); 1067 1068 std::pair<const void*, int> data(static_cast<const void*>(buffer.get()), 1069 int(buffer.Length())); 1070 1071 nsCOMPtr<nsIVariant> result = new mozilla::storage::BlobVariant(data); 1072 1073 result.forget(aResult); 1074 return NS_OK; 1075 } 1076 }; 1077 1078 nsresult UpgradeSchemaFrom11_0To12_0(mozIStorageConnection& aConnection) { 1079 AssertIsOnIOThread(); 1080 1081 AUTO_PROFILER_LABEL("UpgradeSchemaFrom11_0To12_0", DOM); 1082 1083 constexpr auto encoderName = "encode"_ns; 1084 1085 nsCOMPtr<mozIStorageFunction> encoder = new EncodeKeysFunction(); 1086 1087 nsresult rv = aConnection.CreateFunction(encoderName, 1, encoder); 1088 if (NS_WARN_IF(NS_FAILED(rv))) { 1089 return rv; 1090 } 1091 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; 1102 } 1103 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; 1110 } 1111 1112 rv = aConnection.ExecuteSimpleSQL("DROP TABLE object_data;"_ns); 1113 if (NS_WARN_IF(NS_FAILED(rv))) { 1114 return rv; 1115 } 1116 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; 1130 } 1131 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; 1138 } 1139 1140 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns); 1141 if (NS_WARN_IF(NS_FAILED(rv))) { 1142 return rv; 1143 } 1144 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; 1155 } 1156 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; 1167 } 1168 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; 1178 } 1179 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; 1189 } 1190 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; 1197 } 1198 1199 rv = aConnection.ExecuteSimpleSQL("DROP TABLE index_data;"_ns); 1200 if (NS_WARN_IF(NS_FAILED(rv))) { 1201 return rv; 1202 } 1203 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; 1218 } 1219 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; 1226 } 1227 1228 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns); 1229 if (NS_WARN_IF(NS_FAILED(rv))) { 1230 return rv; 1231 } 1232 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; 1238 } 1239 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; 1249 } 1250 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; 1257 } 1258 1259 rv = aConnection.ExecuteSimpleSQL("DROP TABLE unique_index_data;"_ns); 1260 if (NS_WARN_IF(NS_FAILED(rv))) { 1261 return rv; 1262 } 1263 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; 1279 } 1280 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; 1287 } 1288 1289 rv = aConnection.ExecuteSimpleSQL("DROP TABLE temp_upgrade;"_ns); 1290 if (NS_WARN_IF(NS_FAILED(rv))) { 1291 return rv; 1292 } 1293 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; 1299 } 1300 1301 rv = aConnection.RemoveFunction(encoderName); 1302 if (NS_WARN_IF(NS_FAILED(rv))) { 1303 return rv; 1304 } 1305 1306 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(12, 0)); 1307 if (NS_WARN_IF(NS_FAILED(rv))) { 1308 return rv; 1309 } 1310 1311 return NS_OK; 1312 } 1313 1314 nsresult UpgradeSchemaFrom12_0To13_0(mozIStorageConnection& aConnection, 1315 bool* aVacuumNeeded) { 1316 AssertIsOnIOThread(); 1317 1318 AUTO_PROFILER_LABEL("UpgradeSchemaFrom12_0To13_0", DOM); 1319 1320 nsresult rv; 1321 1322 #ifdef IDB_MOBILE 1323 int32_t defaultPageSize; 1324 rv = aConnection.GetDefaultPageSize(&defaultPageSize); 1325 if (NS_WARN_IF(NS_FAILED(rv))) { 1326 return rv; 1327 } 1328 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); 1332 1333 rv = aConnection.ExecuteSimpleSQL(upgradeQuery); 1334 if (NS_WARN_IF(NS_FAILED(rv))) { 1335 return rv; 1336 } 1337 1338 *aVacuumNeeded = true; 1339 #endif 1340 1341 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(13, 0)); 1342 if (NS_WARN_IF(NS_FAILED(rv))) { 1343 return rv; 1344 } 1345 1346 return NS_OK; 1347 } 1348 1349 nsresult UpgradeSchemaFrom13_0To14_0(mozIStorageConnection& aConnection) { 1350 AssertIsOnIOThread(); 1351 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; 1357 } 1358 1359 return NS_OK; 1360 } 1361 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; 1368 } 1369 1370 return NS_OK; 1371 } 1372 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; 1379 } 1380 1381 return NS_OK; 1382 } 1383 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; 1390 } 1391 1392 return NS_OK; 1393 } 1394 1395 class UpgradeSchemaFrom17_0To18_0Helper final { 1396 class InsertIndexDataValuesFunction; 1397 class UpgradeKeyFunction; 1398 1399 public: 1400 static nsresult DoUpgrade(mozIStorageConnection& aConnection, 1401 const nsACString& aOrigin); 1402 1403 private: 1404 static nsresult DoUpgradeInternal(mozIStorageConnection& aConnection, 1405 const nsACString& aOrigin); 1406 1407 UpgradeSchemaFrom17_0To18_0Helper() = delete; 1408 ~UpgradeSchemaFrom17_0To18_0Helper() = delete; 1409 }; 1410 1411 class UpgradeSchemaFrom17_0To18_0Helper::InsertIndexDataValuesFunction final 1412 : public mozIStorageFunction { 1413 public: 1414 InsertIndexDataValuesFunction() = default; 1415 1416 NS_DECL_ISUPPORTS 1417 1418 private: 1419 ~InsertIndexDataValuesFunction() = default; 1420 1421 NS_DECL_MOZISTORAGEFUNCTION 1422 }; 1423 1424 NS_IMPL_ISUPPORTS( 1425 UpgradeSchemaFrom17_0To18_0Helper::InsertIndexDataValuesFunction, 1426 mozIStorageFunction); 1427 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); 1435 1436 #ifdef DEBUG 1437 { 1438 uint32_t argCount; 1439 MOZ_ALWAYS_SUCCEEDS(aValues->GetNumEntries(&argCount)); 1440 MOZ_ASSERT(argCount == 4); 1441 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); 1446 1447 MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(1, &valueType)); 1448 MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_INTEGER); 1449 1450 MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(2, &valueType)); 1451 MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_INTEGER); 1452 1453 MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(3, &valueType)); 1454 MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB); 1455 } 1456 #endif 1457 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)); 1461 1462 IndexOrObjectStoreId indexId; 1463 nsresult rv = aValues->GetInt64(1, &indexId); 1464 if (NS_WARN_IF(NS_FAILED(rv))) { 1465 return rv; 1466 } 1467 1468 int32_t unique; 1469 rv = aValues->GetInt32(2, &unique); 1470 if (NS_WARN_IF(NS_FAILED(rv))) { 1471 return rv; 1472 } 1473 1474 Key value; 1475 rv = value.SetFromValueArray(aValues, 3); 1476 if (NS_WARN_IF(NS_FAILED(rv))) { 1477 return rv; 1478 } 1479 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; 1485 } 1486 1487 // Compress the array. 1488 QM_TRY_UNWRAP((auto [indexValuesBlob, indexValuesBlobLength]), 1489 MakeCompressedIndexDataValues(indexValues)); 1490 1491 // The compressed blob is the result of this function. 1492 nsCOMPtr<nsIVariant> result = new storage::AdoptedBlobVariant( 1493 std::pair(indexValuesBlob.release(), indexValuesBlobLength)); 1494 1495 result.forget(_retval); 1496 return NS_OK; 1497 } 1498 1499 class UpgradeSchemaFrom17_0To18_0Helper::UpgradeKeyFunction final 1500 : public mozIStorageFunction { 1501 public: 1502 UpgradeKeyFunction() = default; 1503 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 */); 1510 } 1511 1512 NS_DECL_ISUPPORTS 1513 1514 private: 1515 ~UpgradeKeyFunction() = default; 1516 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); 1522 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); 1529 1530 return std::min(aMaxSize, uint32_t(aSourceEnd - aSource)); 1531 } 1532 1533 NS_DECL_MOZISTORAGEFUNCTION 1534 }; 1535 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); 1550 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; 1556 1557 if (NS_WARN_IF(aRecursionDepth > Key::kMaxRecursionDepth)) { 1558 IDB_REPORT_INTERNAL_ERR(); 1559 return NS_ERROR_FILE_CORRUPTED; 1560 } 1561 1562 const uint8_t sourceTag = *aSource - (aTagOffset * kOldMaxType); 1563 MOZ_ASSERT(sourceTag); 1564 1565 if (NS_WARN_IF(sourceTag > kOldMaxType * Key::kMaxArrayCollapse)) { 1566 IDB_REPORT_INTERNAL_ERR(); 1567 return NS_ERROR_FILE_CORRUPTED; 1568 } 1569 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++; 1575 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); 1580 1581 aDestination = std::copy(aSource, aSource + byteCount, aDestination); 1582 aSource += byteCount; 1583 1584 return NS_OK; 1585 } 1586 1587 if (sourceTag == kOldStringTag) { 1588 // Write the new tag. 1589 *aDestination++ = Key::eString + (aTagOffset * Key::eMaxType); 1590 aSource++; 1591 1592 while (aSource < aSourceEnd) { 1593 const uint8_t byte = *aSource++; 1594 *aDestination++ = byte; 1595 1596 if (!byte) { 1597 // Just copied the terminator. 1598 break; 1599 } 1600 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); 1606 1607 aDestination = std::copy(aSource, aSource + byteCount, aDestination); 1608 aSource += byteCount; 1609 } 1610 } 1611 1612 return NS_OK; 1613 } 1614 1615 if (NS_WARN_IF(sourceTag < kOldArrayTag)) { 1616 IDB_REPORT_INTERNAL_ERR(); 1617 return NS_ERROR_FILE_CORRUPTED; 1618 } 1619 1620 aTagOffset++; 1621 1622 if (aTagOffset == Key::kMaxArrayCollapse) { 1623 MOZ_ASSERT(sourceTag == kOldArrayTag); 1624 1625 *aDestination++ = (aTagOffset * Key::eMaxType); 1626 aSource++; 1627 1628 aTagOffset = 0; 1629 } 1630 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; 1637 } 1638 1639 aTagOffset = 0; 1640 } 1641 1642 if (aSource < aSourceEnd) { 1643 MOZ_ASSERT((*aSource - (aTagOffset * kOldMaxType)) == Key::eTerminator); 1644 *aDestination++ = Key::eTerminator + (aTagOffset * Key::eMaxType); 1645 aSource++; 1646 } 1647 1648 return NS_OK; 1649 } 1650 1651 NS_IMPL_ISUPPORTS(UpgradeSchemaFrom17_0To18_0Helper::UpgradeKeyFunction, 1652 mozIStorageFunction); 1653 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); 1661 1662 #ifdef DEBUG 1663 { 1664 uint32_t argCount; 1665 MOZ_ALWAYS_SUCCEEDS(aValues->GetNumEntries(&argCount)); 1666 MOZ_ASSERT(argCount == 1); 1667 1668 int32_t valueType; 1669 MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(0, &valueType)); 1670 MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB); 1671 } 1672 #endif 1673 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; 1680 } 1681 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; 1688 } 1689 1690 rv = CopyAndUpgradeKeyBuffer(blobData, blobData + blobDataLength, 1691 upgradedBlobData.get()); 1692 if (NS_WARN_IF(NS_FAILED(rv))) { 1693 return rv; 1694 } 1695 1696 // The upgraded key is the result of this function. 1697 std::pair<uint8_t*, int> data(upgradedBlobData.release(), 1698 int(blobDataLength)); 1699 1700 nsCOMPtr<nsIVariant> result = new mozilla::storage::AdoptedBlobVariant(data); 1701 1702 result.forget(_retval); 1703 return NS_OK; 1704 } 1705 1706 // static 1707 nsresult UpgradeSchemaFrom17_0To18_0Helper::DoUpgrade( 1708 mozIStorageConnection& aConnection, const nsACString& aOrigin) { 1709 MOZ_ASSERT(!aOrigin.IsEmpty()); 1710 1711 // Register the |upgrade_key| function. 1712 RefPtr<UpgradeKeyFunction> updateFunction = new UpgradeKeyFunction(); 1713 1714 constexpr auto upgradeKeyFunctionName = "upgrade_key"_ns; 1715 1716 nsresult rv = 1717 aConnection.CreateFunction(upgradeKeyFunctionName, 1, updateFunction); 1718 if (NS_WARN_IF(NS_FAILED(rv))) { 1719 return rv; 1720 } 1721 1722 // Register the |insert_idv| function. 1723 RefPtr<InsertIndexDataValuesFunction> insertIDVFunction = 1724 new InsertIndexDataValuesFunction(); 1725 1726 constexpr auto insertIDVFunctionName = "insert_idv"_ns; 1727 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; 1732 } 1733 1734 rv = DoUpgradeInternal(aConnection, aOrigin); 1735 1736 MOZ_ALWAYS_SUCCEEDS(aConnection.RemoveFunction(upgradeKeyFunctionName)); 1737 MOZ_ALWAYS_SUCCEEDS(aConnection.RemoveFunction(insertIDVFunctionName)); 1738 1739 if (NS_WARN_IF(NS_FAILED(rv))) { 1740 return rv; 1741 } 1742 1743 return NS_OK; 1744 } 1745 1746 // static 1747 nsresult UpgradeSchemaFrom17_0To18_0Helper::DoUpgradeInternal( 1748 mozIStorageConnection& aConnection, const nsACString& aOrigin) { 1749 MOZ_ASSERT(!aOrigin.IsEmpty()); 1750 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; 1759 } 1760 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; 1767 } 1768 1769 // Create the new tables and triggers first. 1770 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; 1783 } 1784 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; 1795 } 1796 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; 1811 } 1812 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; 1827 } 1828 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; 1844 } 1845 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; 1861 } 1862 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; 1875 } 1876 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" 1895 ")" 1896 ");" 1897 "END;"_ns); 1898 if (NS_WARN_IF(NS_FAILED(rv))) { 1899 return rv; 1900 } 1901 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(" 1911 "(" 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" 1919 ")" 1920 ");" 1921 "END;"_ns); 1922 if (NS_WARN_IF(NS_FAILED(rv))) { 1923 return rv; 1924 } 1925 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; 1941 } 1942 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; 1948 } 1949 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; 1955 } 1956 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; 1963 } 1964 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; 1980 } 1981 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; 1987 } 1988 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; 1994 } 1995 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; 2002 } 2003 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; 2024 } 2025 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; 2031 } 2032 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; 2038 } 2039 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; 2046 } 2047 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; 2056 } 2057 2058 rv = aConnection.ExecuteSimpleSQL("DROP TABLE object_store_index;"_ns); 2059 if (NS_WARN_IF(NS_FAILED(rv))) { 2060 return rv; 2061 } 2062 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; 2068 } 2069 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; 2077 } 2078 2079 rv = aConnection.ExecuteSimpleSQL("DROP TABLE object_store;"_ns); 2080 if (NS_WARN_IF(NS_FAILED(rv))) { 2081 return rv; 2082 } 2083 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; 2089 } 2090 2091 // Update the |database| table to include the origin, vacuum information, and 2092 // apply the WITHOUT ROWID optimization. 2093 nsCOMPtr<mozIStorageStatement> stmt; 2094 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; 2104 } 2105 2106 rv = stmt->BindUTF8StringByIndex(0, aOrigin); 2107 if (NS_WARN_IF(NS_FAILED(rv))) { 2108 return rv; 2109 } 2110 2111 rv = stmt->Execute(); 2112 if (NS_WARN_IF(NS_FAILED(rv))) { 2113 return rv; 2114 } 2115 2116 rv = aConnection.ExecuteSimpleSQL("DROP TABLE database;"_ns); 2117 if (NS_WARN_IF(NS_FAILED(rv))) { 2118 return rv; 2119 } 2120 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; 2126 } 2127 2128 #ifdef DEBUG 2129 { 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); 2135 2136 int64_t count; 2137 MOZ_ASSERT(NS_SUCCEEDED(stmt->GetInt64(0, &count))); 2138 2139 MOZ_ASSERT(count == 1); 2140 } 2141 #endif 2142 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; 2153 } 2154 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; 2164 } 2165 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; 2175 } 2176 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 2186 ); 2187 if (NS_WARN_IF(NS_FAILED(rv))) { 2188 return rv; 2189 } 2190 2191 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(18, 0)); 2192 if (NS_WARN_IF(NS_FAILED(rv))) { 2193 return rv; 2194 } 2195 2196 return NS_OK; 2197 } 2198 2199 nsresult UpgradeSchemaFrom17_0To18_0(mozIStorageConnection& aConnection, 2200 const nsACString& aOrigin) { 2201 MOZ_ASSERT(!aOrigin.IsEmpty()); 2202 2203 AUTO_PROFILER_LABEL("UpgradeSchemaFrom17_0To18_0", DOM); 2204 2205 return UpgradeSchemaFrom17_0To18_0Helper::DoUpgrade(aConnection, aOrigin); 2206 } 2207 2208 nsresult UpgradeSchemaFrom18_0To19_0(mozIStorageConnection& aConnection) { 2209 AssertIsOnIOThread(); 2210 2211 nsresult rv; 2212 AUTO_PROFILER_LABEL("UpgradeSchemaFrom18_0To19_0", DOM); 2213 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; 2219 } 2220 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; 2226 } 2227 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; 2233 } 2234 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; 2240 } 2241 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; 2248 } 2249 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; 2256 } 2257 2258 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(19, 0)); 2259 if (NS_WARN_IF(NS_FAILED(rv))) { 2260 return rv; 2261 } 2262 2263 return NS_OK; 2264 } 2265 2266 class UpgradeFileIdsFunction final : public mozIStorageFunction { 2267 SafeRefPtr<DatabaseFileManager> mFileManager; 2268 2269 public: 2270 UpgradeFileIdsFunction() { AssertIsOnIOThread(); } 2271 2272 nsresult Init(nsIFile* aFMDirectory, mozIStorageConnection& aConnection); 2273 2274 NS_DECL_ISUPPORTS 2275 2276 private: 2277 ~UpgradeFileIdsFunction() { 2278 AssertIsOnIOThread(); 2279 2280 if (mFileManager) { 2281 mFileManager->Invalidate(); 2282 } 2283 } 2284 2285 NS_IMETHOD 2286 OnFunctionCall(mozIStorageValueArray* aArguments, 2287 nsIVariant** aResult) override; 2288 }; 2289 2290 nsresult UpgradeSchemaFrom19_0To20_0(nsIFile* aFMDirectory, 2291 mozIStorageConnection& aConnection) { 2292 AssertIsOnIOThread(); 2293 2294 AUTO_PROFILER_LABEL("UpgradeSchemaFrom19_0To20_0", DOM); 2295 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; 2304 } 2305 2306 int64_t count; 2307 2308 { 2309 mozStorageStatementScoper scoper(stmt); 2310 2311 QM_TRY_INSPECT(const bool& hasResult, 2312 MOZ_TO_RESULT_INVOKE_MEMBER(stmt, ExecuteStep)); 2313 2314 if (NS_WARN_IF(!hasResult)) { 2315 MOZ_ASSERT(false, "This should never be possible!"); 2316 return NS_ERROR_FAILURE; 2317 } 2318 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; 2323 } 2324 } 2325 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; 2331 } 2332 2333 return NS_OK; 2334 } 2335 2336 RefPtr<UpgradeFileIdsFunction> function = new UpgradeFileIdsFunction(); 2337 2338 rv = function->Init(aFMDirectory, aConnection); 2339 if (NS_WARN_IF(NS_FAILED(rv))) { 2340 return rv; 2341 } 2342 2343 constexpr auto functionName = "upgrade"_ns; 2344 2345 rv = aConnection.CreateFunction(functionName, 2, function); 2346 if (NS_WARN_IF(NS_FAILED(rv))) { 2347 return rv; 2348 } 2349 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; 2355 } 2356 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; 2363 } 2364 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; 2376 } 2377 2378 rv = aConnection.RemoveFunction(functionName); 2379 if (NS_WARN_IF(NS_FAILED(rv))) { 2380 return rv; 2381 } 2382 2383 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(20, 0)); 2384 if (NS_WARN_IF(NS_FAILED(rv))) { 2385 return rv; 2386 } 2387 2388 return NS_OK; 2389 } 2390 2391 class UpgradeIndexDataValuesFunction final : public mozIStorageFunction { 2392 public: 2393 UpgradeIndexDataValuesFunction() { AssertIsOnIOThread(); } 2394 2395 NS_DECL_ISUPPORTS 2396 2397 private: 2398 ~UpgradeIndexDataValuesFunction() { AssertIsOnIOThread(); } 2399 2400 using IndexDataValuesArray = IndexDataValuesAutoArray; 2401 Result<IndexDataValuesArray, nsresult> ReadOldCompressedIDVFromBlob( 2402 Span<const uint8_t> aBlobData); 2403 2404 NS_IMETHOD 2405 OnFunctionCall(mozIStorageValueArray* aArguments, 2406 nsIVariant** aResult) override; 2407 }; 2408 2409 NS_IMPL_ISUPPORTS(UpgradeIndexDataValuesFunction, mozIStorageFunction) 2410 2411 Result<UpgradeIndexDataValuesFunction::IndexDataValuesArray, nsresult> 2412 UpgradeIndexDataValuesFunction::ReadOldCompressedIDVFromBlob( 2413 const Span<const uint8_t> aBlobData) { 2414 MOZ_ASSERT(!NS_IsMainThread()); 2415 MOZ_ASSERT(!IsOnBackgroundThread()); 2416 2417 IndexOrObjectStoreId indexId; 2418 bool unique; 2419 bool nextIndexIdAlreadyRead = false; 2420 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)); 2426 } 2427 nextIndexIdAlreadyRead = false; 2428 2429 if (NS_WARN_IF(remainder.IsEmpty())) { 2430 IDB_REPORT_INTERNAL_ERR(); 2431 return Err(NS_ERROR_FILE_CORRUPTED); 2432 } 2433 2434 // Read key buffer length. 2435 QM_TRY_INSPECT( 2436 (const auto& [keyBufferLength, remainderAfterKeyBufferLength]), 2437 ReadCompressedNumber(remainder)); 2438 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); 2444 } 2445 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); 2452 } 2453 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)); 2459 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; 2467 } 2468 2469 remainder = remainderAfterIndexId; 2470 } 2471 } 2472 result.Sort(); 2473 2474 return result; 2475 } 2476 2477 NS_IMETHODIMP 2478 UpgradeIndexDataValuesFunction::OnFunctionCall( 2479 mozIStorageValueArray* aArguments, nsIVariant** aResult) { 2480 MOZ_ASSERT(aArguments); 2481 MOZ_ASSERT(aResult); 2482 2483 AUTO_PROFILER_LABEL("UpgradeIndexDataValuesFunction::OnFunctionCall", DOM); 2484 2485 uint32_t argc; 2486 nsresult rv = aArguments->GetNumEntries(&argc); 2487 if (NS_WARN_IF(NS_FAILED(rv))) { 2488 return rv; 2489 } 2490 2491 if (argc != 1) { 2492 NS_WARNING("Don't call me with the wrong number of arguments!"); 2493 return NS_ERROR_UNEXPECTED; 2494 } 2495 2496 int32_t type; 2497 rv = aArguments->GetTypeOfIndex(0, &type); 2498 if (NS_WARN_IF(NS_FAILED(rv))) { 2499 return rv; 2500 } 2501 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; 2505 } 2506 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; 2512 } 2513 2514 QM_TRY_INSPECT(const auto& oldIdv, 2515 ReadOldCompressedIDVFromBlob(Span(oldBlob, oldBlobLength))); 2516 2517 QM_TRY_UNWRAP((auto [newIdv, newIdvLength]), 2518 MakeCompressedIndexDataValues(oldIdv)); 2519 2520 nsCOMPtr<nsIVariant> result = new storage::AdoptedBlobVariant( 2521 std::pair(newIdv.release(), newIdvLength)); 2522 2523 result.forget(aResult); 2524 return NS_OK; 2525 } 2526 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. 2531 2532 AssertIsOnIOThread(); 2533 2534 AUTO_PROFILER_LABEL("UpgradeSchemaFrom20_0To21_0", DOM); 2535 2536 RefPtr<UpgradeIndexDataValuesFunction> function = 2537 new UpgradeIndexDataValuesFunction(); 2538 2539 constexpr auto functionName = "upgrade_idv"_ns; 2540 2541 nsresult rv = aConnection.CreateFunction(functionName, 1, function); 2542 if (NS_WARN_IF(NS_FAILED(rv))) { 2543 return rv; 2544 } 2545 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; 2552 } 2553 2554 rv = aConnection.RemoveFunction(functionName); 2555 if (NS_WARN_IF(NS_FAILED(rv))) { 2556 return rv; 2557 } 2558 2559 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(21, 0)); 2560 if (NS_WARN_IF(NS_FAILED(rv))) { 2561 return rv; 2562 } 2563 2564 return NS_OK; 2565 } 2566 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; 2573 } 2574 2575 return NS_OK; 2576 } 2577 2578 nsresult UpgradeSchemaFrom22_0To23_0(mozIStorageConnection& aConnection, 2579 const nsACString& aOrigin) { 2580 AssertIsOnIOThread(); 2581 2582 MOZ_ASSERT(!aOrigin.IsEmpty()); 2583 2584 AUTO_PROFILER_LABEL("UpgradeSchemaFrom22_0To23_0", DOM); 2585 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; 2593 } 2594 2595 rv = stmt->BindUTF8StringByIndex(0, aOrigin); 2596 if (NS_WARN_IF(NS_FAILED(rv))) { 2597 return rv; 2598 } 2599 2600 rv = stmt->Execute(); 2601 if (NS_WARN_IF(NS_FAILED(rv))) { 2602 return rv; 2603 } 2604 2605 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(23, 0)); 2606 if (NS_WARN_IF(NS_FAILED(rv))) { 2607 return rv; 2608 } 2609 2610 return NS_OK; 2611 } 2612 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; 2619 } 2620 2621 return NS_OK; 2622 } 2623 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; 2631 } 2632 2633 return NS_OK; 2634 } 2635 2636 class StripObsoleteOriginAttributesFunction final : public mozIStorageFunction { 2637 public: 2638 NS_DECL_ISUPPORTS 2639 2640 private: 2641 ~StripObsoleteOriginAttributesFunction() = default; 2642 2643 NS_IMETHOD 2644 OnFunctionCall(mozIStorageValueArray* aArguments, 2645 nsIVariant** aResult) override { 2646 MOZ_ASSERT(aArguments); 2647 MOZ_ASSERT(aResult); 2648 2649 AUTO_PROFILER_LABEL("StripObsoleteOriginAttributesFunction::OnFunctionCall", 2650 DOM); 2651 2652 #ifdef DEBUG 2653 { 2654 uint32_t argCount; 2655 MOZ_ALWAYS_SUCCEEDS(aArguments->GetNumEntries(&argCount)); 2656 MOZ_ASSERT(argCount == 1); 2657 2658 int32_t type; 2659 MOZ_ALWAYS_SUCCEEDS(aArguments->GetTypeOfIndex(0, &type)); 2660 MOZ_ASSERT(type == mozIStorageValueArray::VALUE_TYPE_TEXT); 2661 } 2662 #endif 2663 2664 nsCString origin; 2665 nsresult rv = aArguments->GetUTF8String(0, origin); 2666 if (NS_WARN_IF(NS_FAILED(rv))) { 2667 return rv; 2668 } 2669 2670 // Deserialize and re-serialize to automatically drop any obsolete origin 2671 // attributes. 2672 OriginAttributes oa; 2673 2674 nsCString originNoSuffix; 2675 bool ok = oa.PopulateFromOrigin(origin, originNoSuffix); 2676 if (NS_WARN_IF(!ok)) { 2677 return NS_ERROR_FAILURE; 2678 } 2679 2680 nsCString suffix; 2681 oa.CreateSuffix(suffix); 2682 2683 nsCOMPtr<nsIVariant> result = 2684 new mozilla::storage::UTF8TextVariant(originNoSuffix + suffix); 2685 2686 result.forget(aResult); 2687 return NS_OK; 2688 } 2689 }; 2690 2691 nsresult UpgradeSchemaFrom25_0To26_0(mozIStorageConnection& aConnection) { 2692 AssertIsOnIOThread(); 2693 2694 AUTO_PROFILER_LABEL("UpgradeSchemaFrom25_0To26_0", DOM); 2695 2696 constexpr auto functionName = "strip_obsolete_attributes"_ns; 2697 2698 nsCOMPtr<mozIStorageFunction> stripObsoleteAttributes = 2699 new StripObsoleteOriginAttributesFunction(); 2700 2701 nsresult rv = aConnection.CreateFunction(functionName, 2702 /* aNumArguments */ 1, 2703 stripObsoleteAttributes); 2704 if (NS_WARN_IF(NS_FAILED(rv))) { 2705 return rv; 2706 } 2707 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; 2714 } 2715 2716 rv = aConnection.RemoveFunction(functionName); 2717 if (NS_WARN_IF(NS_FAILED(rv))) { 2718 return rv; 2719 } 2720 2721 rv = aConnection.SetSchemaVersion(MakeSchemaVersion(26, 0)); 2722 if (NS_WARN_IF(NS_FAILED(rv))) { 2723 return rv; 2724 } 2725 2726 return NS_OK; 2727 } 2728 2729 NS_IMPL_ISUPPORTS(CompressDataBlobsFunction, mozIStorageFunction) 2730 NS_IMPL_ISUPPORTS(EncodeKeysFunction, mozIStorageFunction) 2731 NS_IMPL_ISUPPORTS(StripObsoleteOriginAttributesFunction, mozIStorageFunction); 2732 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) {} 2741 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; 2747 } 2748 2749 // The operation will continue on the main-thread. 2750 2751 MOZ_ASSERT(!(mCloneReadInfo.Data().Size() % sizeof(uint64_t))); 2752 2753 MonitorAutoLock lock(mMonitor); 2754 2755 RefPtr<Runnable> self = this; 2756 const nsresult rv = SchedulerGroup::Dispatch(self.forget()); 2757 if (NS_WARN_IF(NS_FAILED(rv))) { 2758 return rv; 2759 } 2760 2761 lock.Wait(); 2762 2763 if (NS_FAILED(mStatus)) { 2764 return mStatus; 2765 } 2766 2767 PopulateFileIds(aFileIds); 2768 return NS_OK; 2769 } 2770 2771 NS_IMETHOD 2772 Run() override { 2773 MOZ_ASSERT(NS_IsMainThread()); 2774 2775 AutoJSAPI jsapi; 2776 jsapi.Init(); 2777 JSContext* cx = jsapi.cx(); 2778 2779 JS::Rooted<JSObject*> global(cx, GetSandbox(cx)); 2780 if (NS_WARN_IF(!global)) { 2781 OperationCompleted(NS_ERROR_FAILURE); 2782 return NS_OK; 2783 } 2784 2785 const JSAutoRealm ar(cx, global); 2786 2787 JS::Rooted<JS::Value> value(cx); 2788 const nsresult rv = DeserializeUpgradeValue(cx, &value); 2789 if (NS_WARN_IF(NS_FAILED(rv))) { 2790 OperationCompleted(rv); 2791 return NS_OK; 2792 } 2793 2794 OperationCompleted(NS_OK); 2795 return NS_OK; 2796 } 2797 2798 private: 2799 nsresult DeserializeUpgradeValue(JSContext* aCx, 2800 JS::MutableHandle<JS::Value> aValue) { 2801 static const JSStructuredCloneCallbacks callbacks = { 2802 StructuredCloneReadCallback<StructuredCloneReadInfoParent>, 2803 nullptr, 2804 nullptr, 2805 nullptr, 2806 nullptr, 2807 nullptr, 2808 nullptr, 2809 nullptr}; 2810 2811 if (!JS_ReadStructuredClone( 2812 aCx, mCloneReadInfo.Data(), JS_STRUCTURED_CLONE_VERSION, 2813 JS::StructuredCloneScope::DifferentProcessForIndexedDB, aValue, 2814 JS::CloneDataPolicy(), &callbacks, &mCloneReadInfo)) { 2815 return NS_ERROR_DOM_DATA_CLONE_ERR; 2816 } 2817 2818 return NS_OK; 2819 } 2820 2821 void PopulateFileIds(nsAString& aFileIds) { 2822 for (uint32_t count = mCloneReadInfo.Files().Length(), index = 0; 2823 index < count; index++) { 2824 const StructuredCloneFileParent& file = mCloneReadInfo.Files()[index]; 2825 2826 const int64_t id = file.FileInfo().Id(); 2827 2828 if (index) { 2829 aFileIds.Append(' '); 2830 } 2831 aFileIds.AppendInt(file.Type() == StructuredCloneFileBase::eBlob ? id 2832 : -id); 2833 } 2834 } 2835 2836 void OperationCompleted(nsresult aStatus) { 2837 mStatus = aStatus; 2838 2839 MonitorAutoLock lock(mMonitor); 2840 lock.Notify(); 2841 } 2842 2843 Monitor mMonitor MOZ_UNANNOTATED; 2844 StructuredCloneReadInfoParent& mCloneReadInfo; 2845 nsresult mStatus; 2846 }; 2847 2848 nsresult DeserializeUpgradeValueToFileIds( 2849 StructuredCloneReadInfoParent& aCloneReadInfo, nsAString& aFileIds) { 2850 MOZ_ASSERT(!NS_IsMainThread()); 2851 2852 const RefPtr<DeserializeUpgradeValueHelper> helper = 2853 new DeserializeUpgradeValueHelper(aCloneReadInfo); 2854 return helper->DispatchAndWait(aFileIds); 2855 } 2856 2857 nsresult UpgradeFileIdsFunction::Init(nsIFile* aFMDirectory, 2858 mozIStorageConnection& aConnection) { 2859 // This DatabaseFileManager doesn't need real origin info, etc. The only 2860 // purpose is to store file ids without adding more complexity or code 2861 // duplication. 2862 auto fileManager = MakeSafeRefPtr<DatabaseFileManager>( 2863 PERSISTENCE_TYPE_INVALID, quota::OriginMetadata{}, 2864 /* aDatabaseName */ u""_ns, /* aDatabaseID */ ""_ns, 2865 /* aDatabaseFilePath */ u""_ns, /* aEnforcingQuota */ false, 2866 /* aIsInPrivateBrowsingMode */ false); 2867 2868 nsresult rv = 2869 fileManager->Init(aFMDirectory, /* aDatabaseVersion */ 0, aConnection); 2870 if (NS_WARN_IF(NS_FAILED(rv))) { 2871 return rv; 2872 } 2873 2874 mFileManager = std::move(fileManager); 2875 return NS_OK; 2876 } 2877 2878 NS_IMPL_ISUPPORTS(UpgradeFileIdsFunction, mozIStorageFunction) 2879 2880 NS_IMETHODIMP 2881 UpgradeFileIdsFunction::OnFunctionCall(mozIStorageValueArray* aArguments, 2882 nsIVariant** aResult) { 2883 MOZ_ASSERT(aArguments); 2884 MOZ_ASSERT(aResult); 2885 MOZ_ASSERT(mFileManager); 2886 2887 AUTO_PROFILER_LABEL("UpgradeFileIdsFunction::OnFunctionCall", DOM); 2888 2889 uint32_t argc; 2890 nsresult rv = aArguments->GetNumEntries(&argc); 2891 if (NS_WARN_IF(NS_FAILED(rv))) { 2892 return rv; 2893 } 2894 2895 if (argc != 2) { 2896 NS_WARNING("Don't call me with the wrong number of arguments!"); 2897 return NS_ERROR_UNEXPECTED; 2898 } 2899 2900 QM_TRY_UNWRAP(auto cloneInfo, GetStructuredCloneReadInfoFromValueArray( 2901 aArguments, 1, 0, *mFileManager)); 2902 2903 nsAutoString fileIds; 2904 // XXX does this really need non-const cloneInfo? 2905 rv = DeserializeUpgradeValueToFileIds(cloneInfo, fileIds); 2906 if (NS_WARN_IF(NS_FAILED(rv))) { 2907 return NS_ERROR_DOM_DATA_CLONE_ERR; 2908 } 2909 2910 nsCOMPtr<nsIVariant> result = new mozilla::storage::TextVariant(fileIds); 2911 2912 result.forget(aResult); 2913 return NS_OK; 2914 } 2915 2916 } // namespace 2917 2918 Result<bool, nsresult> MaybeUpgradeSchema(mozIStorageConnection& aConnection, 2919 const int32_t aSchemaVersion, 2920 nsIFile& aFMDirectory, 2921 const nsACString& aOrigin) { 2922 bool vacuumNeeded = false; 2923 int32_t schemaVersion = aSchemaVersion; 2924 2925 // This logic needs to change next time we change the schema! 2926 static_assert(kSQLiteSchemaVersion == int32_t((26 << 4) + 0), 2927 "Upgrade function needed due to schema version increase."); 2928 2929 while (schemaVersion != kSQLiteSchemaVersion) { 2930 switch (schemaVersion) { 2931 case 4: 2932 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom4To5(aConnection))); 2933 break; 2934 case 5: 2935 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom5To6(aConnection))); 2936 break; 2937 case 6: 2938 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom6To7(aConnection))); 2939 break; 2940 case 7: 2941 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom7To8(aConnection))); 2942 break; 2943 case 8: 2944 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom8To9_0(aConnection))); 2945 vacuumNeeded = true; 2946 break; 2947 case MakeSchemaVersion(9, 0): 2948 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom9_0To10_0(aConnection))); 2949 break; 2950 case MakeSchemaVersion(10, 0): 2951 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom10_0To11_0(aConnection))); 2952 break; 2953 case MakeSchemaVersion(11, 0): 2954 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom11_0To12_0(aConnection))); 2955 break; 2956 case MakeSchemaVersion(12, 0): 2957 QM_TRY(MOZ_TO_RESULT( 2958 UpgradeSchemaFrom12_0To13_0(aConnection, &vacuumNeeded))); 2959 break; 2960 case MakeSchemaVersion(13, 0): 2961 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom13_0To14_0(aConnection))); 2962 break; 2963 case MakeSchemaVersion(14, 0): 2964 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom14_0To15_0(aConnection))); 2965 break; 2966 case MakeSchemaVersion(15, 0): 2967 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom15_0To16_0(aConnection))); 2968 break; 2969 case MakeSchemaVersion(16, 0): 2970 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom16_0To17_0(aConnection))); 2971 break; 2972 case MakeSchemaVersion(17, 0): 2973 QM_TRY( 2974 MOZ_TO_RESULT(UpgradeSchemaFrom17_0To18_0(aConnection, aOrigin))); 2975 vacuumNeeded = true; 2976 break; 2977 case MakeSchemaVersion(18, 0): 2978 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom18_0To19_0(aConnection))); 2979 break; 2980 case MakeSchemaVersion(19, 0): 2981 QM_TRY(MOZ_TO_RESULT( 2982 UpgradeSchemaFrom19_0To20_0(&aFMDirectory, aConnection))); 2983 break; 2984 case MakeSchemaVersion(20, 0): 2985 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom20_0To21_0(aConnection))); 2986 break; 2987 case MakeSchemaVersion(21, 0): 2988 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom21_0To22_0(aConnection))); 2989 break; 2990 case MakeSchemaVersion(22, 0): 2991 QM_TRY( 2992 MOZ_TO_RESULT(UpgradeSchemaFrom22_0To23_0(aConnection, aOrigin))); 2993 break; 2994 case MakeSchemaVersion(23, 0): 2995 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom23_0To24_0(aConnection))); 2996 break; 2997 case MakeSchemaVersion(24, 0): 2998 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom24_0To25_0(aConnection))); 2999 break; 3000 case MakeSchemaVersion(25, 0): 3001 QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom25_0To26_0(aConnection))); 3002 break; 3003 default: 3004 QM_FAIL(Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR), []() { 3005 IDB_WARNING( 3006 "Unable to open IndexedDB database, no upgrade path is " 3007 "available!"); 3008 }); 3009 } 3010 3011 QM_TRY_UNWRAP(schemaVersion, 3012 MOZ_TO_RESULT_INVOKE_MEMBER(aConnection, GetSchemaVersion)); 3013 } 3014 3015 MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion); 3016 3017 return vacuumNeeded; 3018 } 3019 3020 } // namespace mozilla::dom::indexedDB 3021 3022 #undef IDB_MOBILE