Skip to content

Commit f4152e6

Browse files
Sergey Glukhovdahlerlend
authored andcommitted
Bug#27660560 RECENT REGRESSION: CRASH IN CHECK_COVERING_PREFIX_KEYS.
There are three problems in Item_func_like::check_covering_prefix_keys: -Missing check for null value after val_str() call, added. -Missing chech for THD::error after val_str() call, added. -Calculation of the const part length of the wild pattern. It was calculated using prefix_length * wild_str->charset()->mbmaxlen, where prefix_length is number of characters. Then it compared with KEY_PART_INF::length mesured in bytes. It does not work properly for BLOB fields if wild_str->charset()->mbmaxlen is bigger than 1. Now prefix_length is passed to TABLE::update_covering_prefix_keys() and KEY_PART_INF::length is divided by mbmaxlen of the Field chararcter set.
1 parent 2b2f28d commit f4152e6

File tree

5 files changed

+72
-17
lines changed

5 files changed

+72
-17
lines changed

mysql-test/r/func_prefix_key.result

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,3 +776,32 @@ id select_type table partitions type possible_keys key key_len ref rows filtered
776776
Warnings:
777777
Note 1003 /* select#1 */ select count(0) AS `COUNT(*)` from `test`.`t1` where (`test`.`t1`.`b` like 'aaaa')
778778
DROP TABLE t1;
779+
#
780+
# Bug#27660560 RECENT REGRESSION: CRASH IN CHECK_COVERING_PREFIX_KEYS.
781+
#
782+
CREATE TABLE t1(f1 BLOB, KEY(f1(1))) ENGINE=INNODB;
783+
INSERT INTO t1 VALUES ('ccc'), ('aa');
784+
ANALYZE TABLE t1;
785+
Table Op Msg_type Msg_text
786+
test.t1 analyze status OK
787+
SELECT (f1 LIKE null) from t1;
788+
(f1 LIKE null)
789+
NULL
790+
NULL
791+
SELECT 1 FROM t1 WHERE f1 NOT LIKE json_merge('' ,'+' );
792+
ERROR 22032: Invalid JSON text in argument 1 to function json_merge_preserve: "The document is empty." at position 0.
793+
SELECT 1 FROM t1 WHERE f1 LIKE json_contains('key2' ,'key4' );
794+
ERROR 22032: Invalid JSON text in argument 1 to function json_contains: "Invalid value." at position 0.
795+
SELECT 1 FROM t1 WHERE f1 LIKE json_depth(null);
796+
1
797+
EXPLAIN SELECT (f1 LIKE null) from t1;
798+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
799+
1 SIMPLE t1 NULL index NULL f1 4 NULL 2 100.00 Using index
800+
Warnings:
801+
Note 1003 /* select#1 */ select (`test`.`t1`.`f1` like NULL) AS `(f1 LIKE null)` from `test`.`t1`
802+
EXPLAIN SELECT (f1 LIKE null) from t1 WHERE f1 LIKE 'a%';
803+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
804+
1 SIMPLE t1 NULL range f1 f1 4 NULL 1 100.00 Using where; Using index
805+
Warnings:
806+
Note 1003 /* select#1 */ select (`test`.`t1`.`f1` like NULL) AS `(f1 LIKE null)` from `test`.`t1` where (`test`.`t1`.`f1` like 'a%')
807+
DROP TABLE t1;

mysql-test/t/func_prefix_key.test

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,24 @@ ANALYZE TABLE t1;
9090
--echo # Index access is used since key is covering.
9191
EXPLAIN SELECT COUNT(*) FROM t1 WHERE b like 'aaaa';
9292
DROP TABLE t1;
93+
94+
--echo #
95+
--echo # Bug#27660560 RECENT REGRESSION: CRASH IN CHECK_COVERING_PREFIX_KEYS.
96+
--echo #
97+
98+
CREATE TABLE t1(f1 BLOB, KEY(f1(1))) ENGINE=INNODB;
99+
INSERT INTO t1 VALUES ('ccc'), ('aa');
100+
ANALYZE TABLE t1;
101+
102+
SELECT (f1 LIKE null) from t1;
103+
--error ER_INVALID_JSON_TEXT_IN_PARAM
104+
SELECT 1 FROM t1 WHERE f1 NOT LIKE json_merge('' ,'+' );
105+
--error ER_INVALID_JSON_TEXT_IN_PARAM
106+
SELECT 1 FROM t1 WHERE f1 LIKE json_contains('key2' ,'key4' );
107+
SELECT 1 FROM t1 WHERE f1 LIKE json_depth(null);
108+
109+
# Tests below should use index access.
110+
EXPLAIN SELECT (f1 LIKE null) from t1;
111+
EXPLAIN SELECT (f1 LIKE null) from t1 WHERE f1 LIKE 'a%';
112+
113+
DROP TABLE t1;

