Closed Bug 1201977 Opened 9 years ago Closed 8 years ago

Replace usage of nsINavHistoryQuery with a string query to nsPIPlacesDatabase.asyncStatement

Categories

(Firefox :: New Tab Page, defect, P3)

43 Branch
defect

Tracking

()

RESOLVED FIXED
Firefox 47
Iteration:
43.3 - Sep 21
Tracking Status
firefox43 --- affected
firefox47 --- fixed

People

(Reporter: oyiptong, Assigned: mzhilyaev)

References

Details

Attachments

(2 files, 1 obsolete file)

The use of nsINavHistoryQuery involves additional code to maintain that doesn't need to be implemented.

For instance, in PlacesProvider.jsm, there is code to sort the results of a database query after all results are collected.

Using a string query and asyncStatement execution will make that code much shorter.

The nsINavHistoryQuery for getLinks ends up being:

SELECT h.id, h.url, h.title AS page_title, h.rev_host, h.visit_count, h.last_visit_date, f.url, null, null, null, null, null AS tags , h.frecency, h.hidden, h.guid
FROM moz_places h LEFT JOIN moz_favicons f ON h.favicon_id = f.id
WHERE 1  AND hidden = 0 AND last_visit_date NOTNULL
ORDER BY 13 DESC LIMIT 100;

This could be replaced with:

SELECT url, title, last_visit_date, frecency
FROM moz_places
WHERE hidden = 0 AND last_visit_date NOTNULL
ORDER BY frecency DESC LIMIT 10;

Since the old code has not been tested before, we need to make sure the functionality stays the same.
ugh, i meant:

SELECT url, title, last_visit_date, frecency
FROM moz_places
WHERE hidden = 0 AND last_visit_date NOTNULL
ORDER BY frecency DESC, last_visit_date DESC, url DESC LIMIT 100;
This query would further add more work on sqlite and reduce the need to remove links in JS:

SELECT url, title, MAX(last_visit_date), MAX(frecency)
FROM moz_places
WHERE hidden = 0 AND last_visit_date NOTNULL 
GROUP BY rev_host
ORDER BY frecency DESC, last_visit_date DESC, url DESC
LIMIT 10;

NewTabUtils.getLinks currently calls extractSites. This would diminish the work extractSites has to do, because it would group by reverse hostname, and ignores the HTTP scheme
mak/ttaubert, can you provide any context on why NewTabUtils used nsINavHistoryQuery.getNewQuery? Or now that there's asyncStatement, it's okay to just query now?
I think we could just query through PlacesUtils.promiseDBConnection.
Priority: -- → P3
Assignee: nobody → mzhilyaev
Attached patch v1 of places query executer (obsolete) — Splinter Review
- parked in Links objects for now
- also not that domain deduplication is not performed as RNT code does not seem to attempt domain deduplication (only the old NT code dedupes links on base domains)
Attachment #8711087 - Flags: review?(oyiptong)
Comment on attachment 8711087 [details] [diff] [review]
v1 of places query executer

Review of attachment 8711087 [details] [diff] [review]:
-----------------------------------------------------------------

::: browser/components/newtab/PlacesProvider.jsm
@@ +195,5 @@
>     *
>     * @returns {Promise} Returns a promise with the array of links as payload.
>     */
>    getLinks: function PlacesProvider_getLinks() {
> +    return Task.spawn(function* () {

I think what you want is:

> getLinks: Task.async(function* () {
>   ...
> }),

@@ +209,5 @@
> +                      params: {limit: this.maxNumLinks}
> +                  });
> +
> +      // It's very important that the initial list of links is sorted in
> +      // the same order imposed by compareLinks, because Links uses compareLinks

Please remove mention of compareLinks, because compareLinks will be (and should be) absent from this file.
An explanation from first principles is a better way.

localeCompare as defined in: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Object/String/localeCompare

does a string comparison in ascending order, taking into account the character encoding if passed. compareLinks doesn't. Therefore, I believe that the sqlite string comparison shouldn't be very different.

LinkUtils should die. With Fire.

@@ +226,5 @@
> +        i = BinarySearch.insertionIndexOf(LinkUtils.compareLinks, links, link);
> +        links.splice(i, 0, link);
> +      }
> +
> +      return links;

