Bug 888784 - Make FormHistory.getAutoCompleteResults use Sqlite.jsm backend. r=mak
☠☠ backed out by 9fc7e71752fd ☠ ☠
authorMike Conley <mconley@mozilla.com>
Fri, 01 Dec 2017 10:53:32 -0500
changeset 450178 67c58cb32ac9cb44bee2d15be016d9065172d964
parent 450177 46fb8f82f2bffe2bbd27ee8bcec815e3f59d7697
child 450179 d92599272745f5dd442cb1e4d66e3c18b56e42df
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak
bugs888784
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 888784 - Make FormHistory.getAutoCompleteResults use Sqlite.jsm backend. r=mak MozReview-Commit-ID: 7rYj1lMosLW
toolkit/components/satchel/FormHistory.jsm
--- a/toolkit/components/satchel/FormHistory.jsm
+++ b/toolkit/components/satchel/FormHistory.jsm
@@ -1355,31 +1355,41 @@ this.FormHistory = {
     }
 
     if (numSearches == 0) {
       // We don't have to wait for any statements to return.
       updateFormHistoryWrite(aChanges, handlers);
     }
   },
 
-  getAutoCompleteResults(searchString, params, aCallbacks) {
+  getAutoCompleteResults(searchString, params, aHandlers) {
     // only do substring matching when the search string contains more than one character
     let searchTokens;
     let where = "";
     let boundaryCalc = "";
+
+    if (searchString.length >= 1) {
+      params.valuePrefix = searchString + "%";
+    }
+
     if (searchString.length > 1) {
       searchTokens = searchString.split(/\s+/);
 
       // build up the word boundary and prefix match bonus calculation
       boundaryCalc = "MAX(1, :prefixWeight * (value LIKE :valuePrefix ESCAPE '/') + (";
       // for each word, calculate word boundary weights for the SELECT clause and
       // add word to the WHERE clause of the query
       let tokenCalc = [];
       let searchTokenCount = Math.min(searchTokens.length, MAX_SEARCH_TOKENS);
       for (let i = 0; i < searchTokenCount; i++) {
+        let escapedToken = searchTokens[i];
+        params["tokenBegin" + i] = escapedToken + "%";
+        params["tokenBoundary" + i] = "% " + escapedToken + "%";
+        params["tokenContains" + i] = "%" + escapedToken + "%";
+
         tokenCalc.push("(value LIKE :tokenBegin" + i + " ESCAPE '/') + " +
                             "(value LIKE :tokenBoundary" + i + " ESCAPE '/')");
         where += "AND (value LIKE :tokenContains" + i + " ESCAPE '/') ";
       }
       // add more weight if we have a traditional prefix match and
       // multiply boundary bonuses by boundary weight
       boundaryCalc += tokenCalc.join(" + ") + ") * :boundaryWeight)";
     } else if (searchString.length == 1) {
@@ -1391,16 +1401,18 @@ this.FormHistory = {
       where = "";
       boundaryCalc = "1";
       delete params.prefixWeight;
       delete params.boundaryWeight;
     }
 
     params.now = Date.now() * 1000; // convert from ms to microseconds
 
+    let handlers = this._prepareHandlers(aHandlers);
+
     /* Three factors in the frecency calculation for an entry (in order of use in calculation):
      * 1) average number of times used - items used more are ranked higher
      * 2) how recently it was last used - items used recently are ranked higher
      * 3) additional weight for aged entries surviving expiry - these entries are relevant
      *    since they have been used multiple times over a large time span so rank them higher
      * The score is then divided by the bucket size and we round the result so that entries
      * with a very similar frecency are bucketed together with an alphabetical sort. This is
      * to reduce the amount of moving around by entries while typing.
@@ -1414,72 +1426,52 @@ this.FormHistory = {
                     "MAX(1.0, :agedWeight * (firstUsed < :expiryDate)) / " +
                     ":bucketSize " +
                 ", 3) AS frecency, " +
                 boundaryCalc + " AS boundaryBonuses " +
                 "FROM moz_formhistory " +
                 "WHERE fieldname=:fieldname " + where +
                 "ORDER BY ROUND(frecency * boundaryBonuses) DESC, UPPER(value) ASC";
 
-    let stmt = dbCreateAsyncStatement(query, params);
+    let cancelled = false;
+
+    let cancellableQuery = {
+      cancel() {
+        cancelled = true;
+      },
+    };
 
-    // Chicken and egg problem: Need the statement to escape the params we
-    // pass to the function that gives us the statement. So, fix it up now.
-    if (searchString.length >= 1) {
-      stmt.params.valuePrefix = stmt.escapeStringForLIKE(searchString, "/") + "%";
-    }
-    if (searchString.length > 1) {
-      let searchTokenCount = Math.min(searchTokens.length, MAX_SEARCH_TOKENS);
-      for (let i = 0; i < searchTokenCount; i++) {
-        let escapedToken = stmt.escapeStringForLIKE(searchTokens[i], "/");
-        stmt.params["tokenBegin" + i] = escapedToken + "%";
-        stmt.params["tokenBoundary" + i] =  "% " + escapedToken + "%";
-        stmt.params["tokenContains" + i] = "%" + escapedToken + "%";
-      }
-    } else {
-      // no additional params need to be substituted into the query when the
-      // length is zero or one
-    }
+    this.db.then(async conn => {
+      try {
+        await conn.executeCached(query, params, (row, cancel) => {
+          if (cancelled) {
+            cancel();
+            return;
+          }
 
-    let pending = stmt.executeAsync({
-      handleResult(aResultSet) {
-        for (let row = aResultSet.getNextRow(); row; row = aResultSet.getNextRow()) {
           let value = row.getResultByName("value");
           let guid = row.getResultByName("guid");
           let frecency = row.getResultByName("frecency");
           let entry = {
-            text:          value,
+            text: value,
             guid,
             textLowerCase: value.toLowerCase(),
             frecency,
-            totalScore:    Math.round(frecency * row.getResultByName("boundaryBonuses")),
+            totalScore: Math.round(frecency * row.getResultByName("boundaryBonuses")),
           };
-          if (aCallbacks && aCallbacks.handleResult) {
-            aCallbacks.handleResult(entry);
-          }
-        }
-      },
+          handlers.handleResult(entry);
+        });
+        handlers.handleCompletion(0);
+      } catch (e) {
+        handlers.handleError(e);
+        handlers.handleCompletion(1);
+      }
+    });
 
-      handleError(aError) {
-        if (aCallbacks && aCallbacks.handleError) {
-          aCallbacks.handleError(aError);
-        }
-      },
-
-      handleCompletion(aReason) {
-        if (aCallbacks && aCallbacks.handleCompletion) {
-          aCallbacks.handleCompletion(
-            aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED ?
-              0 :
-              1
-          );
-        }
-      },
-    });
-    return pending;
+    return cancellableQuery;
   },
 
   // This is used only so that the test can verify deleted table support.
   get _supportsDeletedTable() {
     return supportsDeletedTable;
   },
   set _supportsDeletedTable(val) {
     supportsDeletedTable = val;