diff options
Diffstat (limited to 'sql/sql_binlog.cc')
-rw-r--r-- | sql/sql_binlog.cc | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc new file mode 100644 index 00000000000..d96e4d6456e --- /dev/null +++ b/sql/sql_binlog.cc @@ -0,0 +1,252 @@ +/* + Copyright (c) 2005, 2010, 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), "Error executing BINLOG statement"); + 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; +} |