Make WordPress Core

Changeset 52176


Ignore:
Timestamp:
11/16/2021 02:57:53 AM (4 months ago)
Author:
hellofromTonya
Message:

WPDB: Capture error in wpdb::$last_error when insert fails instead of silently failing for invalid data or value too long.

Instead of silently failing when attempting to insert a value into a field, this commit saves the error in the wpdb::$last_error property.

Sets last_error with an error message if:

  • wpdb::query() fails for invalid data
  • wpdb::process_fields() fails to process the value(s) for the field(s) where the value could be too long or contain invalid data

Sets last_query if wpdb::query() fails for invalid data.

If __() is not available, uses non-translated error message to ensure the error is captured.

There is no change to wpdb aborting when an error occurs.

Adds tests.

Props dlt101, mnelson4, dd32, pento, hellofromTonya, davidbaumwald, sergeybiryukov, johnbillion, swissspidy, datainterlock, anandau14, anthonyeden, asif2bd, audrasjb, chaion07, dpegasusm, fpcsjames, galbaras, jdgrimes, justindocanto, kwisatz, liammitchell, lucasw89, lukecarbis, nettsite, nlpro, procodewp, psufan, richardfoley, skunkbad, travisnorthcutt, woodyhayday, zoiec.
Fixes #37267.

Location:
trunk
Files:
2 edited