sql/item_cmpfunc.cc

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5374,34 +5374,31 @@ Item_func::optimize_type Item_func_like::select_optimize() const {
53745374
: OPTIMIZE_OP;
53755375
}
53765376

5377-
/**
5378-
The method updates covering keys depending on the
5379-
length of wild string prefix.
5380-
*/
5381-
5382-
void Item_func_like::check_covering_prefix_keys() {
5377+
bool Item_func_like::check_covering_prefix_keys(THD *thd) {
53835378
Item *first_arg = args[0]->real_item();
53845379
Item *second_arg = args[1]->real_item();
53855380
if (first_arg->type() == Item::FIELD_ITEM) {
53865381
Field *field = down_cast<Item_field *>(first_arg)->field;
53875382
Key_map covering_keys = field->get_covering_prefix_keys();
5388-
if (covering_keys.is_clear_all()) return;
5383+
if (covering_keys.is_clear_all()) return false;
53895384
if (second_arg->const_item()) {
53905385
size_t prefix_length = 0;
53915386
String *wild_str = second_arg->val_str(&cmp.value2);
5387+
if (thd->is_error()) return true;
5388+
if (second_arg->is_null()) return false;
53925389
if (my_is_prefixidx_cand(wild_str->charset(), wild_str->ptr(),
53935390
wild_str->ptr() + wild_str->length(), escape,
53945391
wild_many, &prefix_length))
5395-
field->table->update_covering_prefix_keys(
5396-
field, prefix_length * wild_str->charset()->mbmaxlen,
5397-
&covering_keys);
5392+
field->table->update_covering_prefix_keys(field, prefix_length,
5393+
&covering_keys);
53985394
else
53995395
// Not comparing to a prefix, remove all prefix indexes
54005396
field->table->covering_keys.subtract(field->part_of_prefixkey);
54015397
} else
54025398
// Second argument is not a const
54035399
field->table->covering_keys.subtract(field->part_of_prefixkey);
54045400
}
5401+
return false;
54055402
}
54065403

54075404
bool Item_func_like::fix_fields(THD *thd, Item **ref) {
@@ -5435,9 +5432,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref) {
54355432
if (eval_escape_clause(thd)) return true;
54365433
}
54375434

5438-
check_covering_prefix_keys();
5439-
5440-
return false;
5435+
return check_covering_prefix_keys(thd);
54415436
}
54425437

54435438
void Item_func_like::cleanup() {

sql/item_cmpfunc.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1863,7 +1863,16 @@ class Item_func_like final : public Item_bool_func2 {
18631863
double rows_in_table) override;
18641864

18651865
private:
1866-
void check_covering_prefix_keys();
1866+
/**
1867+
The method updates covering keys depending on the
1868+
length of wild string prefix.
1869+
1870+
@param thd Pointer to THD object.
1871+
1872+
@retval true if error happens during wild string prefix claculation,
1873+
false otherwise.
1874+
*/
1875+
bool check_covering_prefix_keys(THD *thd);
18671876
};
18681877

18691878
class Item_cond : public Item_bool_func {

sql/table.cc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7517,9 +7517,10 @@ void TABLE::update_covering_prefix_keys(Field *field, uint16 key_read_length,
75177517
for (KEY_PART_INFO *part = key_info->key_part,
75187518
*part_end = part + actual_key_parts(key_info);
75197519
part != part_end; ++part)
7520-
if ((part->key_part_flag & HA_PART_KEY_SEG) && field->eq(part->field) &&
7521-
part->length < key_read_length)
7522-
covering_keys.clear_bit(keyno);
7520+
if ((part->key_part_flag & HA_PART_KEY_SEG) && field->eq(part->field)) {
7521+
uint16 key_part_length = part->length / field->charset()->mbmaxlen;
7522+
if (key_part_length < key_read_length) covering_keys.clear_bit(keyno);
7523+
}
75237524
}
75247525
}
75257526

0 commit comments

Comments
 (0)