tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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