Fix busted IN, NOT IN query system [1]#977
Conversation
Trying to use any `__in` or `__not_in` results in a database error:
```
WordPress database error You have an error in your SQL syntax; check the
manual that corresponds to your MariaDB server version for the right syntax to
use near ''context' IN ('')
```
It looks like the algorithm here has been busted for years. `array_shift()`
mutates the underlying `$values` array, losing data user has passed into the
query parameters.
This required rewriting some of the algorithm and throwing some stuff out.
I modelled the fix mostly by grepping for `prepare.*\\\ IN\\\ ` for examples
and picked [1]
EXAMPLE QUERY (custom connector `posts-viewed`)
```
$stream = wp_stream_get_instance();
$records = $stream->db->get_records( [
'action' => 'viewed',
'author' => get_current_user_id(),
'connector' => 'posts-viewed',
'context__in' => [ 'course', 'lesson' ],
] );
```
[1]: https://github.com/WordPress/WordPress/blob/4.9.1/wp-includes/class-wp-comment-query.php#L794
lukecarbis
left a comment
There was a problem hiding this comment.
Sorry it's taken me almost a year to review this! Left a couple of comments, but keen to include this fix in a release soon.
| $field = str_replace( array( 'record_', '__in' ), '', $key ); | ||
| $field = empty( $field ) ? 'ID' : $field; | ||
| $type = is_numeric( array_shift( $value ) ) ? '%d' : '%s'; | ||
| $type = is_numeric( $value[ 0 ] ) ? '%d' : '%s'; |
There was a problem hiding this comment.
This won't work if the first value in the array isn't 0. Could we use this as a non-mutating solution, instead?
array_shift( array_values( $value ) );
There was a problem hiding this comment.
But in what case wouldn't the first item's array index be exactly 0? Can there be string array index values, or?
I've yet to see any PHP warnings or anything in the 1 year this has been running in production. Wouldn't there be?
There was a problem hiding this comment.
It wouldn't be unreasonable to filter the post ids I'm sending as an arg. For example:
$post_ids = array( 1, 2, 3, 4, 5, 6, 7, 8, 9 );
$args['__in'] = array_filter( $post_ids, function( $id ) {
// Returns whether the integer is odd
return($var & 1);
} );That would break the zero index. Also unsetting it as in this example:
foreach ( $args['__in'] as $key => $value ) {
if ( $value > 100 ) {
unset( $args['__in'][ $key ];
}
}| $field = str_replace( array( 'record_', '__not_in' ), '', $key ); | ||
| $field = empty( $field ) ? 'ID' : $field; | ||
| $type = is_numeric( array_shift( $value ) ) ? '%d' : '%s'; | ||
| $type = is_numeric( $value[ 0 ] ) ? '%d' : '%s'; |
There was a problem hiding this comment.
Same here – can we use the non-mutating solution above?
|
#WCEU notes w/ @kasparsd
|
|
|
||
| if ( ! empty( $value ) ) { | ||
| $format = '(' . join( ',', array_fill( 0, count( $value ), $type ) ) . ')'; | ||
| $where .= $wpdb->prepare( " AND $wpdb->stream.%s IN {$format}", $field, $value ); // @codingStandardsIgnoreLine prepare okay |
There was a problem hiding this comment.
$wpdb->stream.%s always becomes $wpdb->stream.'FIELDNAME' which never works. We need to specify the field name picking it from a list of known "property" field names and use it directly as $wpdb->stream.{$field}.
(retrying #958)
Trying to use any
__inor__not_inresults in a database error:It looks like the algorithm here has been busted for years.
array_shift()mutates the underlying
$valuesarray, losing data user has passed into thequery parameters.
This required rewriting some of the algorithm and throwing some stuff out.
I modelled the fix mostly by grepping for
prepare.*\\\ IN\\\for examplesand picked 1
EXAMPLE QUERY (custom connector
posts-viewed)