On the other hand, we want LinkChecker to run before we return the links. We want to filter out uncouth urls.

@@ +280,2 @@
>    }
> +

nit: additional whitespace

::: browser/components/newtab/tests/xpcshell/test_PlacesProvider.js
@@ +335,5 @@
> +
> +  yield PlacesTestUtils.addVisits(visits);
> +
> +  function testItemValue(results, index, value) {
> +    equal(results[index][0], "https://mozilla.com/" + value, "raw url");

nit: please make use of string templating, as such, where sensible:

> let name = "FooBar";
> let someText = `my name is ${name}`;

It makes code more readable and less error-prone

@@ +397,5 @@
> +  }
> +  catch (e) {
> +    do_check_true("expected failure - bad sql");
> +  }
> +  // missing bidnings

nit: typo

should be

> // missing bindings
Also, did we say we were going to do:

> GROUP BY rev_host

?
Attachment #8711087 - Attachment is obsolete: true
Attachment #8711087 - Flags: review?(oyiptong)
Attachment #8711734 - Flags: review?(oyiptong)
Comment on attachment 8711734 [details] [diff] [review]
v2. fixed reviewer comments and reworked SQL logic

Review of attachment 8711734 [details] [diff] [review]:
-----------------------------------------------------------------

::: browser/components/newtab/PlacesProvider.jsm
@@ +167,5 @@
> +                   "       \"history\" as type " +
> +                   "FROM " +
> +                   "(    " +
> +                   " SELECT rev_host, url, title, frecency, last_visit_date " +
> +                   " FROM moz_places " +

why are you doing a subquery with an order by like this? I don't see a reason, selecting directly from moz_places would bring the same results, maybe even better ones.
This is very unefficient since it cannot use indices and has to go through the whole database. did you try to profile it? On my db with a very fast computer it takes more than 2 seconds.

If you tell me what you are trying to query I can maybe help building something more efficient.
Attachment #8711734 - Flags: feedback-
Marco,

This is our (obviously poor) attempt to off-load as much work to sqlite as we can.
I will be descriptive at how we got to this query, so you can give us an expert advice.

What we wanted to achieve was quite simple:
For every host select a SINGLE page with highest frecency, highest recency.
Then, from that page set ordered by frecency, recency select top N pages.
The result set is a list of pages ordered by frecency, recency where only a single page from a unique host is present.

We made a few naive attempts with this approach:
select rev_host, title, url, MAX(frecency), MAX(last_visit_date)
from moz_places
group by rev_host
order by frecency DESC, last_visit_date DESC

That did not work because "group by" behavior for columns other then aggregates is undefined in SQL.
Since there are two aggregates in select, sqlte ended up mixing urls and titles from different records to satisfy both MAX(frecency) and MAX(last_visit_date).  For example, it would take 'title' from a record with MAX(frecency) and 'url' from a record with MAX(last_visit_date), and would place them together into a resulting record.

To get around this issue, we moved ordering with groupby into subselect, and noticed that sqlite picks the last row in the groupby bucket (which explains this backward orderby clause inside subselect).
And then we re-order the result outside the subselect and do the limit.  This forces sqlite to do the full table scan and screws performance. 

So, what should we do? One optimization that I can think of is to join moz_places with moz_hosts inside subselect - this will choose only the rows which hosts are listed in moz_hosts table.
I am not particularity thrilled about this solution because it would require reversing host column if moz_hosts table in order to join on rev_host.
  
Another option is to revert back to old behavior where we simply select to N most fresent pages inside subselect, and then run date ordering and rev_host deduplication outside the subselect.  That will definitely help, but....  If you could advice a better solution, I would rather use that.

