[wp-trac] [WordPress Trac] #40984: Possible Bug with Named Orderby Meta Query SQL

WordPress Trac noreply at wordpress.org
Fri Jun 9 19:51:57 UTC 2017


#40984: Possible Bug with Named Orderby Meta Query SQL
--------------------------+-----------------------------
 Reporter:  maiorano84    |      Owner:
     Type:  defect (bug)  |     Status:  new
 Priority:  normal        |  Milestone:  Awaiting Review
Component:  General       |    Version:  4.8
 Severity:  normal        |   Keywords:
  Focuses:                |
--------------------------+-----------------------------
 I don't know how easy this would be to replicate, but I can at least walk
 through the steps in identifying the bug, and how I was able to address it
 for my own use-case.

 On the web application we're building, we've added a simple meta field
 called "spotlight" to basically drive a single post to the front of the
 list, and apply some extra styling.

 To do this, I had built the following query:

 {{{#!php
 <?php
 $news = new WP_Query([
     'post_type' => 'post',
     'posts_per_page' => 3,
     'orderby' => 'has_spotlight post_date',
     'meta_query' => [
         'relation' => 'OR',
         'has_spotlight' => [
             'key' => 'spotlight',
             'value' => '1'
         ],
         'standard' => [
             'key' => 'spotlight',
             'compare' => 'NOT EXISTS'
         ]
     ],
     'tax_query' => [
         [
             'taxonomy' => 'post_tag',
             'field'    => 'name',
             'terms'    => 'News'
         ]
     ]
 ]);
 }}}

 In other areas of the web application, similar queries appeared to
 function as expected - the "spotlight" post would appear first in the
 list, with remaining posts ordered by date.

 However, for the above query, something interesting happened: A post with
 no spotlight metadata was appearing at the top of the list, and the actual
 spotlight post was appearing below it.

 Using PHPStorm and Xdebug, I set a breakpoint for the query and examined
 the object. Under the request property, the following SQL was generated:

 {{{
 SELECT SQL_CALC_FOUND_ROWS wp_posts.ID
 FROM wp_posts
 LEFT JOIN wp_term_relationships ON (wp_posts.ID =
 wp_term_relationships.object_id)
 LEFT JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id )
 LEFT JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id AND
 mt1.meta_key = 'spotlight' )
 WHERE 1=1
     AND (
         wp_term_relationships.term_taxonomy_id IN (724)
     )
     AND (
          ( wp_postmeta.meta_key = 'spotlight' AND wp_postmeta.meta_value =
 '1' )
          OR
          mt1.post_id IS NULL
     )
     AND wp_posts.post_type = 'post'
     AND (
         wp_posts.post_status = 'publish'
         OR
         wp_posts.post_status = 'acf-disabled'
     )
 GROUP BY wp_posts.ID
 ORDER BY wp_posts.menu_order, CAST(wp_postmeta.meta_value AS CHAR) DESC,
 wp_posts.post_date DESC
 LIMIT 0, 3
 }}}

 When running this SQL directly in SequelPro, the following IDs were
 returned, confirming the order that I was seeing:

 1
 227 <-- Spotlight Post ID
 225

 After looking through the SQL statement, I decided to check exactly what
 Wordpress was seeing. After modifying the query to include everything from
 the wp_postmeta query, something interesting happened:

 {{{
 SELECT SQL_CALC_FOUND_ROWS  wp_posts.ID, wp_postmeta.* FROM wp_posts
 ...
 }}}

 I receive the following results:

 {{{
 ID      meta_id post_id meta_key        meta_value
 1       51      1       _edit_lock      1496942377:1
 227     20794   227     spotlight       1
 225     3305    225     _yst_is_cornerstone
 }}}

 It appears that the table that's being used to define the ordering -
 '''wp_postmeta''' - is not being filtered the same way as the other table
 alias '''mt1'''.

 Upon changing the order clause to use the filtered meta table:

 {{{
 SELECT SQL_CALC_FOUND_ROWS  wp_posts.ID, mt1.*
 ...
 CAST(mt1.meta_value AS CHAR) DESC
 }}}

 I received the correct results:

 {{{
 ID      meta_id post_id meta_key        meta_value
 227     20794   227     spotlight       1
 225     NULL    NULL    NULL            NULL
 1       NULL    NULL    NULL            NULL
 }}}

 This is strange to me, as this line should theoretically prevent other
 unrelated meta information from being included in the results:

 {{{
 WHERE
 ...
 ( wp_postmeta.meta_key = 'spotlight' AND wp_postmeta.meta_value = '1' )
 OR
 mt1.post_id IS NULL
 ...
 }}}

 It should be noted that simply changing the ORDER BY clause in my case -
 while it "fixes" the issue - is not technically correct.

 Instead, the primary meta query clause should also contain the same
 filtering that the second does. The complete query looks like so:

 {{{
 SELECT SQL_CALC_FOUND_ROWS wp_posts.ID
 FROM wp_posts
 LEFT JOIN wp_term_relationships ON (wp_posts.ID =
 wp_term_relationships.object_id)
 LEFT JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id AND
 wp_postmeta.meta_key = 'spotlight' )
 LEFT JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id AND
 mt1.meta_key = 'spotlight' )
 WHERE 1=1
     AND (
         wp_term_relationships.term_taxonomy_id IN (724)
     )
     AND (
         ( wp_postmeta.meta_key = 'spotlight' AND wp_postmeta.meta_value =
 '1' )
         OR
         mt1.post_id IS NULL
     )
     AND wp_posts.post_type = 'post'
     AND (
         wp_posts.post_status = 'publish'
         OR
         wp_posts.post_status = 'acf-disabled'
     )
 GROUP BY wp_posts.ID
 ORDER BY wp_posts.menu_order, CAST(wp_postmeta.meta_value AS CHAR) DESC,
 wp_posts.post_date DESC
 LIMIT 0, 3
 }}}

 '''Notice that the first metadata left join contains the same filtering by
 meta_key as the second.'''

 I'm not sure what far-reaching consequences this might have, or if this
 issue has come up in the past. I imagine it's a bit of an edge-case.

 Let me know if this is something that you're able to reproduce on your
 end, or if you need more information in regards to this.

--
Ticket URL: <https://core.trac.wordpress.org/ticket/40984>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list