From 6260e12b99f9d3276a69c115ea3b922bea87a50e Mon Sep 17 00:00:00 2001 From: "cmiller@zippy.cornsilk.net" <> Date: Wed, 8 Nov 2006 10:11:02 -0500 Subject: [PATCH] Bug#10963: LEFT/RIGHT/SUBSTR/.. string functions returns wrong result \ on large length Problem: Most (all) of the numeric inputs were being coerced into int (32 bit) sized variables. Works OK for sane inputs; any input larger than 2^32 (or 2^31 for signed vars) exihibited predictable wrapping behavior (up to about 10^18) and then started having really strange behaviour past that point (since the conversion to 64 bit int from the DECIMAL type can do weird things on out of range numbers). Solution: 1) Add many tests. 2) Convert input from (u)long type to (u)longlong. 3) Do (sometimes multiple) sanity checks on input, keeping in mind that sometimes a negative longlong is not a negative longlong (if the unsigned_flag is set). 4) Emulate existing behavior w/rt negative and "small" out-of-bounds values. --- mysql-test/r/func_str.result | 755 +++++++++++++++++++++++++++++++++++ mysql-test/t/func_str.test | 189 +++++++++ sql/item_func.cc | 20 +- sql/item_strfunc.cc | 187 ++++++--- 4 files changed, 1096 insertions(+), 55 deletions(-) diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index c2c12f8d291..e5342eec54f 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -1148,4 +1148,759 @@ id select_type table type possible_keys key key_len ref rows Extra Warnings: Note 1003 select `test`.`t1`.`code` AS `code`,`test`.`t2`.`id` AS `id` from `test`.`t1` join `test`.`t2` where ((`test`.`t1`.`code` = _latin1'a12') and (length(`test`.`t1`.`code`) = 5)) DROP TABLE t1,t2; +select locate('he','hello',-2); +locate('he','hello',-2) +0 +select locate('lo','hello',-4294967295); +locate('lo','hello',-4294967295) +0 +select locate('lo','hello',4294967295); +locate('lo','hello',4294967295) +0 +select locate('lo','hello',-4294967296); +locate('lo','hello',-4294967296) +0 +select locate('lo','hello',4294967296); +locate('lo','hello',4294967296) +0 +select locate('lo','hello',-4294967297); +locate('lo','hello',-4294967297) +0 +select locate('lo','hello',4294967297); +locate('lo','hello',4294967297) +0 +select locate('lo','hello',-18446744073709551615); +locate('lo','hello',-18446744073709551615) +0 +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +select locate('lo','hello',18446744073709551615); +locate('lo','hello',18446744073709551615) +0 +select locate('lo','hello',-18446744073709551616); +locate('lo','hello',-18446744073709551616) +0 +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +select locate('lo','hello',18446744073709551616); +locate('lo','hello',18446744073709551616) +0 +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +select locate('lo','hello',-18446744073709551617); +locate('lo','hello',-18446744073709551617) +0 +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +select locate('lo','hello',18446744073709551617); +locate('lo','hello',18446744073709551617) +0 +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +select left('hello', 10); +left('hello', 10) +hello +select left('hello', 0); +left('hello', 0) + +select left('hello', -1); +left('hello', -1) + +select left('hello', -4294967295); +left('hello', -4294967295) + +select left('hello', 4294967295); +left('hello', 4294967295) +hello +select left('hello', -4294967296); +left('hello', -4294967296) + +select left('hello', 4294967296); +left('hello', 4294967296) +hello +select left('hello', -4294967297); +left('hello', -4294967297) + +select left('hello', 4294967297); +left('hello', 4294967297) +hello +select left('hello', -18446744073709551615); +left('hello', -18446744073709551615) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select left('hello', 18446744073709551615); +left('hello', 18446744073709551615) +hello +select left('hello', -18446744073709551616); +left('hello', -18446744073709551616) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select left('hello', 18446744073709551616); +left('hello', 18446744073709551616) +hello +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select left('hello', -18446744073709551617); +left('hello', -18446744073709551617) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select left('hello', 18446744073709551617); +left('hello', 18446744073709551617) +hello +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select right('hello', 10); +right('hello', 10) +hello +select right('hello', 0); +right('hello', 0) + +select right('hello', -1); +right('hello', -1) + +select right('hello', -4294967295); +right('hello', -4294967295) + +select right('hello', 4294967295); +right('hello', 4294967295) +hello +select right('hello', -4294967296); +right('hello', -4294967296) + +select right('hello', 4294967296); +right('hello', 4294967296) +hello +select right('hello', -4294967297); +right('hello', -4294967297) + +select right('hello', 4294967297); +right('hello', 4294967297) +hello +select right('hello', -18446744073709551615); +right('hello', -18446744073709551615) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select right('hello', 18446744073709551615); +right('hello', 18446744073709551615) +hello +select right('hello', -18446744073709551616); +right('hello', -18446744073709551616) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select right('hello', 18446744073709551616); +right('hello', 18446744073709551616) +hello +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select right('hello', -18446744073709551617); +right('hello', -18446744073709551617) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select right('hello', 18446744073709551617); +right('hello', 18446744073709551617) +hello +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select substring('hello', 2, -1); +substring('hello', 2, -1) + +select substring('hello', -1, 1); +substring('hello', -1, 1) +o +select substring('hello', -2, 1); +substring('hello', -2, 1) +l +select substring('hello', -4294967295, 1); +substring('hello', -4294967295, 1) + +select substring('hello', 4294967295, 1); +substring('hello', 4294967295, 1) + +select substring('hello', -4294967296, 1); +substring('hello', -4294967296, 1) + +select substring('hello', 4294967296, 1); +substring('hello', 4294967296, 1) + +select substring('hello', -4294967297, 1); +substring('hello', -4294967297, 1) + +select substring('hello', 4294967297, 1); +substring('hello', 4294967297, 1) + +select substring('hello', -18446744073709551615, 1); +substring('hello', -18446744073709551615, 1) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select substring('hello', 18446744073709551615, 1); +substring('hello', 18446744073709551615, 1) + +select substring('hello', -18446744073709551616, 1); +substring('hello', -18446744073709551616, 1) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select substring('hello', 18446744073709551616, 1); +substring('hello', 18446744073709551616, 1) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select substring('hello', -18446744073709551617, 1); +substring('hello', -18446744073709551617, 1) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select substring('hello', 18446744073709551617, 1); +substring('hello', 18446744073709551617, 1) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select substring('hello', 1, -1); +substring('hello', 1, -1) + +select substring('hello', 1, -4294967295); +substring('hello', 1, -4294967295) + +select substring('hello', 1, 4294967295); +substring('hello', 1, 4294967295) +hello +select substring('hello', 1, -4294967296); +substring('hello', 1, -4294967296) + +select substring('hello', 1, 4294967296); +substring('hello', 1, 4294967296) +hello +select substring('hello', 1, -4294967297); +substring('hello', 1, -4294967297) + +select substring('hello', 1, 4294967297); +substring('hello', 1, 4294967297) +hello +select substring('hello', 1, -18446744073709551615); +substring('hello', 1, -18446744073709551615) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select substring('hello', 1, 18446744073709551615); +substring('hello', 1, 18446744073709551615) +hello +select substring('hello', 1, -18446744073709551616); +substring('hello', 1, -18446744073709551616) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select substring('hello', 1, 18446744073709551616); +substring('hello', 1, 18446744073709551616) +hello +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select substring('hello', 1, -18446744073709551617); +substring('hello', 1, -18446744073709551617) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select substring('hello', 1, 18446744073709551617); +substring('hello', 1, 18446744073709551617) +hello +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select substring('hello', -1, -1); +substring('hello', -1, -1) + +select substring('hello', -4294967295, -4294967295); +substring('hello', -4294967295, -4294967295) + +select substring('hello', 4294967295, 4294967295); +substring('hello', 4294967295, 4294967295) + +select substring('hello', -4294967296, -4294967296); +substring('hello', -4294967296, -4294967296) + +select substring('hello', 4294967296, 4294967296); +substring('hello', 4294967296, 4294967296) + +select substring('hello', -4294967297, -4294967297); +substring('hello', -4294967297, -4294967297) + +select substring('hello', 4294967297, 4294967297); +substring('hello', 4294967297, 4294967297) + +select substring('hello', -18446744073709551615, -18446744073709551615); +substring('hello', -18446744073709551615, -18446744073709551615) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select substring('hello', 18446744073709551615, 18446744073709551615); +substring('hello', 18446744073709551615, 18446744073709551615) + +select substring('hello', -18446744073709551616, -18446744073709551616); +substring('hello', -18446744073709551616, -18446744073709551616) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select substring('hello', 18446744073709551616, 18446744073709551616); +substring('hello', 18446744073709551616, 18446744073709551616) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select substring('hello', -18446744073709551617, -18446744073709551617); +substring('hello', -18446744073709551617, -18446744073709551617) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select substring('hello', 18446744073709551617, 18446744073709551617); +substring('hello', 18446744073709551617, 18446744073709551617) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select insert('hello', -1, 1, 'hi'); +insert('hello', -1, 1, 'hi') +hello +select insert('hello', -4294967295, 1, 'hi'); +insert('hello', -4294967295, 1, 'hi') +hello +select insert('hello', 4294967295, 1, 'hi'); +insert('hello', 4294967295, 1, 'hi') +hello +select insert('hello', -4294967296, 1, 'hi'); +insert('hello', -4294967296, 1, 'hi') +hello +select insert('hello', 4294967296, 1, 'hi'); +insert('hello', 4294967296, 1, 'hi') +hello +select insert('hello', -4294967297, 1, 'hi'); +insert('hello', -4294967297, 1, 'hi') +hello +select insert('hello', 4294967297, 1, 'hi'); +insert('hello', 4294967297, 1, 'hi') +hello +select insert('hello', -18446744073709551615, 1, 'hi'); +insert('hello', -18446744073709551615, 1, 'hi') +hello +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +select insert('hello', 18446744073709551615, 1, 'hi'); +insert('hello', 18446744073709551615, 1, 'hi') +hello +select insert('hello', -18446744073709551616, 1, 'hi'); +insert('hello', -18446744073709551616, 1, 'hi') +hello +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +select insert('hello', 18446744073709551616, 1, 'hi'); +insert('hello', 18446744073709551616, 1, 'hi') +hello +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +select insert('hello', -18446744073709551617, 1, 'hi'); +insert('hello', -18446744073709551617, 1, 'hi') +hello +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +select insert('hello', 18446744073709551617, 1, 'hi'); +insert('hello', 18446744073709551617, 1, 'hi') +hello +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +select insert('hello', 1, -1, 'hi'); +insert('hello', 1, -1, 'hi') +hi +select insert('hello', 1, -4294967295, 'hi'); +insert('hello', 1, -4294967295, 'hi') +hi +select insert('hello', 1, 4294967295, 'hi'); +insert('hello', 1, 4294967295, 'hi') +hi +select insert('hello', 1, -4294967296, 'hi'); +insert('hello', 1, -4294967296, 'hi') +hi +select insert('hello', 1, 4294967296, 'hi'); +insert('hello', 1, 4294967296, 'hi') +hi +select insert('hello', 1, -4294967297, 'hi'); +insert('hello', 1, -4294967297, 'hi') +hi +select insert('hello', 1, 4294967297, 'hi'); +insert('hello', 1, 4294967297, 'hi') +hi +select insert('hello', 1, -18446744073709551615, 'hi'); +insert('hello', 1, -18446744073709551615, 'hi') +hi +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +select insert('hello', 1, 18446744073709551615, 'hi'); +insert('hello', 1, 18446744073709551615, 'hi') +hi +select insert('hello', 1, -18446744073709551616, 'hi'); +insert('hello', 1, -18446744073709551616, 'hi') +hi +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +select insert('hello', 1, 18446744073709551616, 'hi'); +insert('hello', 1, 18446744073709551616, 'hi') +hi +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +select insert('hello', 1, -18446744073709551617, 'hi'); +insert('hello', 1, -18446744073709551617, 'hi') +hi +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +select insert('hello', 1, 18446744073709551617, 'hi'); +insert('hello', 1, 18446744073709551617, 'hi') +hi +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +select insert('hello', -1, -1, 'hi'); +insert('hello', -1, -1, 'hi') +hello +select insert('hello', -4294967295, -4294967295, 'hi'); +insert('hello', -4294967295, -4294967295, 'hi') +hello +select insert('hello', 4294967295, 4294967295, 'hi'); +insert('hello', 4294967295, 4294967295, 'hi') +hello +select insert('hello', -4294967296, -4294967296, 'hi'); +insert('hello', -4294967296, -4294967296, 'hi') +hello +select insert('hello', 4294967296, 4294967296, 'hi'); +insert('hello', 4294967296, 4294967296, 'hi') +hello +select insert('hello', -4294967297, -4294967297, 'hi'); +insert('hello', -4294967297, -4294967297, 'hi') +hello +select insert('hello', 4294967297, 4294967297, 'hi'); +insert('hello', 4294967297, 4294967297, 'hi') +hello +select insert('hello', -18446744073709551615, -18446744073709551615, 'hi'); +insert('hello', -18446744073709551615, -18446744073709551615, 'hi') +hello +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select insert('hello', 18446744073709551615, 18446744073709551615, 'hi'); +insert('hello', 18446744073709551615, 18446744073709551615, 'hi') +hello +select insert('hello', -18446744073709551616, -18446744073709551616, 'hi'); +insert('hello', -18446744073709551616, -18446744073709551616, 'hi') +hello +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select insert('hello', 18446744073709551616, 18446744073709551616, 'hi'); +insert('hello', 18446744073709551616, 18446744073709551616, 'hi') +hello +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select insert('hello', -18446744073709551617, -18446744073709551617, 'hi'); +insert('hello', -18446744073709551617, -18446744073709551617, 'hi') +hello +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select insert('hello', 18446744073709551617, 18446744073709551617, 'hi'); +insert('hello', 18446744073709551617, 18446744073709551617, 'hi') +hello +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select repeat('hello', -1); +repeat('hello', -1) + +select repeat('hello', -4294967295); +repeat('hello', -4294967295) + +select repeat('hello', 4294967295); +repeat('hello', 4294967295) +NULL +Warnings: +Warning 1301 Result of repeat() was larger than max_allowed_packet (1048576) - truncated +select repeat('hello', -4294967296); +repeat('hello', -4294967296) + +select repeat('hello', 4294967296); +repeat('hello', 4294967296) +NULL +Warnings: +Warning 1301 Result of repeat() was larger than max_allowed_packet (1048576) - truncated +select repeat('hello', -4294967297); +repeat('hello', -4294967297) + +select repeat('hello', 4294967297); +repeat('hello', 4294967297) +NULL +Warnings: +Warning 1301 Result of repeat() was larger than max_allowed_packet (1048576) - truncated +select repeat('hello', -18446744073709551615); +repeat('hello', -18446744073709551615) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select repeat('hello', 18446744073709551615); +repeat('hello', 18446744073709551615) +NULL +Warnings: +Warning 1301 Result of repeat() was larger than max_allowed_packet (1048576) - truncated +select repeat('hello', -18446744073709551616); +repeat('hello', -18446744073709551616) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select repeat('hello', 18446744073709551616); +repeat('hello', 18446744073709551616) +NULL +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +Warning 1301 Result of repeat() was larger than max_allowed_packet (1048576) - truncated +select repeat('hello', -18446744073709551617); +repeat('hello', -18446744073709551617) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select repeat('hello', 18446744073709551617); +repeat('hello', 18446744073709551617) +NULL +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +Warning 1301 Result of repeat() was larger than max_allowed_packet (1048576) - truncated +select space(-1); +space(-1) + +select space(-4294967295); +space(-4294967295) + +select space(4294967295); +space(4294967295) +NULL +Warnings: +Warning 1301 Result of repeat() was larger than max_allowed_packet (1048576) - truncated +select space(-4294967296); +space(-4294967296) + +select space(4294967296); +space(4294967296) +NULL +Warnings: +Warning 1301 Result of repeat() was larger than max_allowed_packet (1048576) - truncated +select space(-4294967297); +space(-4294967297) + +select space(4294967297); +space(4294967297) +NULL +Warnings: +Warning 1301 Result of repeat() was larger than max_allowed_packet (1048576) - truncated +select space(-18446744073709551615); +space(-18446744073709551615) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select space(18446744073709551615); +space(18446744073709551615) +NULL +Warnings: +Warning 1301 Result of repeat() was larger than max_allowed_packet (1048576) - truncated +select space(-18446744073709551616); +space(-18446744073709551616) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select space(18446744073709551616); +space(18446744073709551616) +NULL +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +Warning 1301 Result of repeat() was larger than max_allowed_packet (1048576) - truncated +select space(-18446744073709551617); +space(-18446744073709551617) + +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select space(18446744073709551617); +space(18446744073709551617) +NULL +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +Warning 1301 Result of repeat() was larger than max_allowed_packet (1048576) - truncated +select rpad('hello', -1, '1'); +rpad('hello', -1, '1') +NULL +select rpad('hello', -4294967295, '1'); +rpad('hello', -4294967295, '1') +NULL +select rpad('hello', 4294967295, '1'); +rpad('hello', 4294967295, '1') +NULL +Warnings: +Warning 1301 Result of rpad() was larger than max_allowed_packet (1048576) - truncated +select rpad('hello', -4294967296, '1'); +rpad('hello', -4294967296, '1') +NULL +select rpad('hello', 4294967296, '1'); +rpad('hello', 4294967296, '1') +NULL +Warnings: +Warning 1301 Result of rpad() was larger than max_allowed_packet (1048576) - truncated +select rpad('hello', -4294967297, '1'); +rpad('hello', -4294967297, '1') +NULL +select rpad('hello', 4294967297, '1'); +rpad('hello', 4294967297, '1') +NULL +Warnings: +Warning 1301 Result of rpad() was larger than max_allowed_packet (1048576) - truncated +select rpad('hello', -18446744073709551615, '1'); +rpad('hello', -18446744073709551615, '1') +NULL +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select rpad('hello', 18446744073709551615, '1'); +rpad('hello', 18446744073709551615, '1') +NULL +Warnings: +Warning 1301 Result of rpad() was larger than max_allowed_packet (1048576) - truncated +select rpad('hello', -18446744073709551616, '1'); +rpad('hello', -18446744073709551616, '1') +NULL +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select rpad('hello', 18446744073709551616, '1'); +rpad('hello', 18446744073709551616, '1') +NULL +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +Warning 1301 Result of rpad() was larger than max_allowed_packet (1048576) - truncated +select rpad('hello', -18446744073709551617, '1'); +rpad('hello', -18446744073709551617, '1') +NULL +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select rpad('hello', 18446744073709551617, '1'); +rpad('hello', 18446744073709551617, '1') +NULL +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +Warning 1301 Result of rpad() was larger than max_allowed_packet (1048576) - truncated +select lpad('hello', -1, '1'); +lpad('hello', -1, '1') +NULL +select lpad('hello', -4294967295, '1'); +lpad('hello', -4294967295, '1') +NULL +select lpad('hello', 4294967295, '1'); +lpad('hello', 4294967295, '1') +NULL +Warnings: +Warning 1301 Result of lpad() was larger than max_allowed_packet (1048576) - truncated +select lpad('hello', -4294967296, '1'); +lpad('hello', -4294967296, '1') +NULL +select lpad('hello', 4294967296, '1'); +lpad('hello', 4294967296, '1') +NULL +Warnings: +Warning 1301 Result of lpad() was larger than max_allowed_packet (1048576) - truncated +select lpad('hello', -4294967297, '1'); +lpad('hello', -4294967297, '1') +NULL +select lpad('hello', 4294967297, '1'); +lpad('hello', 4294967297, '1') +NULL +Warnings: +Warning 1301 Result of lpad() was larger than max_allowed_packet (1048576) - truncated +select lpad('hello', -18446744073709551615, '1'); +lpad('hello', -18446744073709551615, '1') +NULL +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select lpad('hello', 18446744073709551615, '1'); +lpad('hello', 18446744073709551615, '1') +NULL +Warnings: +Warning 1301 Result of lpad() was larger than max_allowed_packet (1048576) - truncated +select lpad('hello', -18446744073709551616, '1'); +lpad('hello', -18446744073709551616, '1') +NULL +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select lpad('hello', 18446744073709551616, '1'); +lpad('hello', 18446744073709551616, '1') +NULL +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +Warning 1301 Result of lpad() was larger than max_allowed_packet (1048576) - truncated +select lpad('hello', -18446744073709551617, '1'); +lpad('hello', -18446744073709551617, '1') +NULL +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +select lpad('hello', 18446744073709551617, '1'); +lpad('hello', 18446744073709551617, '1') +NULL +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +Error 1292 Truncated incorrect DECIMAL value: '' +Warning 1301 Result of lpad() was larger than max_allowed_packet (1048576) - truncated End of 5.0 tests diff --git a/mysql-test/t/func_str.test b/mysql-test/t/func_str.test index 45415882ac7..9622de96143 100644 --- a/mysql-test/t/func_str.test +++ b/mysql-test/t/func_str.test @@ -780,4 +780,193 @@ SELECT * FROM t1 INNER JOIN t2 ON code=id DROP TABLE t1,t2; +# +# Bug #10963 +# 4294967296 18446744073709551616 + +select locate('he','hello',-2); +select locate('lo','hello',-4294967295); +select locate('lo','hello',4294967295); +select locate('lo','hello',-4294967296); +select locate('lo','hello',4294967296); +select locate('lo','hello',-4294967297); +select locate('lo','hello',4294967297); +select locate('lo','hello',-18446744073709551615); +select locate('lo','hello',18446744073709551615); +select locate('lo','hello',-18446744073709551616); +select locate('lo','hello',18446744073709551616); +select locate('lo','hello',-18446744073709551617); +select locate('lo','hello',18446744073709551617); + +select left('hello', 10); +select left('hello', 0); +select left('hello', -1); +select left('hello', -4294967295); +select left('hello', 4294967295); +select left('hello', -4294967296); +select left('hello', 4294967296); +select left('hello', -4294967297); +select left('hello', 4294967297); +select left('hello', -18446744073709551615); +select left('hello', 18446744073709551615); +select left('hello', -18446744073709551616); +select left('hello', 18446744073709551616); +select left('hello', -18446744073709551617); +select left('hello', 18446744073709551617); + +select right('hello', 10); +select right('hello', 0); +select right('hello', -1); +select right('hello', -4294967295); +select right('hello', 4294967295); +select right('hello', -4294967296); +select right('hello', 4294967296); +select right('hello', -4294967297); +select right('hello', 4294967297); +select right('hello', -18446744073709551615); +select right('hello', 18446744073709551615); +select right('hello', -18446744073709551616); +select right('hello', 18446744073709551616); +select right('hello', -18446744073709551617); +select right('hello', 18446744073709551617); + +select substring('hello', 2, -1); + +select substring('hello', -1, 1); +select substring('hello', -2, 1); +select substring('hello', -4294967295, 1); +select substring('hello', 4294967295, 1); +select substring('hello', -4294967296, 1); +select substring('hello', 4294967296, 1); +select substring('hello', -4294967297, 1); +select substring('hello', 4294967297, 1); +select substring('hello', -18446744073709551615, 1); +select substring('hello', 18446744073709551615, 1); +select substring('hello', -18446744073709551616, 1); +select substring('hello', 18446744073709551616, 1); +select substring('hello', -18446744073709551617, 1); +select substring('hello', 18446744073709551617, 1); +select substring('hello', 1, -1); +select substring('hello', 1, -4294967295); +select substring('hello', 1, 4294967295); +select substring('hello', 1, -4294967296); +select substring('hello', 1, 4294967296); +select substring('hello', 1, -4294967297); +select substring('hello', 1, 4294967297); +select substring('hello', 1, -18446744073709551615); +select substring('hello', 1, 18446744073709551615); +select substring('hello', 1, -18446744073709551616); +select substring('hello', 1, 18446744073709551616); +select substring('hello', 1, -18446744073709551617); +select substring('hello', 1, 18446744073709551617); +select substring('hello', -1, -1); +select substring('hello', -4294967295, -4294967295); +select substring('hello', 4294967295, 4294967295); +select substring('hello', -4294967296, -4294967296); +select substring('hello', 4294967296, 4294967296); +select substring('hello', -4294967297, -4294967297); +select substring('hello', 4294967297, 4294967297); +select substring('hello', -18446744073709551615, -18446744073709551615); +select substring('hello', 18446744073709551615, 18446744073709551615); +select substring('hello', -18446744073709551616, -18446744073709551616); +select substring('hello', 18446744073709551616, 18446744073709551616); +select substring('hello', -18446744073709551617, -18446744073709551617); +select substring('hello', 18446744073709551617, 18446744073709551617); + +select insert('hello', -1, 1, 'hi'); +select insert('hello', -4294967295, 1, 'hi'); +select insert('hello', 4294967295, 1, 'hi'); +select insert('hello', -4294967296, 1, 'hi'); +select insert('hello', 4294967296, 1, 'hi'); +select insert('hello', -4294967297, 1, 'hi'); +select insert('hello', 4294967297, 1, 'hi'); +select insert('hello', -18446744073709551615, 1, 'hi'); +select insert('hello', 18446744073709551615, 1, 'hi'); +select insert('hello', -18446744073709551616, 1, 'hi'); +select insert('hello', 18446744073709551616, 1, 'hi'); +select insert('hello', -18446744073709551617, 1, 'hi'); +select insert('hello', 18446744073709551617, 1, 'hi'); +select insert('hello', 1, -1, 'hi'); +select insert('hello', 1, -4294967295, 'hi'); +select insert('hello', 1, 4294967295, 'hi'); +select insert('hello', 1, -4294967296, 'hi'); +select insert('hello', 1, 4294967296, 'hi'); +select insert('hello', 1, -4294967297, 'hi'); +select insert('hello', 1, 4294967297, 'hi'); +select insert('hello', 1, -18446744073709551615, 'hi'); +select insert('hello', 1, 18446744073709551615, 'hi'); +select insert('hello', 1, -18446744073709551616, 'hi'); +select insert('hello', 1, 18446744073709551616, 'hi'); +select insert('hello', 1, -18446744073709551617, 'hi'); +select insert('hello', 1, 18446744073709551617, 'hi'); +select insert('hello', -1, -1, 'hi'); +select insert('hello', -4294967295, -4294967295, 'hi'); +select insert('hello', 4294967295, 4294967295, 'hi'); +select insert('hello', -4294967296, -4294967296, 'hi'); +select insert('hello', 4294967296, 4294967296, 'hi'); +select insert('hello', -4294967297, -4294967297, 'hi'); +select insert('hello', 4294967297, 4294967297, 'hi'); +select insert('hello', -18446744073709551615, -18446744073709551615, 'hi'); +select insert('hello', 18446744073709551615, 18446744073709551615, 'hi'); +select insert('hello', -18446744073709551616, -18446744073709551616, 'hi'); +select insert('hello', 18446744073709551616, 18446744073709551616, 'hi'); +select insert('hello', -18446744073709551617, -18446744073709551617, 'hi'); +select insert('hello', 18446744073709551617, 18446744073709551617, 'hi'); + +select repeat('hello', -1); +select repeat('hello', -4294967295); +select repeat('hello', 4294967295); +select repeat('hello', -4294967296); +select repeat('hello', 4294967296); +select repeat('hello', -4294967297); +select repeat('hello', 4294967297); +select repeat('hello', -18446744073709551615); +select repeat('hello', 18446744073709551615); +select repeat('hello', -18446744073709551616); +select repeat('hello', 18446744073709551616); +select repeat('hello', -18446744073709551617); +select repeat('hello', 18446744073709551617); + +select space(-1); +select space(-4294967295); +select space(4294967295); +select space(-4294967296); +select space(4294967296); +select space(-4294967297); +select space(4294967297); +select space(-18446744073709551615); +select space(18446744073709551615); +select space(-18446744073709551616); +select space(18446744073709551616); +select space(-18446744073709551617); +select space(18446744073709551617); + +select rpad('hello', -1, '1'); +select rpad('hello', -4294967295, '1'); +select rpad('hello', 4294967295, '1'); +select rpad('hello', -4294967296, '1'); +select rpad('hello', 4294967296, '1'); +select rpad('hello', -4294967297, '1'); +select rpad('hello', 4294967297, '1'); +select rpad('hello', -18446744073709551615, '1'); +select rpad('hello', 18446744073709551615, '1'); +select rpad('hello', -18446744073709551616, '1'); +select rpad('hello', 18446744073709551616, '1'); +select rpad('hello', -18446744073709551617, '1'); +select rpad('hello', 18446744073709551617, '1'); + +select lpad('hello', -1, '1'); +select lpad('hello', -4294967295, '1'); +select lpad('hello', 4294967295, '1'); +select lpad('hello', -4294967296, '1'); +select lpad('hello', 4294967296, '1'); +select lpad('hello', -4294967297, '1'); +select lpad('hello', 4294967297, '1'); +select lpad('hello', -18446744073709551615, '1'); +select lpad('hello', 18446744073709551615, '1'); +select lpad('hello', -18446744073709551616, '1'); +select lpad('hello', 18446744073709551616, '1'); +select lpad('hello', -18446744073709551617, '1'); +select lpad('hello', 18446744073709551617, '1'); + --echo End of 5.0 tests diff --git a/sql/item_func.cc b/sql/item_func.cc index ca71701841b..fa953889619 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2313,21 +2313,27 @@ longlong Item_func_locate::val_int() return 0; /* purecov: inspected */ } null_value=0; - uint start=0; - uint start0=0; + /* must be longlong to avoid truncation */ + longlong start= 0; + longlong start0= 0; my_match_t match; if (arg_count == 3) { - start0= start =(uint) args[2]->val_int()-1; - start=a->charpos(start); - - if (start > a->length() || start+b->length() > a->length()) + start0= start= args[2]->val_int() - 1; + + if ((start < 0) || (start > a->length())) + return 0; + + /* start is now sufficiently valid to pass to charpos function */ + start= a->charpos(start); + + if (start + b->length() > a->length()) return 0; } if (!b->length()) // Found empty string at start - return (longlong) (start+1); + return start + 1; if (!cmp_collation.collation->coll->instr(cmp_collation.collation, a->ptr()+start, a->length()-start, diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 4f3e280fc8a..c2f16ffac10 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -951,22 +951,33 @@ String *Item_func_insert::val_str(String *str) { DBUG_ASSERT(fixed == 1); String *res,*res2; - uint start,length; + longlong start, length; /* must be longlong to avoid truncation */ null_value=0; res=args[0]->val_str(str); res2=args[3]->val_str(&tmp_value); - start=(uint) args[1]->val_int()-1; - length=(uint) args[2]->val_int(); + start= args[1]->val_int() - 1; + length= args[2]->val_int(); + if (args[0]->null_value || args[1]->null_value || args[2]->null_value || args[3]->null_value) goto null; /* purecov: inspected */ - start=res->charpos(start); - length=res->charpos(length,start); - if (start > res->length()+1) - return res; // Wrong param; skip insert - if (length > res->length()-start) - length=res->length()-start; + + if ((start < 0) || (start > res->length() + 1)) + return res; // Wrong param; skip insert + if ((length < 0) || (length > res->length() + 1)) + length= res->length() + 1; + + /* start and length are now sufficiently valid to pass to charpos function */ + start= res->charpos(start); + length= res->charpos(length, start); + + /* Re-testing with corrected params */ + if (start > res->length() + 1) + return res; // Wrong param; skip insert + if (length > res->length() - start) + length= res->length() - start; + if (res->length() - length + res2->length() > current_thd->variables.max_allowed_packet) { @@ -1039,16 +1050,21 @@ String *Item_str_conv::val_str(String *str) String *Item_func_left::val_str(String *str) { DBUG_ASSERT(fixed == 1); - String *res =args[0]->val_str(str); - long length =(long) args[1]->val_int(); + String *res= args[0]->val_str(str); + + /* must be longlong to avoid truncation */ + longlong length= args[1]->val_int(); uint char_pos; if ((null_value=(args[0]->null_value || args[1]->null_value))) return 0; - if (length <= 0) + + /* if "unsigned_flag" is set, we have a *huge* positive number. */ + if ((length <= 0) && (!args[1]->unsigned_flag)) return &my_empty_string; - if (res->length() <= (uint) length || - res->length() <= (char_pos= res->charpos(length))) + + if ((res->length() <= (ulonglong) length) || + (res->length() <= (char_pos= res->charpos(length)))) return res; tmp_value.set(*res, 0, char_pos); @@ -1080,14 +1096,18 @@ void Item_func_left::fix_length_and_dec() String *Item_func_right::val_str(String *str) { DBUG_ASSERT(fixed == 1); - String *res =args[0]->val_str(str); - long length =(long) args[1]->val_int(); + String *res= args[0]->val_str(str); + /* must be longlong to avoid truncation */ + longlong length= args[1]->val_int(); if ((null_value=(args[0]->null_value || args[1]->null_value))) return 0; /* purecov: inspected */ - if (length <= 0) + + /* if "unsigned_flag" is set, we have a *huge* positive number. */ + if ((length <= 0) && (!args[1]->unsigned_flag)) return &my_empty_string; /* purecov: inspected */ - if (res->length() <= (uint) length) + + if (res->length() <= (ulonglong) length) return res; /* purecov: inspected */ uint start=res->numchars(); @@ -1110,25 +1130,43 @@ String *Item_func_substr::val_str(String *str) { DBUG_ASSERT(fixed == 1); String *res = args[0]->val_str(str); - int32 start = (int32) args[1]->val_int(); - int32 length = arg_count == 3 ? (int32) args[2]->val_int() : INT_MAX32; - int32 tmp_length; + /* must be longlong to avoid truncation */ + longlong start= args[1]->val_int(); + /* Assumes that the maximum length of a String is < INT_MAX32. */ + /* Limit so that code sees out-of-bound value properly. */ + longlong length= arg_count == 3 ? args[2]->val_int() : INT_MAX32; + longlong tmp_length; if ((null_value=(args[0]->null_value || args[1]->null_value || (arg_count == 3 && args[2]->null_value)))) return 0; /* purecov: inspected */ - start= (int32)((start < 0) ? res->numchars() + start : start -1); - start=res->charpos(start); - length=res->charpos(length,start); - if (start < 0 || (uint) start+1 > res->length() || length <= 0) + + /* Negative length, will return empty string. */ + if ((arg_count == 3) && (length <= 0) && !args[2]->unsigned_flag) return &my_empty_string; - tmp_length=(int32) res->length()-start; - length=min(length,tmp_length); + /* Assumes that the maximum length of a String is < INT_MAX32. */ + /* Set here so that rest of code sees out-of-bound value as such. */ + if ((length <= 0) || (length > INT_MAX32)) + length= INT_MAX32; - if (!start && res->length() == (uint) length) + /* if "unsigned_flag" is set, we have a *huge* positive number. */ + /* Assumes that the maximum length of a String is < INT_MAX32. */ + if ((args[1]->unsigned_flag) || (start < INT_MIN32) || (start > INT_MAX32)) + return &my_empty_string; + + start= ((start < 0) ? res->numchars() + start : start - 1); + start= res->charpos(start); + if ((start < 0) || ((uint) start + 1 > res->length())) + return &my_empty_string; + + length= res->charpos(length, start); + tmp_length= res->length() - start; + length= min(length, tmp_length); + + if (!start && res->length() == (ulonglong) length) return res; - tmp_value.set(*res,(uint) start,(uint) length); + tmp_value.set(*res, (ulonglong) start, (ulonglong) length); return &tmp_value; } @@ -2141,8 +2179,15 @@ void Item_func_repeat::fix_length_and_dec() collation.set(args[0]->collation); if (args[1]->const_item()) { - ulonglong max_result_length= ((ulonglong) args[0]->max_length * - args[1]->val_int()); + /* must be longlong to avoid truncation */ + longlong count= args[1]->val_int(); + + /* Assumes that the maximum length of a String is < INT_MAX32. */ + /* Set here so that rest of code sees out-of-bound value as such. */ + if (count > INT_MAX32) + count= INT_MAX32; + + ulonglong max_result_length= (ulonglong) args[0]->max_length * count; if (max_result_length >= MAX_BLOB_WIDTH) { max_result_length= MAX_BLOB_WIDTH; @@ -2167,13 +2212,20 @@ String *Item_func_repeat::val_str(String *str) DBUG_ASSERT(fixed == 1); uint length,tot_length; char *to; - long count= (long) args[1]->val_int(); - String *res =args[0]->val_str(str); + /* must be longlong to avoid truncation */ + longlong tmp_count= args[1]->val_int(); + long count= tmp_count; + String *res= args[0]->val_str(str); + + /* Assumes that the maximum length of a String is < INT_MAX32. */ + /* Bounds check on count: If this is triggered, we will error. */ + if ((tmp_count > INT_MAX32) || args[1]->unsigned_flag) + count= INT_MAX32; if (args[0]->null_value || args[1]->null_value) goto err; // string and/or delim are null - null_value=0; - if (count <= 0) // For nicer SQL code + null_value= 0; + if ((tmp_count <= 0) && !args[1]->unsigned_flag) // For nicer SQL code return &my_empty_string; if (count == 1) // To avoid reallocs return res; @@ -2212,8 +2264,20 @@ void Item_func_rpad::fix_length_and_dec() return; if (args[1]->const_item()) { - ulonglong length= ((ulonglong) args[1]->val_int() * - collation.collation->mbmaxlen); + ulonglong length= 0; + + if (collation.collation->mbmaxlen > 0) + { + ulonglong temp= (ulonglong) args[1]->val_int(); + + /* Assumes that the maximum length of a String is < INT_MAX32. */ + /* Set here so that rest of code sees out-of-bound value as such. */ + if (temp > INT_MAX32) + temp = INT_MAX32; + + length= temp * collation.collation->mbmaxlen; + } + if (length >= MAX_BLOB_WIDTH) { length= MAX_BLOB_WIDTH; @@ -2235,21 +2299,30 @@ String *Item_func_rpad::val_str(String *str) uint32 res_byte_length,res_char_length,pad_char_length,pad_byte_length; char *to; const char *ptr_pad; - int32 count= (int32) args[1]->val_int(); - int32 byte_count= count * collation.collation->mbmaxlen; - String *res =args[0]->val_str(str); - String *rpad = args[2]->val_str(&rpad_str); + /* must be longlong to avoid truncation */ + longlong count= args[1]->val_int(); + longlong byte_count; + String *res= args[0]->val_str(str); + String *rpad= args[2]->val_str(&rpad_str); + + /* Assumes that the maximum length of a String is < INT_MAX32. */ + /* Set here so that rest of code sees out-of-bound value as such. */ + if ((count > INT_MAX32) || args[1]->unsigned_flag) + count= INT_MAX32; if (!res || args[1]->null_value || !rpad || count < 0) goto err; null_value=0; - if (count <= (int32) (res_char_length=res->numchars())) + + if (count <= (res_char_length= res->numchars())) { // String to pad is big enough res->length(res->charpos(count)); // Shorten result if longer return (res); } pad_char_length= rpad->numchars(); - if ((ulong) byte_count > current_thd->variables.max_allowed_packet) + + byte_count= count * collation.collation->mbmaxlen; + if ((ulonglong) byte_count > current_thd->variables.max_allowed_packet) { push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_ALLOWED_PACKET_OVERFLOWED, @@ -2295,8 +2368,20 @@ void Item_func_lpad::fix_length_and_dec() if (args[1]->const_item()) { - ulonglong length= ((ulonglong) args[1]->val_int() * - collation.collation->mbmaxlen); + ulonglong length= 0; + + if (collation.collation->mbmaxlen > 0) + { + ulonglong temp= (ulonglong) args[1]->val_int(); + + /* Assumes that the maximum length of a String is < INT_MAX32. */ + /* Set here so that rest of code sees out-of-bound value as such. */ + if (temp > INT_MAX32) + temp= INT_MAX32; + + length= temp * collation.collation->mbmaxlen; + } + if (length >= MAX_BLOB_WIDTH) { length= MAX_BLOB_WIDTH; @@ -2316,13 +2401,19 @@ String *Item_func_lpad::val_str(String *str) { DBUG_ASSERT(fixed == 1); uint32 res_char_length,pad_char_length; - ulong count= (long) args[1]->val_int(), byte_count; + /* must be longlong to avoid truncation */ + longlong count= args[1]->val_int(); + longlong byte_count; String *res= args[0]->val_str(&tmp_value); String *pad= args[2]->val_str(&lpad_str); - if (!res || args[1]->null_value || !pad) - goto err; + /* Assumes that the maximum length of a String is < INT_MAX32. */ + /* Set here so that rest of code sees out-of-bound value as such. */ + if ((count > INT_MAX32) || args[1]->unsigned_flag) + count= INT_MAX32; + if (!res || args[1]->null_value || !pad || count < 0) + goto err; null_value=0; res_char_length= res->numchars();