Make WordPress Core

Changeset 30400


Ignore:
Timestamp:
11/20/2014 01:45:10 AM (10 years ago)
Author:
pento
Message:

WPDB: Force STRICT_ALL_TABLES to be enabled as soon as we connect to the MySQL server.

This improves data integrity when inserting and updating rows in the database, particularly when trying to insert emoji into posts stored with character sets that don't support emoji.

See #21212.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/wp-db.php

    r30375 r30400  
    567567     * @var array
    568568     */
    569     protected $incompatible_modes = array( 'NO_ZERO_DATE', 'ONLY_FULL_GROUP_BY',
    570         'STRICT_TRANS_TABLES', 'STRICT_ALL_TABLES', 'TRADITIONAL' );
     569    protected $incompatible_modes = array( 'NO_ZERO_DATE', 'ONLY_FULL_GROUP_BY', 'TRADITIONAL' );
     570
     571    /**
     572     * A list of required SQL modes.
     573     *
     574     * @since 4.1.0
     575     * @access protected
     576     * @var array
     577     */
     578    protected $required_modes = array( 'STRICT_ALL_TABLES' );
    571579
    572580    /**
     
    779787    public function set_sql_mode( $modes = array() ) {
    780788        if ( empty( $modes ) ) {
    781             if ( $this->use_mysqli ) {
    782                 $res = mysqli_query( $this->dbh, 'SELECT @@SESSION.sql_mode' );
     789            $modes = $this->get_var( "SELECT @@SESSION.sql_mode" );
     790            if ( $modes ) {
     791                $modes = $original_modes = explode( ',', $modes );
    783792            } else {
    784                 $res = mysql_query( 'SELECT @@SESSION.sql_mode', $this->dbh );
    785             }
    786 
    787             if ( empty( $res ) ) {
    788                 return;
    789             }
    790 
    791             if ( $this->use_mysqli ) {
    792                 $modes_array = mysqli_fetch_array( $res );
    793                 if ( empty( $modes_array[0] ) ) {
    794                     return;
    795                 }
    796                 $modes_str = $modes_array[0];
    797             } else {
    798                 $modes_str = mysql_result( $res, 0 );
    799             }
    800 
    801             if ( empty( $modes_str ) ) {
    802                 return;
    803             }
    804 
    805             $modes = explode( ',', $modes_str );
     793                $modes = $original_modes = array();
     794            }
    806795        }
    807796
     
    813802         * @since 3.9.0
    814803         *
    815          * @see wpdb::$incompatible_modes
    816          *
    817804         * @param array $incompatible_modes An array of incompatible modes.
    818805         */
    819806        $incompatible_modes = (array) apply_filters( 'incompatible_sql_modes', $this->incompatible_modes );
    820807
    821         foreach( $modes as $i => $mode ) {
    822             if ( in_array( $mode, $incompatible_modes ) ) {
    823                 unset( $modes[ $i ] );
     808        /**
     809         * Filter the list of required SQL modes to include.
     810         *
     811         * @since 4.1.0
     812         *
     813         * @param array $required_modes An array of required modes.
     814         */
     815        $required_modes = (array) apply_filters( 'required_sql_modes', $this->required_modes );
     816
     817        $modes = array_diff( $modes, $incompatible_modes );
     818        $modes = array_unique( array_merge( $modes, $required_modes ) );
     819
     820        // Don't run SET SESSION if we have nothing to change.
     821        if ( isset( $original_modes ) ) {
     822            sort( $original_modes );
     823            sort( $modes );
     824            if ( $original_modes === $modes ) {
     825                return;
    824826            }
    825827        }
     
    827829        $modes_str = implode( ',', $modes );
    828830
    829         if ( $this->use_mysqli ) {
    830             mysqli_query( $this->dbh, "SET SESSION sql_mode='$modes_str'" );
    831         } else {
    832             mysql_query( "SET SESSION sql_mode='$modes_str'", $this->dbh );
     831        $this->query( "SET SESSION sql_mode='$modes_str'" );
     832        if ( $this->last_error ) {
     833            dead_db();
    833834        }
    834835    }
     
    14831484            $this->has_connected = true;
    14841485            $this->set_charset( $this->dbh );
     1486            $this->ready = true;
    14851487            $this->set_sql_mode();
    1486             $this->ready = true;
    14871488            $this->select( $this->dbname, $this->dbh );
    14881489
  • trunk/tests/phpunit/tests/db.php

    r30345 r30400  
    277277     * @ticket 26847
    278278     */
    279     public function test_set_sql_mode() {
     279    function test_set_sql_mode() {
    280280        global $wpdb;
    281281
    282282        $current_modes = $wpdb->get_var( 'SELECT @@SESSION.sql_mode;' );
    283283
    284         $new_modes = array( 'IGNORE_SPACE', 'NO_AUTO_CREATE_USER' );
     284        $new_modes = $expected_modes = array( 'IGNORE_SPACE', 'NO_AUTO_CREATE_USER' );
     285        $expected_modes[] = 'STRICT_ALL_TABLES';
     286
    285287        $wpdb->set_sql_mode( $new_modes );
     288
    286289        $check_new_modes = $wpdb->get_var( 'SELECT @@SESSION.sql_mode;' );
    287         $this->assertEquals( implode( ',', $new_modes ), $check_new_modes );
     290        $this->assertEqualSets( $expected_modes, explode( ',', $check_new_modes ) );
    288291
    289292        $wpdb->set_sql_mode( explode( ',', $current_modes ) );
     
    294297     * @ticket 26847
    295298     */
    296     public function test_set_incompatible_sql_mode() {
     299    function test_set_incompatible_sql_mode() {
    297300        global $wpdb;
    298301
     
    302305        $wpdb->set_sql_mode( $new_modes );
    303306        $check_new_modes = $wpdb->get_var( 'SELECT @@SESSION.sql_mode;' );
    304         $this->assertFalse( in_array( 'NO_ZERO_DATE', explode( ',', $check_new_modes ) ) );
     307        $this->assertNotContains( 'NO_ZERO_DATE', explode( ',', $check_new_modes ) );
    305308
    306309        $wpdb->set_sql_mode( explode( ',', $current_modes ) );
     
    311314     * @ticket 26847
    312315     */
    313     public function test_set_allowed_incompatible_sql_mode() {
     316    function test_set_allowed_incompatible_sql_mode() {
    314317        global $wpdb;
    315318
     
    323326
    324327        $check_new_modes = $wpdb->get_var( 'SELECT @@SESSION.sql_mode;' );
    325         $this->assertTrue( in_array( 'NO_ZERO_DATE', explode( ',', $check_new_modes ) ) );
     328        $this->assertContains( 'NO_ZERO_DATE', explode( ',', $check_new_modes ) );
    326329
    327330        $wpdb->set_sql_mode( explode( ',', $current_modes ) );
     
    338341        unset( $modes[ $pos ] );
    339342        return $modes;
     343    }
     344
     345    /**
     346     * @ticket 21212
     347     */
     348    function test_set_sql_mode_strict() {
     349        global $wpdb;
     350        $wpdb->set_sql_mode();
     351        $sql_modes = $wpdb->get_var( 'SELECT @@SESSION.sql_mode;' );
     352        $this->assertContains( 'STRICT_ALL_TABLES', explode( ',', $sql_modes ) );
     353    }
     354
     355    /**
     356     * @ticket 21212
     357     */
     358    function test_strict_mode_numeric_strings() {
     359        global $wpdb;
     360        $post_id = $this->factory->post->create();
     361        $wpdb->update( $wpdb->posts, array( 'post_parent' => 4 ), array( 'ID' => $post_id ), array( '%s' ) );
     362        $this->assertContains( "`post_parent` = '4'", $wpdb->last_query );
     363        $this->assertEmpty( $wpdb->last_error );
     364    }
     365
     366    /**
     367     * @ticket 21212
     368     */
     369    function test_strict_mode_numeric_strings_using_query() {
     370        global $wpdb;
     371        $post_id = $this->factory->post->create();
     372        $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET post_parent = %s WHERE ID = %s", '4', $post_id ) );
     373        $this->assertEmpty( $wpdb->last_error );
     374    }
     375
     376    /**
     377     * @ticket 21212
     378     */
     379    function test_strict_mode_nan() {
     380        global $wpdb;
     381        $post_id = $this->factory->post->create();
     382        $suppress = $wpdb->suppress_errors( true );
     383        $wpdb->update( $wpdb->posts, array( 'post_parent' => 'foo' ), array( 'ID' => $post_id ), array( '%s' ) );
     384        $this->assertContains( "`post_parent` = 'foo'", $wpdb->last_query );
     385        $this->assertContains( 'Incorrect integer value', $wpdb->last_error );
     386        $wpdb->suppress_errors( $suppress );
     387    }
     388
     389    /**
     390     * @ticket 21212
     391     */
     392    function test_strict_mode_nan_using_query() {
     393        global $wpdb;
     394        $post_id = $this->factory->post->create();
     395        $suppress = $wpdb->suppress_errors( true );
     396        $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET post_parent = %s WHERE ID = %s", 'foo', $post_id ) );
     397        $this->assertContains( 'Incorrect integer value', $wpdb->last_error );
     398        $wpdb->suppress_errors( $suppress );
     399    }
     400
     401    /**
     402     * @ticket 21212
     403     */
     404    function test_strict_mode_number_start_of_string() {
     405        global $wpdb;
     406        $post_id = $this->factory->post->create();
     407        $suppress = $wpdb->suppress_errors( true );
     408        $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET post_parent = %s WHERE ID = %s", '4foo', $post_id ) );
     409        $this->assertContains( "Data truncated for column 'post_parent'", $wpdb->last_error );
     410        $wpdb->suppress_errors( $suppress );
     411    }
     412
     413    /**
     414     * @ticket 21212
     415     */
     416    function test_strict_mode_booleans_true() {
     417        global $wpdb;
     418        $user_id = $this->factory->user->create();
     419        $wpdb->query( "UPDATE $wpdb->users SET user_status = true WHERE ID = $user_id" );
     420        $this->assertEmpty( $wpdb->last_error );
     421        $user = get_userdata( $user_id );
     422        $this->assertSame( '1', $user->user_status );
     423    }
     424
     425    /**
     426     * @ticket 21212
     427     */
     428    function test_strict_mode_booleans_false() {
     429        global $wpdb;
     430        $user_id = $this->factory->user->create();
     431        $wpdb->query( "UPDATE $wpdb->users SET user_status = false WHERE ID = $user_id" );
     432        $this->assertEmpty( $wpdb->last_error );
     433        $user = get_userdata( $user_id );
     434        $this->assertEquals( '0', $user->user_status );
     435    }
     436
     437    /**
     438     * @ticket 21212
     439     */
     440    function test_strict_mode_zero_date_is_valid() {
     441        global $wpdb;
     442        $user_id = $this->factory->user->create();
     443        $wpdb->query( "UPDATE $wpdb->users SET user_registered = '0000-00-00' WHERE ID = $user_id" );
     444        $this->assertEmpty( $wpdb->last_error );
     445        $user = get_userdata( $user_id );
     446        $this->assertEquals( '0000-00-00 00:00:00', $user->user_registered );
     447    }
     448
     449    /**
     450     * @ticket 21212
     451     */
     452    function test_strict_mode_zero_datetime_is_valid() {
     453        global $wpdb;
     454        $user_id = $this->factory->user->create();
     455        $wpdb->query( "UPDATE $wpdb->users SET user_registered = '0000-00-00 00:00:00' WHERE ID = $user_id" );
     456        $this->assertEmpty( $wpdb->last_error );
     457        $user = get_userdata( $user_id );
     458        $this->assertEquals( '0000-00-00 00:00:00', $user->user_registered );
     459    }
     460
     461    /**
     462     * @ticket 21212
     463     */
     464    function test_strict_mode_invalid_dates_are_invalid() {
     465        global $wpdb;
     466        $user_id = $this->factory->user->create();
     467        $suppress = $wpdb->suppress_errors( true );
     468        $wpdb->query( "UPDATE $wpdb->users SET user_registered = '2014-02-29 00:00:00' WHERE ID = $user_id" );
     469        $this->assertContains( 'Incorrect datetime value', $wpdb->last_error );
     470        $wpdb->suppress_errors( $suppress );
     471    }
     472
     473    /**
     474     * @ticket 21212
     475     */
     476    function test_strict_mode_nulls_are_invalid() {
     477        global $wpdb;
     478        $user_id = $this->factory->user->create();
     479        $suppress = $wpdb->suppress_errors( true );
     480        $wpdb->query( "UPDATE $wpdb->users SET user_nicename = NULL WHERE ID = $user_id" );
     481        $this->assertContains( 'cannot be null', $wpdb->last_error );
     482        $wpdb->suppress_errors( $suppress );
    340483    }
    341484
     
    767910
    768911    /**
    769      * @ ticket 21212
     912     * @ticket 21212
    770913     */
    771914    function test_pre_get_col_charset_filter() {
Note: See TracChangeset for help on using the changeset viewer.