Finally, if you have a beefy places.sqlite file to test performance on, please share. My miki-mouse profile did not catch performance degrade, and I woudl rather use what you use for performance testing.
Flags: needinfo?(mak77)
Thanks for chiming in, Marco.

I'll wait for Marco's feedback before I resume reviewing.
The perf problem is usually due to the most internal query, indeed in your case the most internal query selects the whole table. But you don't need the whole table for the external query, you just need the pages for the 10 most frecent domains. So a first improvement may be to add a condition in the most internal query to limit the number of entries that the external one will have to go through, for example:

    rev_host IN ( 
        SELECT DISTINCT rev_host FROM moz_places
        WHERE hidden = 0 AND last_visit_date NOTNULL
        ORDER by frecency DESC, last_visit_date DESC
        LIMIT 10
    )

With this, you may even try the query you were trying to do originally, but as you said SQL will take "random" values, thus to make it work you should select each value one by one. This works when you need just a couple values but in this case the query would be quite long:

SELECT
(SELECT url FROM moz_places WHERE rev_host = h.rev_host ORDER BY frecency DESC, last_visit_date DESC LIMIT 1) AS url,
(SELECT title FROM moz_places WHERE rev_host = h.rev_host ORDER BY frecency DESC, last_visit_date DESC LIMIT 1) AS title,
(SELECT frecency FROM moz_places WHERE rev_host = h.rev_host ORDER BY frecency DESC, last_visit_date DESC LIMIT 1) AS frecency,
(SELECT last_visit_date FROM moz_places WHERE rev_host = h.rev_host ORDER BY frecency DESC, last_visit_date DESC LIMIT 1) AS lastVisitDate,
"history" as type
FROM moz_places h
WHERE rev_host IN ( 
    SELECT DISTINCT rev_host FROM moz_places
    WHERE hidden = 0 AND last_visit_date NOTNULL
    ORDER by frecency DESC, last_visit_date DESC
    LIMIT 10
)
GROUP BY rev_host
ORDER BY frecency DESC, lastVisitDate DESC, url
LIMIT 10

Even if this executes 6 subqueries, it is faster than your original query cause it acts on a subset and can use indices (about 70ms)

There are various ways to obtain the result starting from that internal optimization, you could even apply it to the subquery trick you used and check how it performs.
In the end, after some playing, I obtained this query that is about 4 times faster than the previous one (15ms):

SELECT url, title, frecency, last_visit_date as lastVisitDate, "history" as type
FROM moz_places
WHERE frecency IN ( 
    SELECT MAX(frecency) AS frecency FROM moz_places
    WHERE rev_host IN ( 
        SELECT DISTINCT rev_host FROM moz_places
        WHERE hidden = 0 AND last_visit_date NOTNULL
        ORDER by frecency DESC, last_visit_date DESC
        LIMIT 10
    )
    GROUP BY rev_host
)
GROUP BY rev_host HAVING MAX(last_visit_date)
ORDER BY frecency DESC, last_visit_date DESC

The idea is, starting from the 10 most frecent hosts, extract the max frecency for each of them, then select the pages having those frecency values, then group by rev_host to remove dupes. the HAVING is using an aggregate and thus it applies to the group subset.
Finally it just needs sorting.

Note I did some quick tests and I seem to get the expected values, but you should double check that I was not fooled.
Regardless this should give you some interesting ideas to investigate.

To test I'm currently just using my everyday browsing profile, it is about 80MB places.sqlite, nothing crazy. Unfortunaly it has many personal data so it's hard to share it... Btw, a good testing db should have something like 100 thousands unique pages from 7000 unique hosts.
Flags: needinfo?(mak77)
Attachment #8711754 - Flags: review?(oyiptong)
Comment on attachment 8711754 [details]
MozReview Request: Bug 1201977 - Replace usage of nsINavHistoryQuery with a string query to nsPIPlacesDatabase.asyncStatement r=oyiptong

https://reviewboard.mozilla.org/r/32297/#review29739

