mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
12c42b980a
In sql_class.cc, 'row_count', of type 'ha_rows', was used as last argument for ER_TRUNCATED_WRONG_VALUE_FOR_FIELD which is "Incorrect %-.32s value: '%-.128s' for column '%.192s' at row %ld". So 'ha_rows' was used as 'long'. On SPARC32 Solaris builds, 'long' is 4 bytes and 'ha_rows' is 'longlong' i.e. 8 bytes. So the printf-like code was reading only the first 4 bytes. Because the CPU is big-endian, 1LL is 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01 so the first four bytes yield 0. So the warning message had "row 0" instead of "row 1" in test outfile_loaddata.test: -Warning 1366 Incorrect string value: '\xE1\xE2\xF7' for column 'b' at row 1 +Warning 1366 Incorrect string value: '\xE1\xE2\xF7' for column 'b' at row 0 All error-messaging functions which internally invoke some printf-life function are potential candidate for such mistakes. One apparently easy way to catch such mistakes is to use ATTRIBUTE_FORMAT (from my_attribute.h). But this works only when call site has both: a) the format as a string literal b) the types of arguments. So: func(ER(ER_BLAH), 10); will silently not be checked, because ER(ER_BLAH) is not known at compile time (it is known at run-time, and depends on the chosen language). And func("%s", a va_list argument); has the same problem, as the *real* type of arguments is not known at this site at compile time (it's known in some caller). Moreover, func(ER(ER_BLAH)); though possibly correct (if ER(ER_BLAH) has no '%' markers), will not compile (gcc says "error: format not a string literal and no format arguments"). Consequences: 1) ATTRIBUTE_FORMAT is here added only to functions which in practice take "string literal" formats: "my_error_reporter" and "print_admin_msg". 2) it cannot be added to the other functions: my_error(), push_warning_printf(), Table_check_intact::report_error(), general_log_print(). To do a one-time check of functions listed in (2), the following "static code analysis" has been done: 1) replace my_error(ER_xxx, arguments for substitution in format) with the equivalent my_printf_error(ER_xxx,ER(ER_xxx), arguments for substitution in format), so that we have ER(ER_xxx) and the arguments *in the same call site* 2) add ATTRIBUTE_FORMAT to push_warning_printf(), Table_check_intact::report_error(), general_log_print() 3) replace ER(xxx) with the hard-coded English text found in errmsg.txt (like: ER(ER_UNKNOWN_ERROR) is replaced with "Unknown error"), so that a call site has the format as string literal 4) this way, ATTRIBUTE_FORMAT can effectively do its job 5) compile, fix errors detected by ATTRIBUTE_FORMAT 6) revert steps 1-2-3. The present patch has no compiler error when submitted again to the static code analysis above. It cannot catch all problems though: see Field::set_warning(), in which a call to push_warning_printf() has a variable error (thus, not replacable by a string literal); I checked set_warning() calls by hand though. See also WL 5883 for one proposal to avoid such bugs from appearing again in the future. The issues fixed in the patch are: a) mismatch in types (like 'int' passed to '%ld') b) more arguments passed than specified in the format. This patch resolves mismatches by changing the type/number of arguments, not by changing error messages of sql/share/errmsg.txt. The latter would be wrong, per the following old rule: errmsg.txt must be as stable as possible; no insertions or deletions of messages, no changes of type or number of printf-like format specifiers, are allowed, as long as the change impacts a message already released in a GA version. If this rule is not followed: - Connectors, which use error message numbers, will be confused (by insertions/deletions of messages) - using errmsg.sys of MySQL 5.1.n with mysqld of MySQL 5.1.(n+1) could produce wrong messages or crash; such usage can easily happen if installing 5.1.(n+1) while /etc/my.cnf still has --language=/path/to/5.1.n/xxx; or if copying mysqld from 5.1.(n+1) into a 5.1.n installation. When fixing b), I have verified that the superfluous arguments were not used in the format in the first 5.1 GA (5.1.30 'bteam@astra04-20081114162938-z8mctjp6st27uobm'). Had they been used, then passing them today, even if the message doesn't use them anymore, would have been necessary, as explained above. include/my_getopt.h: this function pointer is used only with "string literal" formats, so we can add ATTRIBUTE_FORMAT. mysql-test/collections/default.experimental: test should pass now sql/derror.cc: by having a format as string literal, ATTRIBUTE_FORMAT check becomes effective. sql/events.cc: Change justified by the following excerpt from sql/share/errmsg.txt: ER_EVENT_SAME_NAME eng "Same old and new event name" ER_EVENT_SET_VAR_ERROR eng "Error during starting/stopping of the scheduler. Error code %u" sql/field.cc: ER_TOO_BIG_SCALE 42000 S1009 eng "Too big scale %d specified for column '%-.192s'. Maximum is %lu." ER_TOO_BIG_PRECISION 42000 S1009 eng "Too big precision %d specified for column '%-.192s'. Maximum is %lu." ER_TOO_BIG_DISPLAYWIDTH 42000 S1009 eng "Display width out of range for column '%-.192s' (max = %lu)" sql/ha_ndbcluster.cc: ER_OUTOFMEMORY HY001 S1001 eng "Out of memory; restart server and try again (needed %d bytes)" (sizeof() returns size_t) sql/ha_ndbcluster_binlog.cc: Too many arguments for: ER_GET_ERRMSG eng "Got error %d '%-.100s' from %s" Patch by Jonas Oreland. sql/ha_partition.cc: print_admin_msg() is used only with a literal as format, so ATTRIBUTE_FORMAT works. sql/handler.cc: ER_OUTOFMEMORY HY001 S1001 eng "Out of memory; restart server and try again (needed %d bytes)" (sizeof() returns size_t) sql/item_create.cc: ER_TOO_BIG_SCALE 42000 S1009 eng "Too big scale %d specified for column '%-.192s'. Maximum is %lu." ER_TOO_BIG_PRECISION 42000 S1009 eng "Too big precision %d specified for column '%-.192s'. Maximum is %lu." 'c_len' and 'c_dec' are char*, passed as %d !! We don't know their value (as strtoul() failed), but they are likely big, so we use INT_MAX. 'len' is ulong. sql/item_func.cc: ER_WARN_DATA_OUT_OF_RANGE 22003 eng "Out of range value for column '%s' at row %ld" ER_CANT_FIND_UDF eng "Can't load function '%-.192s'" sql/item_strfunc.cc: ER_TOO_BIG_FOR_UNCOMPRESS eng "Uncompressed data size too large; the maximum size is %d (probably, length of uncompressed data was corrupted)" max_allowed_packet is ulong. sql/mysql_priv.h: sql_print_message_func is a function _pointer_. sql/sp_head.cc: ER_SP_RECURSION_LIMIT eng "Recursive limit %d (as set by the max_sp_recursion_depth variable) was exceeded for routine %.192s" max_sp_recursion_depth is ulong sql/sql_acl.cc: ER_PASSWORD_NO_MATCH 42000 eng "Can't find any matching row in the user table" ER_CANT_CREATE_USER_WITH_GRANT 42000 eng "You are not allowed to create a user with GRANT" sql/sql_base.cc: ER_NOT_KEYFILE eng "Incorrect key file for table '%-.200s'; try to repair it" ER_TOO_MANY_TABLES eng "Too many tables; MySQL can only use %d tables in a join" MAX_TABLES is size_t. sql/sql_binlog.cc: ER_UNKNOWN_ERROR eng "Unknown error" sql/sql_class.cc: ER_TRUNCATED_WRONG_VALUE_FOR_FIELD eng "Incorrect %-.32s value: '%-.128s' for column '%.192s' at row %ld" WARN_DATA_TRUNCATED 01000 eng "Data truncated for column '%s' at row %ld" sql/sql_connect.cc: ER_HANDSHAKE_ERROR 08S01 eng "Bad handshake" ER_BAD_HOST_ERROR 08S01 eng "Can't get hostname for your address" sql/sql_insert.cc: ER_WRONG_VALUE_COUNT_ON_ROW 21S01 eng "Column count doesn't match value count at row %ld" sql/sql_parse.cc: ER_WARN_HOSTNAME_WONT_WORK eng "MySQL is started in --skip-name-resolve mode; you must restart it without this switch for this grant to work" ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT eng "Too high level of nesting for select" ER_UNKNOWN_ERROR eng "Unknown error" sql/sql_partition.cc: ER_OUTOFMEMORY HY001 S1001 eng "Out of memory; restart server and try again (needed %d bytes)" sql/sql_plugin.cc: ER_OUTOFMEMORY HY001 S1001 eng "Out of memory; restart server and try again (needed %d bytes)" sql/sql_prepare.cc: ER_OUTOFMEMORY HY001 S1001 eng "Out of memory; restart server and try again (needed %d bytes)" ER_UNKNOWN_STMT_HANDLER eng "Unknown prepared statement handler (%.*s) given to %s" length value (for '%.*s') must be 'int', per the doc of printf() and the code of my_vsnprintf(). sql/sql_show.cc: ER_OUTOFMEMORY HY001 S1001 eng "Out of memory; restart server and try again (needed %d bytes)" sql/sql_table.cc: ER_TOO_BIG_FIELDLENGTH 42000 S1009 eng "Column length too big for column '%-.192s' (max = %lu); use BLOB or TEXT instead" sql/table.cc: ER_NOT_FORM_FILE eng "Incorrect information in file: '%-.200s'" ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE eng "Column count of mysql.%s is wrong. Expected %d, found %d. Created with MySQL %d, now running %d. Please use mysql_upgrade to fix this error." table->s->mysql_version is ulong. sql/unireg.cc: ER_TOO_LONG_TABLE_COMMENT eng "Comment for table '%-.64s' is too long (max = %lu)" ER_TOO_LONG_FIELD_COMMENT eng "Comment for field '%-.64s' is too long (max = %lu)" ER_TOO_BIG_ROWSIZE 42000 eng "Row size too large. The maximum row size for the used table type, not counting BLOBs, is %ld. You have to change some columns to TEXT or BLOBs"
250 lines
7.4 KiB
C++
250 lines
7.4 KiB
C++
/* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
|
|
|
#include "mysql_priv.h"
|
|
#include "rpl_rli.h"
|
|
#include "base64.h"
|
|
|
|
/**
|
|
Execute a BINLOG statement.
|
|
|
|
To execute the BINLOG command properly the server needs to know
|
|
which format the BINLOG command's event is in. Therefore, the first
|
|
BINLOG statement seen must be a base64 encoding of the
|
|
Format_description_log_event, as outputted by mysqlbinlog. This
|
|
Format_description_log_event is cached in
|
|
rli->description_event_for_exec.
|
|
|
|
@param thd Pointer to THD object for the client thread executing the
|
|
statement.
|
|
*/
|
|
|
|
void mysql_client_binlog_statement(THD* thd)
|
|
{
|
|
DBUG_ENTER("mysql_client_binlog_statement");
|
|
DBUG_PRINT("info",("binlog base64: '%*s'",
|
|
(int) (thd->lex->comment.length < 2048 ?
|
|
thd->lex->comment.length : 2048),
|
|
thd->lex->comment.str));
|
|
|
|
if (check_global_access(thd, SUPER_ACL))
|
|
DBUG_VOID_RETURN;
|
|
|
|
size_t coded_len= thd->lex->comment.length;
|
|
if (!coded_len)
|
|
{
|
|
my_error(ER_SYNTAX_ERROR, MYF(0));
|
|
DBUG_VOID_RETURN;
|
|
}
|
|
size_t decoded_len= base64_needed_decoded_length(coded_len);
|
|
|
|
/*
|
|
thd->options will be changed when applying the event. But we don't expect
|
|
it be changed permanently after BINLOG statement, so backup it first.
|
|
It will be restored at the end of this function.
|
|
*/
|
|
ulonglong thd_options= thd->options;
|
|
|
|
/*
|
|
Allocation
|
|
*/
|
|
|
|
/*
|
|
If we do not have a Format_description_event, we create a dummy
|
|
one here. In this case, the first event we read must be a
|
|
Format_description_event.
|
|
*/
|
|
my_bool have_fd_event= TRUE;
|
|
int err;
|
|
Relay_log_info *rli;
|
|
rli= thd->rli_fake;
|
|
if (!rli)
|
|
{
|
|
rli= thd->rli_fake= new Relay_log_info;
|
|
#ifdef HAVE_purify
|
|
rli->is_fake= TRUE;
|
|
#endif
|
|
have_fd_event= FALSE;
|
|
}
|
|
if (rli && !rli->relay_log.description_event_for_exec)
|
|
{
|
|
rli->relay_log.description_event_for_exec=
|
|
new Format_description_log_event(4);
|
|
have_fd_event= FALSE;
|
|
}
|
|
|
|
const char *error= 0;
|
|
char *buf= (char *) my_malloc(decoded_len, MYF(MY_WME));
|
|
Log_event *ev = 0;
|
|
|
|
/*
|
|
Out of memory check
|
|
*/
|
|
if (!(rli &&
|
|
rli->relay_log.description_event_for_exec &&
|
|
buf))
|
|
{
|
|
my_error(ER_OUTOFMEMORY, MYF(0), 1); /* needed 1 bytes */
|
|
goto end;
|
|
}
|
|
|
|
rli->sql_thd= thd;
|
|
rli->no_storage= TRUE;
|
|
|
|
for (char const *strptr= thd->lex->comment.str ;
|
|
strptr < thd->lex->comment.str + thd->lex->comment.length ; )
|
|
{
|
|
char const *endptr= 0;
|
|
int bytes_decoded= base64_decode(strptr, coded_len, buf, &endptr);
|
|
|
|
#ifndef HAVE_purify
|
|
/*
|
|
This debug printout should not be used for valgrind builds
|
|
since it will read from unassigned memory.
|
|
*/
|
|
DBUG_PRINT("info",
|
|
("bytes_decoded: %d strptr: 0x%lx endptr: 0x%lx ('%c':%d)",
|
|
bytes_decoded, (long) strptr, (long) endptr, *endptr,
|
|
*endptr));
|
|
#endif
|
|
|
|
if (bytes_decoded < 0)
|
|
{
|
|
my_error(ER_BASE64_DECODE_ERROR, MYF(0));
|
|
goto end;
|
|
}
|
|
else if (bytes_decoded == 0)
|
|
break; // If no bytes where read, the string contained only whitespace
|
|
|
|
DBUG_ASSERT(bytes_decoded > 0);
|
|
DBUG_ASSERT(endptr > strptr);
|
|
coded_len-= endptr - strptr;
|
|
strptr= endptr;
|
|
|
|
/*
|
|
Now we have one or more events stored in the buffer. The size of
|
|
the buffer is computed based on how much base64-encoded data
|
|
there were, so there should be ample space for the data (maybe
|
|
even too much, since a statement can consist of a considerable
|
|
number of events).
|
|
|
|
TODO: Switch to use a stream-based base64 encoder/decoder in
|
|
order to be able to read exactly what is necessary.
|
|
*/
|
|
|
|
DBUG_PRINT("info",("binlog base64 decoded_len: %lu bytes_decoded: %d",
|
|
(ulong) decoded_len, bytes_decoded));
|
|
|
|
/*
|
|
Now we start to read events of the buffer, until there are no
|
|
more.
|
|
*/
|
|
for (char *bufptr= buf ; bytes_decoded > 0 ; )
|
|
{
|
|
/*
|
|
Checking that the first event in the buffer is not truncated.
|
|
*/
|
|
ulong event_len;
|
|
if (bytes_decoded < EVENT_LEN_OFFSET + 4 ||
|
|
(event_len= uint4korr(bufptr + EVENT_LEN_OFFSET)) >
|
|
(uint) bytes_decoded)
|
|
{
|
|
my_error(ER_SYNTAX_ERROR, MYF(0));
|
|
goto end;
|
|
}
|
|
DBUG_PRINT("info", ("event_len=%lu, bytes_decoded=%d",
|
|
event_len, bytes_decoded));
|
|
|
|
/*
|
|
If we have not seen any Format_description_event, then we must
|
|
see one; it is the only statement that can be read in base64
|
|
without a prior Format_description_event.
|
|
*/
|
|
if (!have_fd_event)
|
|
{
|
|
int type = bufptr[EVENT_TYPE_OFFSET];
|
|
if (type == FORMAT_DESCRIPTION_EVENT || type == START_EVENT_V3)
|
|
have_fd_event= TRUE;
|
|
else
|
|
{
|
|
my_error(ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT,
|
|
MYF(0), Log_event::get_type_str((Log_event_type)type));
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
ev= Log_event::read_log_event(bufptr, event_len, &error,
|
|
rli->relay_log.description_event_for_exec);
|
|
|
|
DBUG_PRINT("info",("binlog base64 err=%s", error));
|
|
if (!ev)
|
|
{
|
|
/*
|
|
This could actually be an out-of-memory, but it is more likely
|
|
causes by a bad statement
|
|
*/
|
|
my_error(ER_SYNTAX_ERROR, MYF(0));
|
|
goto end;
|
|
}
|
|
|
|
bytes_decoded -= event_len;
|
|
bufptr += event_len;
|
|
|
|
DBUG_PRINT("info",("ev->get_type_code()=%d", ev->get_type_code()));
|
|
ev->thd= thd;
|
|
/*
|
|
We go directly to the application phase, since we don't need
|
|
to check if the event shall be skipped or not.
|
|
|
|
Neither do we have to update the log positions, since that is
|
|
not used at all: the rli_fake instance is used only for error
|
|
reporting.
|
|
*/
|
|
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
|
|
err= ev->apply_event(rli);
|
|
#else
|
|
err= 0;
|
|
#endif
|
|
/*
|
|
Format_description_log_event should not be deleted because it
|
|
will be used to read info about the relay log's format; it
|
|
will be deleted when the SQL thread does not need it,
|
|
i.e. when this thread terminates.
|
|
*/
|
|
if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
|
|
delete ev;
|
|
ev= 0;
|
|
if (err)
|
|
{
|
|
/*
|
|
TODO: Maybe a better error message since the BINLOG statement
|
|
now contains several events.
|
|
*/
|
|
my_error(ER_UNKNOWN_ERROR, MYF(0));
|
|
goto end;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
DBUG_PRINT("info",("binlog base64 execution finished successfully"));
|
|
my_ok(thd);
|
|
|
|
end:
|
|
thd->options= thd_options;
|
|
rli->clear_tables_to_lock();
|
|
my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
|
|
DBUG_VOID_RETURN;
|
|
}
|