/* Copyright (C) 2005-2006 MySQL AB 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 "base64.h" /* Execute a BINLOG statement TODO: This currently assumes a MySQL 5.x binlog. When we'll have binlog with a different format, to execute the BINLOG command properly the server will need to know which format the BINLOG command's event is in. mysqlbinlog should then send the Format_description_log_event of the binlog it reads and the server thread should cache this format into rli->description_event_for_exec. */ void mysql_client_binlog_statement(THD* thd) { DBUG_ENTER("mysql_client_binlog_statement"); DBUG_PRINT("info",("binlog base64: '%*s'", (thd->lex->comment.length < 2048 ? thd->lex->comment.length : 2048), thd->lex->comment.str)); /* Temporarily turn off send_ok, since different events handle this differently */ my_bool nsok= thd->net.no_send_ok; thd->net.no_send_ok= TRUE; my_size_t coded_len= thd->lex->comment.length + 1; my_size_t decoded_len= base64_needed_decoded_length(coded_len); DBUG_ASSERT(coded_len > 0); /* Allocation */ if (!thd->rli_fake) thd->rli_fake= new RELAY_LOG_INFO; const Format_description_log_event *desc= new Format_description_log_event(4); const char *error= 0; char *buf= (char *) my_malloc(decoded_len, MYF(MY_WME)); Log_event *ev = 0; /* Out of memory check */ if (!(thd->rli_fake && desc && buf)) { my_error(ER_OUTOFMEMORY, MYF(0), 1); /* needed 1 bytes */ goto end; } thd->rli_fake->sql_thd= thd; thd->rli_fake->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= uint4korr(bufptr + EVENT_LEN_OFFSET); DBUG_PRINT("info", ("event_len=%lu, bytes_decoded=%d", event_len, bytes_decoded)); if (bytes_decoded < EVENT_LEN_OFFSET || (uint) bytes_decoded < event_len) { my_error(ER_SYNTAX_ERROR, MYF(0)); goto end; } ev= Log_event::read_log_event(bufptr, event_len, &error, desc); 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())); #ifndef HAVE_purify /* This debug printout should not be used for valgrind builds since it will read from unassigned memory. */ DBUG_PRINT("info",("bufptr+EVENT_TYPE_OFFSET: 0x%lx", (long) (bufptr+EVENT_TYPE_OFFSET))); DBUG_PRINT("info", ("bytes_decoded: %d bufptr: 0x%lx buf[EVENT_LEN_OFFSET]: %lu", bytes_decoded, (long) bufptr, (ulong) uint4korr(bufptr+EVENT_LEN_OFFSET))); #endif ev->thd= thd; if (IF_DBUG(int err= ) ev->exec_event(thd->rli_fake)) { DBUG_PRINT("error", ("exec_event() returned: %d", 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; } delete ev; ev= 0; } } /* Restore setting of no_send_ok */ thd->net.no_send_ok= nsok; DBUG_PRINT("info",("binlog base64 execution finished successfully")); send_ok(thd); end: /* Restore setting of no_send_ok */ thd->net.no_send_ok= nsok; delete desc; my_free(buf, MYF(MY_ALLOW_ZERO_PTR)); DBUG_VOID_RETURN; }