::: browser/components/newtab/PlacesProvider.jsm:165
(Diff revision 1)
> -        .SORT_BY_FRECENCY_DESCENDING;
> +    let sqlQuery = "SELECT url, title, frecency, " +

Please change query according to Marco's feedback

::: browser/components/newtab/PlacesProvider.jsm:205
(Diff revision 1)
> +    return Task.spawn(function*() {

Can this be Task.async?

::: browser/components/newtab/tests/xpcshell/test_PlacesProvider.js:280
(Diff revision 1)
> +  // test paramter passing

nit: typo: parameter passing

::: browser/components/newtab/tests/xpcshell/test_PlacesProvider.js:303
(Diff revision 1)
> +  // test ordering

Unsure what this is testing. It looks like this is testing SQLite and not testing the logic.
Thanks for the patch! The changes to be made are fairly cosmetic, aside from the SQL query suggestions by Marco.
After some twisting on Marco's idea, I finally arrived to the query below that runs almost as fast as Marco's quickest query, and generates correct results:

SELECT url, title, frecency, last_visit_date as lastVisitDate, "history" as type
FROM moz_places
WHERE frecency in (
  SELECT MAX(frecency) as freceny
  FROM moz_places
  WHERE hidden = 0 AND last_visit_date NOTNULL
  GROUP BY rev_host
  ORDER BY freceny DESC
  LIMIT 10
)
GROUP BY rev_host HAVING MAX(lastVisitDate)
ORDER BY frecency DESC, lastVisitDate DESC

My profile is 1/10 of Marco's, so the speed tests may not be directly applicable.  But I do observe the same performance.  Marco, if not mach trouble, could you run the above query against your monstrous profile and verify performance acceptable.
Flags: needinfo?(mak77)
Comment on attachment 8711754 [details]
MozReview Request: Bug 1201977 - Replace usage of nsINavHistoryQuery with a string query to nsPIPlacesDatabase.asyncStatement r=oyiptong

Review request updated; see interdiff: https://reviewboard.mozilla.org/r/32297/diff/1-2/
Attachment #8711754 - Flags: review?(oyiptong)
Comment on attachment 8711754 [details]
MozReview Request: Bug 1201977 - Replace usage of nsINavHistoryQuery with a string query to nsPIPlacesDatabase.asyncStatement r=oyiptong

https://reviewboard.mozilla.org/r/32297/#review31091

::: browser/components/newtab/PlacesProvider.jsm:192
(Diff revision 2)
> -          // searches on the list.  So, ensure the list is so ordered.
> +   *        aOptions.columns - an array of column names. if supplied the return

typo: returned?

::: browser/components/newtab/PlacesProvider.jsm:193
(Diff revision 2)
> -          let i = 1;
> +   *        items will consists of objects keyed on column names. Otherwise

typo: consist

::: browser/components/newtab/PlacesProvider.jsm:193
(Diff revision 2)
> -          let i = 1;
> +   *        items will consists of objects keyed on column names. Otherwise

Otherwise _an_ array?

::: browser/components/newtab/PlacesProvider.jsm:196
(Diff revision 2)
> -            if (LinkUtils.compareLinks(links[i - 1], links[i]) > 0) {
> +   *        aOptions.callback - a callback to handle query raws

typo: raws

::: browser/components/newtab/PlacesProvider.jsm:196
(Diff revision 2)
> -            if (LinkUtils.compareLinks(links[i - 1], links[i]) > 0) {
> +   *        aOptions.callback - a callback to handle query raws

Do we need a callback parameter?

How about we just return the results of the query?

::: browser/components/newtab/PlacesProvider.jsm:204
(Diff revision 2)
> +    return Task.spawn(function*() {

Can this be in Task.async?

Furthermore, do we need to handle each row or can we just obtain the rows?

We'll probably need to make a copy of the keys, but IIRC, the rows are returned as objects with column names as keys.

Something like:

```javascript
executePlacesQuery: Task.async(function*(aSql, aOptions={}) {
  let {columns, params, callback} = aOptions;
  let conn = yield PlacesUtils.promiseDBConnection();
  let rows = conn.executeCached(aSql, params, callback);

  let items = rows.map(row => {
    let item = {};
    for (let column of columns) {
      item[column] = row.getResultByName(column);
    }
    return item;
  });
  
  return items;
```
Attachment #8711754 - Flags: review?(oyiptong)
https://reviewboard.mozilla.org/r/32297/#review31095

::: browser/components/newtab/PlacesProvider.jsm:168
(Diff revision 2)
> -
> +                   "  SELECT MAX(frecency) as freceny " +

typo: freceny

::: browser/components/newtab/PlacesProvider.jsm:172
(Diff revision 2)
> -            let url = row.getResultByIndex(1);
> +                   "  ORDER BY freceny DESC " +

typo: freceny
Comment on attachment 8711754 [details]
MozReview Request: Bug 1201977 - Replace usage of nsINavHistoryQuery with a string query to nsPIPlacesDatabase.asyncStatement r=oyiptong

Review request updated; see interdiff: https://reviewboard.mozilla.org/r/32297/diff/2-3/
Attachment #8711754 - Flags: review?(oyiptong)
https://reviewboard.mozilla.org/r/32297/#review31091

> Do we need a callback parameter?
> 
> How about we just return the results of the query?

talked to :oyiptong -  decided to keep callback for the sake of generality.

> Can this be in Task.async?
> 
> Furthermore, do we need to handle each row or can we just obtain the rows?
> 
> We'll probably need to make a copy of the keys, but IIRC, the rows are returned as objects with column names as keys.
> 
> Something like:
> 
> ```javascript
> executePlacesQuery: Task.async(function*(aSql, aOptions={}) {
>   let {columns, params, callback} = aOptions;
>   let conn = yield PlacesUtils.promiseDBConnection();
>   let rows = conn.executeCached(aSql, params, callback);
> 
>   let items = rows.map(row => {
>     let item = {};
>     for (let column of columns) {
>       item[column] = row.getResultByName(column);
>     }
>     return item;
>   });
>   
>   return items;
> ```

talked to :oyiptong -  decided to keep the original code for the sake of generality and error handling.
(In reply to maxim zhilyaev from comment #17)
> My profile is 1/10 of Marco's, so the speed tests may not be directly
> applicable.  But I do observe the same performance.  Marco, if not mach
> trouble, could you run the above query against your monstrous profile and
> verify performance acceptable.

Is there a specific reason to change my query? like it is returning wrong results in some case?
Locally I see both queries return the same results, but while my query takes 16ms, yours takes 250ms. That makes sense cause your query requires to group by the whole moz_places table and then it can limit it, while my query can first sort by frecency, then pick the first 10 distinct hosts, so it has to walk less rows.
Flags: needinfo?(mak77)
Marco,

I do see differences in my profile between results sets computed by the two queries.
Consider the sub-query results from the slower query:

  SELECT rev_host, MAX(frecency) as freceny
  FROM moz_places
  WHERE hidden = 0 AND last_visit_date NOTNULL
  GROUP BY rev_host
  ORDER BY freceny DESC
  LIMIT 10

721.66.861.291.|42533
moc.elgoog.liam.|40700
moc.nideknil.www.|16985
moc.lanruojevil.1kintup.|13553
moc.elgoog.www.|12434
moc.lanruojevil.lledyps.|12046
moc.lanruojevil.dassaclenoloc.|10971
ur.oboboh.www.|10704
gro.allizom.koobenohp.|9561
moc.itic.enilno.|8580

Note that it has mail.google.com as a second row.

Now, consider results from your subquery

 SELECT rev_host, MAX(frecency) AS frecency FROM moz_places
 WHERE rev_host IN (
    SELECT DISTINCT rev_host FROM moz_places
    WHERE hidden = 0 AND last_visit_date NOTNULL
    ORDER by frecency DESC, last_visit_date DESC
    LIMIT 10
 )
 GROUP BY rev_host
 ORDER BY frecency DESC

721.66.861.291.|42533
moc.nideknil.www.|16985
moc.lanruojevil.1kintup.|13553
moc.lanruojevil.lledyps.|12046
moc.lanruojevil.dassaclenoloc.|10971
ur.oboboh.www.|10704
gro.allizom.koobenohp.|9561
ur.acimonocoen.|7895
moc.lanruojevil.nizahk.|7740
moc.lanruojevil.vearuk-kaid.|7625

Note, that mail.google.com is gone.

I believe the "SELECT DISTINCT rev_host" does not necessarily picks rev_hosts in the order of frecency (which one would expect).  If you run this query

SELECT DISTINCT rev_host from (
        SELECT rev_host FROM moz_places
        WHERE hidden = 0 AND last_visit_date NOTNULL
        ORDER by frecency DESC, last_visit_date DESC
)       
LIMIT 10

you discover that the result set is different from what one would expect by examining the output of the sub-query and choosing top 10 distinct sites going down the list in frecency order (you may want to increase the LIMIT to see this happening in your profile)

Do you suggest we keep looking for the ways to further improve performance, or it's adequate at 250ms?
Please advice.
Flags: needinfo?(mak77)
Here's additional data points from a run on my machine.

This will be comparing the first (a) and second (b) of mak's comment 13, and maksik's revised query (c) from comment 17.

The size of my profile is:

> sqlite> select count(*) from moz_places;
> 104858

Here are the real clock times, averaged obtained from running 5 queries with `.time ON` in sqlite:

>  clock = {'a': [0.113, 0.106, 0.113, 0.11, 0.104],
>           'b': [0.017, 0.016, 0.016, 0.017, 0.015],
>           'c': [0.12, 0.122, 0.127, 0.126, 0.127]}
>  [(name, sum(real_clock)/len(real_clock)) for name, real_clock in clock.iteritems()]
>
> >> [('a', 0.1092), ('b', 0.0162), ('c', 0.1244)]

i.e. mak's second query (16ms) is 6.7X faster than the first (109ms) and 7.7X faster than maksik's revised query (124ms).

I obtain the same results from all 3 queries.
maksik, if you generated that places db, can you please share that sqlite 3 file?
I made a quick profile anonymizer: https://gist.github.com/oyiptong/784e1ed92c4851da2557

It expects a file named `places.backup.sqlite` to exist and will drop all tables except moz_places.
It then replaces moz_places columns with anonymized entries.

If anonymous enough, using this, we could share large profiles.

Thoughts?
updated the script to include rows where rev_host = null
send anonymized profile via email.  Note that i am using:
sqlite3 -version
3.7.13 2012-07-17 17:46:21 65035912264e3acbced5a3e16793327f0a2f17bb
(In reply to maxim zhilyaev from comment #24)
> Do you suggest we keep looking for the ways to further improve performance,
> or it's adequate at 250ms?
> Please advice.

It depends on UX. If we need to run this query before the user can interact with the page, anything above 50-100ms will be noticeable to the user. If instead it's something running in the background that can happen at any time, it will likely be unnoticed.

That said, I will look into your feedback, I think I have an half idea of the problem, and you're likely right the distinct is not doing what we think, likely it needs an aggregate. Let me see what I can do.
Here's another script that measures the times it takes to run the queries. It also compares if the results are the same:

https://gist.github.com/oyiptong/27a1f3f91204f3d56fb3
My results:

results for          mak_1@10   mean:  106ms    median:  107ms
results for          mak_1@100  mean:  313ms    median:  315ms
results for          mak_1@1000 mean:  739ms    median:  737ms
results for          mak_2@10   mean:   14ms    median:   15ms
results for          mak_2@100  mean:   46ms    median:   46ms
results for          mak_2@1000 mean:  122ms    median:  120ms
results for       maksik_2@10   mean:  149ms    median:  148ms
results for       maksik_2@100  mean:  148ms    median:  148ms
results for       maksik_2@1000 mean:  165ms    median:  166ms
['mak_1', 'mak_2', 'maksik_2'] same for 10 query limit: True
['mak_1', 'mak_2', 'maksik_2'] same for 100 query limit: False
['mak_1', 'mak_2', 'maksik_2'] same for 1000 query limit: False
you query is correct, unfortunately we cannot guarantee much about the distinct behavior, it's mostly undefined unless we distinct the same column we order on, and we can't.
My query is much faster mostly cause it can use the frecency index to sort, but we need to sort on max(frecency), so we can't do better than what you are doing, if we want sqlite do all the work for us.

The only thing I found is that if Sqlite doesn't use the rev_host index for the group by, and goes with a linear scan, it takes half the time, for whatever reason. (try replacing "GROUP BY rev_host" with "GROUP BY +rev_host" to suggest the optimizer not using the rev_host index).

How is UX here? is the query blocking interaction?
You definitely want to add a telemetry probe measuring the time taken by this query in the wild.

as a side note, tomorrow I'm out, if I figure out something better in the meanwhile I'll let you know. But feel free to proceed.
My only suggestions so far are trying to skip the index, add a telemetry probe to check how it performas in the wild, and ensure this doesn't block user interaction.
Flags: needinfo?(mak77)
Comment on attachment 8711754 [details]
MozReview Request: Bug 1201977 - Replace usage of nsINavHistoryQuery with a string query to nsPIPlacesDatabase.asyncStatement r=oyiptong

https://reviewboard.mozilla.org/r/32297/#review31467

::: browser/components/newtab/PlacesProvider.jsm:163
(Diff revision 3)
> -      // Sort by frecency, descending.
> +    let sqlQuery = "SELECT url, title, frecency, " +

please use backticks for multiline string

::: browser/components/newtab/tests/xpcshell/test_PlacesProvider.js:306
(Diff revision 3)
> +              "select url, title, last_visit_date, frecency from moz_places " +

nit: use backticks for multiline string

::: browser/components/newtab/tests/xpcshell/test_PlacesProvider.js:306
(Diff revision 3)
> +              "select url, title, last_visit_date, frecency from moz_places " +

nit: backticks for multiline string
Attachment #8711754 - Flags: review?(oyiptong)
Comment on attachment 8711754 [details]
MozReview Request: Bug 1201977 - Replace usage of nsINavHistoryQuery with a string query to nsPIPlacesDatabase.asyncStatement r=oyiptong

https://reviewboard.mozilla.org/r/32297/#review31469

r+ after string nits
Attachment #8711754 - Flags: review+
Attachment #8711734 - Flags: review?(oyiptong)
Comment on attachment 8711754 [details]
MozReview Request: Bug 1201977 - Replace usage of nsINavHistoryQuery with a string query to nsPIPlacesDatabase.asyncStatement r=oyiptong

Review request updated; see interdiff: https://reviewboard.mozilla.org/r/32297/diff/3-4/
Attachment #8711754 - Attachment description: MozReview Request: Bug 1201977 - Replace usage of nsINavHistoryQuery with a string query to nsPIPlacesDatabase.asyncStatement; r?oyiptong → MozReview Request: imported patch 1201977.v5.patch
Comment on attachment 8711754 [details]
MozReview Request: Bug 1201977 - Replace usage of nsINavHistoryQuery with a string query to nsPIPlacesDatabase.asyncStatement r=oyiptong

Review request updated; see interdiff: https://reviewboard.mozilla.org/r/32297/diff/4-5/
Attachment #8711754 - Attachment description: MozReview Request: imported patch 1201977.v5.patch → MozReview Request: Bug 1201977 - Replace usage of nsINavHistoryQuery with a string query to nsPIPlacesDatabase.asyncStatement r=oyiptong
https://hg.mozilla.org/mozilla-central/rev/874d13a1b6ea
https://hg.mozilla.org/mozilla-central/rev/33fc723946aa
Status: NEW → RESOLVED
Closed: 8 years ago
Resolution: --- → FIXED
Target Milestone: --- → Firefox 47
You need to log in before you can comment on or make changes to this bug.