Legend:

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

    r51919 r52176  
    20162016            $this->flush();
    20172017            if ( $stripped_query !== $query ) {
    2018                 $this->insert_id = 0;
     2018                $this->insert_id  = 0;
     2019                $this->last_query = $query;
     2020
     2021                if ( function_exists( '__' ) ) {
     2022                    $this->last_error = __( 'WordPress database error: Could not perform query because it contains invalid data.' );
     2023                } else {
     2024                    $this->last_error = 'WordPress database error: Could not perform query because it contains invalid data.';
     2025                }
     2026
    20192027                return false;
    20202028            }
     
    25362544
    25372545        if ( $data !== $converted_data ) {
     2546
     2547            $problem_fields = array();
     2548            foreach ( $data as $field => $value ) {
     2549                if ( $value !== $converted_data[ $field ] ) {
     2550                    $problem_fields[] = $field;
     2551                }
     2552            }
     2553
     2554            if ( 1 === count( $problem_fields ) ) {
     2555                if ( function_exists( '__' ) ) {
     2556                    /* translators: %s Database field where the error occurred. */
     2557                    $message = __( 'WordPress database error: Processing the value for the following field failed: %s. The supplied value may be too long or contains invalid data.' );
     2558                } else {
     2559                    $message = 'WordPress database error: Processing the value for the following field failed: %s. The supplied value may be too long or contains invalid data.';
     2560                }
     2561            } else {
     2562                if ( function_exists( '__' ) ) {
     2563                    /* translators: %s Database fields where the error occurred. */
     2564                    $message = __( 'WordPress database error: Processing the value for the following fields failed: %s. The supplied value may be too long or contains invalid data.' );
     2565                } else {
     2566                    $message = 'WordPress database error: Processing the value for the following fields failed: %s. The supplied value may be too long or contains invalid data.';
     2567                }
     2568            }
     2569
     2570            $this->last_error = sprintf( $message, implode( ', ', $problem_fields ) );
     2571
    25382572            return false;
    25392573        }
  • trunk/tests/phpunit/tests/db.php

    r52010 r52176  
    3434        $this->_queries = array();
    3535        add_filter( 'query', array( $this, 'query_filter' ) );
     36        self::$_wpdb->last_error     = null;
     37        $GLOBALS['wpdb']->last_error = null;
    3638    }
    3739
     
    10971099
    10981100    /**
     1101     * @dataProvider data_process_single_field_invalid_data
     1102     * @dataProvider data_process_multiple_fields_invalid_data
     1103     *
     1104     * @ticket 32315
     1105     *
     1106     * @covers wpdb::process_fields
     1107     *
     1108     * @param array  $data           Data to process.
     1109     * @param string $errored_fields Expected fields in the error message.
     1110     */
     1111    public function test_process_fields_value_too_long_for_field( array $data, $errored_fields ) {
     1112        global $wpdb;
     1113
     1114        $this->assertFalse( self::$_wpdb->process_fields( $wpdb->posts, $data, null ) );
     1115        $this->assertSame( $this->get_db_error_value_too_long( $errored_fields ), self::$_wpdb->last_error );
     1116    }
     1117
     1118    /**
     1119     * @dataProvider data_process_single_field_invalid_data
     1120     *
     1121     * @ticket 32315
     1122     *
     1123     * @covers wpdb::insert
     1124     *
     1125     * @param array  $data           Data to process.
     1126     * @param string $errored_fields Expected fields in the error message.
     1127     */
     1128    public function test_insert_value_too_long_for_field( array $data, $errored_fields ) {
     1129        global $wpdb;
     1130
     1131        $this->assertFalse( $wpdb->insert( $wpdb->posts, $data ) );
     1132        $this->assertSame( $this->get_db_error_value_too_long( $errored_fields ), $wpdb->last_error );
     1133    }
     1134
     1135    /**
     1136     * @dataProvider data_process_single_field_invalid_data
     1137     *
     1138     * @ticket 32315
     1139     *
     1140     * @covers wpdb::replace
     1141     *
     1142     * @param array  $data           Data to process.
     1143     * @param string $errored_fields Expected fields in the error message.
     1144     */
     1145    public function test_replace_value_too_long_for_field( array $data, $errored_fields ) {
     1146        global $wpdb;
     1147
     1148        $this->assertFalse( $wpdb->replace( $wpdb->posts, $data ) );
     1149        $this->assertSame( $this->get_db_error_value_too_long( $errored_fields ), $wpdb->last_error );
     1150    }
     1151
     1152    /**
     1153     * @dataProvider data_process_single_field_invalid_data
     1154     *
     1155     * @ticket 32315
     1156     *
     1157     * @covers wpdb::update
     1158     *
     1159     * @param array  $data           Data to process.
     1160     * @param string $errored_fields Expected fields in the error message.
     1161     */
     1162    public function test_update_value_too_long_for_field( array $data, $errored_fields ) {
     1163        global $wpdb;
     1164
     1165        $this->assertFalse( $wpdb->update( $wpdb->posts, $data, array() ) );
     1166        $this->assertSame( $this->get_db_error_value_too_long( $errored_fields ), $wpdb->last_error );
     1167    }
     1168
     1169    /**
     1170     * @dataProvider data_process_single_field_invalid_data
     1171     *
     1172     * @ticket 32315
     1173     *
     1174     * @covers wpdb::delete
     1175     *
     1176     * @param array  $data           Data to process.
     1177     * @param string $errored_fields Expected fields in the error message.
     1178     */
     1179    public function test_delete_value_too_long_for_field( array $data, $errored_fields ) {
     1180        global $wpdb;
     1181
     1182        $this->assertFalse( $wpdb->delete( $wpdb->posts, $data, array() ) );
     1183        $this->assertSame( $this->get_db_error_value_too_long( $errored_fields ), $wpdb->last_error );
     1184    }
     1185
     1186    /**
     1187     * Assert the error message matches the fields.
     1188     *
     1189     * @param string $errored_fields Expected fields in the error message.
     1190     */
     1191    private function get_db_error_value_too_long( $errored_fields ) {
     1192        return sprintf(
     1193            'WordPress database error: Processing the value for the following field%s failed: %s. ' .
     1194            'The supplied value may be too long or contains invalid data.',
     1195            str_contains( $errored_fields, ', ' ) ? 's' : '',
     1196            $errored_fields
     1197        );
     1198    }
     1199
     1200    /**
     1201     * Data provider.
     1202     *
     1203     * @return array
     1204     */
     1205    public function data_process_single_field_invalid_data() {
     1206        return array(
     1207            'too long'      => array(
     1208                'data'           => array( 'post_status' => str_repeat( 'a', 21 ) ),
     1209                'errored_fields' => 'post_status',
     1210            ),
     1211            'invalid chars' => array(
     1212                'data'           => array( 'post_status' => "\xF5" ),
     1213                'errored_fields' => 'post_status',
     1214            ),
     1215        );
     1216    }
     1217
     1218    /**
     1219     * Data provider.
     1220     *
     1221     * @return array
     1222     */
     1223    public function data_process_multiple_fields_invalid_data() {
     1224        return array(
     1225            'too long'      => array(
     1226                'data'           => array(
     1227                    'post_status'  => str_repeat( 'a', 21 ),
     1228                    'post_content' => "\xF5",
     1229                ),
     1230                'errored_fields' => 'post_status, post_content',
     1231            ),
     1232            'invalid chars' => array(
     1233                'data'           => array(
     1234                    'post_status' => "\xF5",
     1235                    'post_name'   => str_repeat( "\xF5", 21 ),
     1236                ),
     1237                'errored_fields' => 'post_status, post_name',
     1238            ),
     1239        );
     1240    }
     1241
     1242    /**
     1243     * @ticket 32315
     1244     */
     1245    public function test_query_value_contains_invalid_chars() {
     1246        global $wpdb;
     1247
     1248        $this->assertFalse(
     1249            $wpdb->query( "INSERT INTO {$wpdb->posts} (post_status) VALUES ('\xF5')" )
     1250        );
     1251
     1252        $this->assertSame(
     1253            'WordPress database error: Could not perform query because it contains invalid data.',
     1254            $wpdb->last_error
     1255        );
     1256    }
     1257
     1258    /**
    10991259     * @ticket 15158
    11001260     */
Note: See TracChangeset for help on using the changeset viewer.