Bug 1392070 - Stop using the StopIteration object in Sqlite.jsm. r?mak draft
authorMasatoshi Kimura <VYV03354@nifty.ne.jp>
Sat, 19 Aug 2017 22:10:44 +0900
changeset 650458 cc9f65b881f0ad286c934374a6333f88abd1f82d
parent 650079 b88aaf9c10da9a96c0a794a19e0c9b37da18311e
child 727414 da9242b7e4380807021fbd4e3981a84149b2e701
push id75400
push userVYV03354@nifty.ne.jp
push dateTue, 22 Aug 2017 12:21:09 +0000
reviewersmak
bugs1392070
milestone57.0a1
Bug 1392070 - Stop using the StopIteration object in Sqlite.jsm. r?mak MozReview-Commit-ID: BP3RuM5EweE
toolkit/components/places/PlacesDBUtils.jsm
toolkit/components/places/UnifiedComplete.js
toolkit/modules/NewTabUtils.jsm
toolkit/modules/Sqlite.jsm
toolkit/modules/tests/xpcshell/test_sqlite.js
--- a/toolkit/components/places/PlacesDBUtils.jsm
+++ b/toolkit/components/places/PlacesDBUtils.jsm
@@ -166,19 +166,19 @@ this.PlacesDBUtils = {
 
     try {
       // Run a integrity check, but stop at the first error.
       await PlacesUtils.withConnectionWrapper("PlacesDBUtils: check the integrity", async (db) => {
         let row;
         await db.execute(
           "PRAGMA integrity_check",
           null,
-          r => {
+          (r, cancel) => {
             row = r;
-            throw StopIteration;
+            cancel();
           });
         if (row.getResultByIndex(0) === "ok") {
           logs.push("The database is sane");
         } else {
           // We stopped due to an integrity corruption, try to fix if possible.
           logs.push("The database is corrupt");
           if (skipReindex) {
             Services.prefs.setBoolPref("places.database.replaceOnStartup", true);
--- a/toolkit/components/places/UnifiedComplete.js
+++ b/toolkit/components/places/UnifiedComplete.js
@@ -1694,17 +1694,17 @@ Search.prototype = {
         (this._trimmedOriginalSearchString.endsWith("/") || uri.pathQueryRef.length > 1)) {
       match.icon = `page-icon:${uri.prePath}/`;
     }
 
     this._addMatch(match);
     return true;
   },
 
-  _onResultRow(row) {
+  _onResultRow(row, cancel) {
     if (this._counts[MATCHTYPE.GENERAL] == 0) {
       TelemetryStopwatch.finish(TELEMETRY_1ST_RESULT, this);
     }
     let queryType = row.getResultByIndex(QUERYINDEX_QUERYTYPE);
     let match;
     switch (queryType) {
       case QUERYTYPE_AUTOFILL_HOST:
         this._result.setDefaultIndex(0);
@@ -1717,17 +1717,17 @@ Search.prototype = {
       case QUERYTYPE_FILTERED:
         match = this._processRow(row);
         break;
     }
     this._addMatch(match);
     // If the search has been canceled by the user or by _addMatch, or we
     // fetched enough results, we can stop the underlying Sqlite query.
     if (!this.pending || this._counts[MATCHTYPE.GENERAL] == Prefs.get("maxRichResults"))
-      throw StopIteration;
+      cancel();
   },
 
   _maybeRestyleSearchMatch(match) {
     // Return if the URL does not represent a search result.
     let parseResult =
       PlacesSearchAutocompleteProvider.parseSubmissionURL(match.value);
     if (!parseResult) {
       return;
--- a/toolkit/modules/NewTabUtils.jsm
+++ b/toolkit/modules/NewTabUtils.jsm
@@ -1077,17 +1077,17 @@ var ActivityStreamProvider = {
    *
    * @returns {Promise} Returns a promise with the array of retrieved items
    */
   async executePlacesQuery(aQuery, aOptions = {}) {
     let {columns, params} = aOptions;
     let items = [];
     let queryError = null;
     let conn = await PlacesUtils.promiseDBConnection();
-    await conn.executeCached(aQuery, params, aRow => {
+    await conn.executeCached(aQuery, params, (aRow, aCancel) => {
       try {
         let item = null;
         // if columns array is given construct an object
         if (columns && Array.isArray(columns)) {
           item = {};
           columns.forEach(column => {
             item[column] = aRow.getResultByName(column);
           });
@@ -1096,17 +1096,17 @@ var ActivityStreamProvider = {
           item = [];
           for (let i = 0; i < aRow.numEntries; i++) {
             item.push(aRow.getResultByIndex(i));
           }
         }
         items.push(item);
       } catch (e) {
         queryError = e;
-        throw StopIteration;
+        aCancel();
       }
     });
     if (queryError) {
       throw new Error(queryError);
     }
     return items;
   }
 };
--- a/toolkit/modules/Sqlite.jsm
+++ b/toolkit/modules/Sqlite.jsm
@@ -766,24 +766,21 @@ ConnectionData.prototype = Object.freeze
           if (!onRow) {
             rows.push(row);
             continue;
           }
 
           handledRow = true;
 
           try {
-            onRow(row);
-          } catch (e) {
-            if (e instanceof StopIteration) {
+            onRow(row, () => {
               userCancelled = true;
               pending.cancel();
-              break;
-            }
-
+            });
+          } catch (e) {
             self._log.warn("Exception when calling onRow callback", e);
           }
         }
       },
 
       handleError(error) {
         self._log.info("Error when executing SQL (" +
                        error.result + "): " + error.message);
@@ -1278,24 +1275,24 @@ OpenedConnection.prototype = Object.free
    * no effect because no rows are returned from these. However, it has
    * implications for SELECT statements.
    *
    * If your SELECT statement could return many rows or rows with large amounts
    * of data, for performance reasons it is recommended to pass an `onRow`
    * handler. Otherwise, the buffering may consume unacceptable amounts of
    * resources.
    *
-   * If a `StopIteration` is thrown during execution of an `onRow` handler,
-   * the execution of the statement is immediately cancelled. Subsequent
-   * rows will not be processed and no more `onRow` invocations will be made.
-   * The promise is resolved immediately.
+   * If the second parameter of an `onRow` handler is called during execution
+   * of the `onRow` handler, the execution of the statement is immediately
+   * cancelled. Subsequent rows will not be processed and no more `onRow`
+   * invocations will be made. The promise is resolved immediately.
    *
-   * If a non-`StopIteration` exception is thrown by the `onRow` handler, the
-   * exception is logged and processing of subsequent rows occurs as if nothing
-   * happened. The promise is still resolved (not rejected).
+   * If an exception is thrown by the `onRow` handler, the exception is logged
+   * and processing of subsequent rows occurs as if nothing happened. The
+   * promise is still resolved (not rejected).
    *
    * The return value is a promise that will be resolved when the statement
    * has completed fully.
    *
    * The promise will be rejected with an `Error` instance if the statement
    * did not finish execution fully. The `Error` may have an `errors` property.
    * If defined, it will be an Array of objects describing individual errors.
    * Each object has the properties `result` and `message`. `result` is a
--- a/toolkit/modules/tests/xpcshell/test_sqlite.js
+++ b/toolkit/modules/tests/xpcshell/test_sqlite.js
@@ -306,21 +306,21 @@ add_task(async function test_on_row_stop
   let c = await getDummyDatabase("on_row_stop_iteration");
 
   let sql = "INSERT INTO dirs (path) VALUES (?)";
   for (let i = 0; i < 10; i++) {
     await c.executeCached(sql, ["dir" + i]);
   }
 
   let i = 0;
-  let hasResult = await c.execute("SELECT * FROM dirs", null, function onRow(row) {
+  let hasResult = await c.execute("SELECT * FROM dirs", null, function onRow(row, cancel) {
     i++;
 
     if (i == 5) {
-      throw StopIteration;
+      cancel();
     }
   });
 
   do_check_eq(hasResult, true);
   do_check_eq(i, 5);
 
   await c.close();
 });