Commit c4e5d2f9 authored by Oleksandr Byelkin's avatar Oleksandr Byelkin

MDEV-22545: my_vsnprintf behaves not as in C standard

Added parameter %T for string which should be visibly truncated.
parent e380f447
...@@ -787,6 +787,7 @@ static ha_checksum checksum_format_specifier(const char* msg) ...@@ -787,6 +787,7 @@ static ha_checksum checksum_format_specifier(const char* msg)
case 'x': case 'x':
case 's': case 's':
case 'M': case 'M':
case 'T':
chksum= my_checksum(chksum, (uchar*) start, (uint) (p + 1 - start)); chksum= my_checksum(chksum, (uchar*) start, (uint) (p + 1 - start));
start= 0; /* Not in format specifier anymore */ start= 0; /* Not in format specifier anymore */
break; break;
......
...@@ -55,7 +55,8 @@ ...@@ -55,7 +55,8 @@
Supported formats are 's' (null pointer is accepted, printed as Supported formats are 's' (null pointer is accepted, printed as
"(null)"), 'b' (extension, see below), 'c', 'd', 'i', 'u', 'x', 'o', "(null)"), 'b' (extension, see below), 'c', 'd', 'i', 'u', 'x', 'o',
'X', 'p' (works as 0x%x), 'f', 'g', 'M' (extension, see below). 'X', 'p' (works as 0x%x), 'f', 'g', 'M' (extension, see below),
'T' (extension, see below).
Standard syntax for positional arguments $n is supported. Standard syntax for positional arguments $n is supported.
...@@ -69,6 +70,9 @@ ...@@ -69,6 +70,9 @@
Format 'M': takes one integer, prints this integer, space, double quote Format 'M': takes one integer, prints this integer, space, double quote
error message, double quote. In other words error message, double quote. In other words
printf("%M", n) === printf("%d \"%s\"", n, strerror(n)) printf("%M", n) === printf("%d \"%s\"", n, strerror(n))
Format 'T': takes string and print it like s but if the strints should be
truncated puts "..." at the end.
*/ */
#ifdef __cplusplus #ifdef __cplusplus
......
...@@ -2,6 +2,6 @@ MySQL error code 150: Foreign key constraint is incorrectly formed ...@@ -2,6 +2,6 @@ MySQL error code 150: Foreign key constraint is incorrectly formed
Win32 error code 150: System trace information was not specified in your CONFIG.SYS file, or tracing is disallowed. Win32 error code 150: System trace information was not specified in your CONFIG.SYS file, or tracing is disallowed.
OS error code 23: Too many open files in system OS error code 23: Too many open files in system
Win32 error code 23: Data error (cyclic redundancy check). Win32 error code 23: Data error (cyclic redundancy check).
MySQL error code 1062 (ER_DUP_ENTRY): Duplicate entry '%-.192s' for key %d MySQL error code 1062 (ER_DUP_ENTRY): Duplicate entry '%-.192T' for key %d
Win32 error code 1062: The service has not been started. Win32 error code 1062: The service has not been started.
Illegal error code: 30000 Illegal error code: 30000
Illegal error code: 10000 Illegal error code: 10000
MySQL error code 1062 (ER_DUP_ENTRY): Duplicate entry '%-.192s' for key %d MySQL error code 1062 (ER_DUP_ENTRY): Duplicate entry '%-.192T' for key %d
MySQL error code 1408 (ER_STARTUP): %s: ready for connections. MySQL error code 1408 (ER_STARTUP): %s: ready for connections.
Version: '%s' socket: '%s' port: %d %s Version: '%s' socket: '%s' port: %d %s
MySQL error code 1459 (ER_TABLE_NEEDS_UPGRADE): Upgrade required. Please do "REPAIR %s %`s" or dump/reload to fix it! MySQL error code 1459 (ER_TABLE_NEEDS_UPGRADE): Upgrade required. Please do "REPAIR %s %`s" or dump/reload to fix it!
......
...@@ -11,7 +11,7 @@ drop table t1; ...@@ -11,7 +11,7 @@ drop table t1;
connection slave; connection slave;
include/wait_for_slave_sql_to_stop.inc include/wait_for_slave_sql_to_stop.inc
call mtr.add_suppression("Slave SQL.*Query caused different errors on master and slave.*Error on master:.* error code=1062.*Error on slave:.* error.* 0"); call mtr.add_suppression("Slave SQL.*Query caused different errors on master and slave.*Error on master:.* error code=1062.*Error on slave:.* error.* 0");
Error: "Query caused different errors on master and slave. Error on master: message (format)='Duplicate entry '%-.192s' for key %d' error code=1062 ; Error on slave: actual message='no error', error code=0. Default database: 'test'. Query: 'insert into t1 values(1),(2)'" (expected different error codes on master and slave) Error: "Query caused different errors on master and slave. Error on master: message (format)='Duplicate entry '%-.192T' for key %d' error code=1062 ; Error on slave: actual message='no error', error code=0. Default database: 'test'. Query: 'insert into t1 values(1),(2)'" (expected different error codes on master and slave)
Errno: "0" (expected 0) Errno: "0" (expected 0)
drop table t1; drop table t1;
include/stop_slave.inc include/stop_slave.inc
......
...@@ -6661,7 +6661,7 @@ Item_float::Item_float(THD *thd, const char *str_arg, uint length): ...@@ -6661,7 +6661,7 @@ Item_float::Item_float(THD *thd, const char *str_arg, uint length):
&error); &error);
if (error) if (error)
{ {
char tmp[NAME_LEN + 1]; char tmp[NAME_LEN + 2];
my_snprintf(tmp, sizeof(tmp), "%.*s", length, str_arg); my_snprintf(tmp, sizeof(tmp), "%.*s", length, str_arg);
my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "double", tmp); my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "double", tmp);
} }
......
...@@ -997,11 +997,16 @@ static Item *create_comparator(MY_XPATH *xpath, ...@@ -997,11 +997,16 @@ static Item *create_comparator(MY_XPATH *xpath,
b->type() == Item::XPATH_NODESET) b->type() == Item::XPATH_NODESET)
{ {
uint len= (uint)(xpath->query.end - context->beg); uint len= (uint)(xpath->query.end - context->beg);
set_if_smaller(len, 32); if (len <= 32)
my_printf_error(ER_UNKNOWN_ERROR, my_printf_error(ER_UNKNOWN_ERROR,
"XPATH error: " "XPATH error: "
"comparison of two nodesets is not supported: '%.*s'", "comparison of two nodesets is not supported: '%.*s'",
MYF(0), len, context->beg); MYF(0), len, context->beg);
else
my_printf_error(ER_UNKNOWN_ERROR,
"XPATH error: "
"comparison of two nodesets is not supported: '%.32T'",
MYF(0), context->beg);
return 0; // TODO: Comparison of two nodesets return 0; // TODO: Comparison of two nodesets
} }
...@@ -2634,9 +2639,12 @@ my_xpath_parse_VariableReference(MY_XPATH *xpath) ...@@ -2634,9 +2639,12 @@ my_xpath_parse_VariableReference(MY_XPATH *xpath)
xpath->item= NULL; xpath->item= NULL;
DBUG_ASSERT(xpath->query.end > dollar_pos); DBUG_ASSERT(xpath->query.end > dollar_pos);
uint len= (uint)(xpath->query.end - dollar_pos); uint len= (uint)(xpath->query.end - dollar_pos);
set_if_smaller(len, 32); if (len <= 32)
my_printf_error(ER_UNKNOWN_ERROR, "Unknown XPATH variable at: '%.*s'", my_printf_error(ER_UNKNOWN_ERROR, "Unknown XPATH variable at: '%.*s'",
MYF(0), len, dollar_pos); MYF(0), len, dollar_pos);
else
my_printf_error(ER_UNKNOWN_ERROR, "Unknown XPATH variable at: '%.32T'",
MYF(0), dollar_pos);
} }
} }
return xpath->item ? 1 : 0; return xpath->item ? 1 : 0;
...@@ -2767,9 +2775,13 @@ bool Item_xml_str_func::fix_fields(THD *thd, Item **ref) ...@@ -2767,9 +2775,13 @@ bool Item_xml_str_func::fix_fields(THD *thd, Item **ref)
if (!rc) if (!rc)
{ {
uint clen= (uint)(xpath.query.end - xpath.lasttok.beg); uint clen= (uint)(xpath.query.end - xpath.lasttok.beg);
set_if_smaller(clen, 32); if (clen <= 32)
my_printf_error(ER_UNKNOWN_ERROR, "XPATH syntax error: '%.*s'", my_printf_error(ER_UNKNOWN_ERROR, "XPATH syntax error: '%.*s'",
MYF(0), clen, xpath.lasttok.beg); MYF(0), clen, xpath.lasttok.beg);
else
my_printf_error(ER_UNKNOWN_ERROR, "XPATH syntax error: '%.32T'",
MYF(0), xpath.lasttok.beg);
return true; return true;
} }
......
This diff is collapsed.
...@@ -224,7 +224,8 @@ static char *backtick_string(CHARSET_INFO *cs, char *to, const char *end, ...@@ -224,7 +224,8 @@ static char *backtick_string(CHARSET_INFO *cs, char *to, const char *end,
*/ */
static char *process_str_arg(CHARSET_INFO *cs, char *to, const char *end, static char *process_str_arg(CHARSET_INFO *cs, char *to, const char *end,
size_t width, char *par, uint print_type) size_t width, char *par, uint print_type,
my_bool nice_cut)
{ {
int well_formed_error; int well_formed_error;
uint dots= 0; uint dots= 0;
...@@ -232,6 +233,8 @@ static char *process_str_arg(CHARSET_INFO *cs, char *to, const char *end, ...@@ -232,6 +233,8 @@ static char *process_str_arg(CHARSET_INFO *cs, char *to, const char *end,
if (!par) if (!par)
par = (char*) "(null)"; par = (char*) "(null)";
if (nice_cut)
{
plen= slen= strnlen(par, width + 1); plen= slen= strnlen(par, width + 1);
if (plen > width) if (plen > width)
plen= width; plen= width;
...@@ -250,6 +253,14 @@ static char *process_str_arg(CHARSET_INFO *cs, char *to, const char *end, ...@@ -250,6 +253,14 @@ static char *process_str_arg(CHARSET_INFO *cs, char *to, const char *end,
plen-= 3; plen-= 3;
} }
} }
}
else
{
plen= slen= strnlen(par, width);
dots= 0;
if (left_len <= plen)
plen = left_len - 1;
}
plen= my_well_formed_length(cs, par, par + plen, width, &well_formed_error); plen= my_well_formed_length(cs, par, par + plen, width, &well_formed_error);
if (print_type & ESCAPED_ARG) if (print_type & ESCAPED_ARG)
...@@ -446,6 +457,7 @@ static char *process_args(CHARSET_INFO *cs, char *to, char *end, ...@@ -446,6 +457,7 @@ static char *process_args(CHARSET_INFO *cs, char *to, char *end,
switch (args_arr[i].arg_type) { switch (args_arr[i].arg_type) {
case 's': case 's':
case 'b': case 'b':
case 'T':
args_arr[i].str_arg= va_arg(ap, char *); args_arr[i].str_arg= va_arg(ap, char *);
break; break;
case 'f': case 'f':
...@@ -480,12 +492,14 @@ static char *process_args(CHARSET_INFO *cs, char *to, char *end, ...@@ -480,12 +492,14 @@ static char *process_args(CHARSET_INFO *cs, char *to, char *end,
size_t width= 0, length= 0; size_t width= 0, length= 0;
switch (print_arr[i].arg_type) { switch (print_arr[i].arg_type) {
case 's': case 's':
case 'T':
{ {
char *par= args_arr[print_arr[i].arg_idx].str_arg; char *par= args_arr[print_arr[i].arg_idx].str_arg;
width= (print_arr[i].flags & WIDTH_ARG) width= (print_arr[i].flags & WIDTH_ARG)
? (size_t)args_arr[print_arr[i].width].longlong_arg ? (size_t)args_arr[print_arr[i].width].longlong_arg
: print_arr[i].width; : print_arr[i].width;
to= process_str_arg(cs, to, end, width, par, print_arr[i].flags); to= process_str_arg(cs, to, end, width, par, print_arr[i].flags,
(print_arr[i].arg_type == 'T'));
break; break;
} }
case 'b': case 'b':
...@@ -552,7 +566,7 @@ static char *process_args(CHARSET_INFO *cs, char *to, char *end, ...@@ -552,7 +566,7 @@ static char *process_args(CHARSET_INFO *cs, char *to, char *end,
*to++= '"'; *to++= '"';
my_strerror(errmsg_buff, sizeof(errmsg_buff), (int) larg); my_strerror(errmsg_buff, sizeof(errmsg_buff), (int) larg);
to= process_str_arg(cs, to, real_end, width, errmsg_buff, to= process_str_arg(cs, to, real_end, width, errmsg_buff,
print_arr[i].flags); print_arr[i].flags, 1);
if (real_end > to) *to++= '"'; if (real_end > to) *to++= '"';
} }
break; break;
...@@ -676,10 +690,10 @@ size_t my_vsnprintf_ex(CHARSET_INFO *cs, char *to, size_t n, ...@@ -676,10 +690,10 @@ size_t my_vsnprintf_ex(CHARSET_INFO *cs, char *to, size_t n,
fmt= check_longlong(fmt, &have_longlong); fmt= check_longlong(fmt, &have_longlong);
if (*fmt == 's') /* String parameter */ if (*fmt == 's' || *fmt == 'T') /* String parameter */
{ {
reg2 char *par= va_arg(ap, char *); reg2 char *par= va_arg(ap, char *);
to= process_str_arg(cs, to, end, width, par, print_type); to= process_str_arg(cs, to, end, width, par, print_type, (*fmt == 'T'));
continue; continue;
} }
else if (*fmt == 'b') /* Buffer parameter */ else if (*fmt == 'b') /* Buffer parameter */
...@@ -731,7 +745,8 @@ size_t my_vsnprintf_ex(CHARSET_INFO *cs, char *to, size_t n, ...@@ -731,7 +745,8 @@ size_t my_vsnprintf_ex(CHARSET_INFO *cs, char *to, size_t n,
*to++= ' '; *to++= ' ';
*to++= '"'; *to++= '"';
my_strerror(errmsg_buff, sizeof(errmsg_buff), (int) larg); my_strerror(errmsg_buff, sizeof(errmsg_buff), (int) larg);
to= process_str_arg(cs, to, real_end, width, errmsg_buff, print_type); to= process_str_arg(cs, to, real_end, width, errmsg_buff,
print_type, 1);
if (real_end > to) *to++= '"'; if (real_end > to) *to++= '"';
} }
continue; continue;
......
...@@ -61,7 +61,7 @@ static void test_many(const char **res, const char *fmt, ...) ...@@ -61,7 +61,7 @@ static void test_many(const char **res, const char *fmt, ...)
int main(void) int main(void)
{ {
plan(43); plan(47);
test1("Constant string", test1("Constant string",
"Constant string"); "Constant string");
...@@ -99,27 +99,33 @@ int main(void) ...@@ -99,27 +99,33 @@ int main(void)
test1("Width is ignored for strings <x> <y>", test1("Width is ignored for strings <x> <y>",
"Width is ignored for strings <%04s> <%5s>", "x", "y"); "Width is ignored for strings <%04s> <%5s>", "x", "y");
test1("Precision works for strings <ab...>", test1("Precision works for strings <abcde>",
"Precision works for strings <%.5s>", "abcdef!"); "Precision works for strings <%.5s>", "abcdef!");
test1("Precision works for strings <ab...>",
"Precision works for strings <%.5T>", "abcdef!");
test1("Flag '`' (backtick) works: `abcd` `op``q` (mysql extension)",
"Flag '`' (backtick) works: %`s %`.4s (mysql extension)",
"abcd", "op`qrst");
test1("Flag '`' (backtick) works: `abcd` `op``q...` (mysql extension)", test1("Flag '`' (backtick) works: `abcd` `op``q...` (mysql extension)",
"Flag '`' (backtick) works: %`s %`.7s (mysql extension)", "Flag '`' (backtick) works: %`T %`.7T (mysql extension)",
"abcd", "op`qrstuuuuuuuuu"); "abcd", "op`qrstuuuuuuuuu");
test1("Flag '`' (backtick) works: `abcd` `.` (mysql extension)", test1("Flag '`' (backtick) works: `abcd` `.` (mysql extension)",
"Flag '`' (backtick) works: %`s %`.1s (mysql extension)", "Flag '`' (backtick) works: %`T %`.1T (mysql extension)",
"abcd", "op`qrstuuuuuuuuu"); "abcd", "op`qrstuuuuuuuuu");
test1("Flag '`' (backtick) works: `abcd` `...` (mysql extension)", test1("Flag '`' (backtick) works: `abcd` `...` (mysql extension)",
"Flag '`' (backtick) works: %`s %`.3s (mysql extension)", "Flag '`' (backtick) works: %`T %`.3T (mysql extension)",
"abcd", "op`qrstuuuuuuuuu"); "abcd", "op`qrstuuuuuuuuu");
test1("Flag '`' (backtick) works: `abcd` `op...` (mysql extension)", test1("Flag '`' (backtick) works: `abcd` `op...` (mysql extension)",
"Flag '`' (backtick) works: %`s %`.5s (mysql extension)", "Flag '`' (backtick) works: %`T %`.5T (mysql extension)",
"abcd", "op`qrstuuuuuuuuu"); "abcd", "op`qrstuuuuuuuuu");
test1("Flag '`' (backtick) works: `abcd` `op``...` (mysql extension)", test1("Flag '`' (backtick) works: `abcd` `op``...` (mysql extension)",
"Flag '`' (backtick) works: %`s %`.6s (mysql extension)", "Flag '`' (backtick) works: %`T %`.6T (mysql extension)",
"abcd", "op`qrstuuuuuuuuu"); "abcd", "op`qrstuuuuuuuuu");
test1("Length modifiers work: 1 * -1 * 2 * 3", test1("Length modifiers work: 1 * -1 * 2 * 3",
...@@ -141,15 +147,21 @@ int main(void) ...@@ -141,15 +147,21 @@ int main(void)
test1("Asterisk '*' as a width works: < 4>", test1("Asterisk '*' as a width works: < 4>",
"Asterisk '*' as a width works: <%*d>", 5, 4); "Asterisk '*' as a width works: <%*d>", 5, 4);
test1("Asterisk '*' as a precision works: <qwe...>", test1("Asterisk '*' as a precision works: <qwerty>",
"Asterisk '*' as a precision works: <%.*s>", 6, "qwertyuiop"); "Asterisk '*' as a precision works: <%.*s>", 6, "qwertyuiop");
test1("Asterisk '*' as a precision works: <qwe...>",
"Asterisk '*' as a precision works: <%.*T>", 6, "qwertyuiop");
test1("Positional arguments for a width: < 4>", test1("Positional arguments for a width: < 4>",
"Positional arguments for a width: <%1$*2$d>", 4, 5); "Positional arguments for a width: <%1$*2$d>", 4, 5);
test1("Positional arguments for a precision: <qwe...>", test1("Positional arguments for a precision: <qwerty>",
"Positional arguments for a precision: <%1$.*2$s>", "qwertyuiop", 6); "Positional arguments for a precision: <%1$.*2$s>", "qwertyuiop", 6);
test1("Positional arguments for a precision: <qwe...>",
"Positional arguments for a precision: <%1$.*2$T>", "qwertyuiop", 6);
test1("Positional arguments and a width: <0000ab>", test1("Positional arguments and a width: <0000ab>",
"Positional arguments and a width: <%1$06x>", 0xab); "Positional arguments and a width: <%1$06x>", 0xab);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment