Commit 593c7ce9 authored by unknown's avatar unknown

Mainly resolving Guilhem's 4.1 patch to 5.0


client/mysqlbinlog.cc:
  Resolveing merge
client/mysqldump.c:
  resolving merge
sql/handler.cc:
  resolving merge
sql/lex.h:
  resolve merge
sql/log.cc:
  resolving merge
sql/log_event.cc:
  resolving merge
sql/sql_parse.cc:
  resolving merge
sql/sql_yacc.yy:
  resolving merge
parent 4d0e9ad9
......@@ -14,11 +14,28 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/*
TODO: print the catalog (some USE catalog.db ????).
Standalone program to read a MySQL binary log (or relay log);
can read files produced by 3.23, 4.x, 5.0 servers.
Can read binlogs from 3.23/4.x/5.0 and relay logs from 4.x/5.0.
Should be able to read any file of these categories, even with
--start-position.
An important fact: the Format_desc event of the log is at most the 3rd event
of the log; if it is the 3rd then there is this combination:
Format_desc_of_slave, Rotate_of_master, Format_desc_of_master.
*/
#define MYSQL_CLIENT
#undef MYSQL_SERVER
#include "client_priv.h"
#include <my_time.h>
#include "log_event.h"
/* That one is necessary for defines of OPTION_NO_FOREIGN_KEY_CHECKS etc */
#include "mysql_priv.h"
#define BIN_LOG_HEADER_SIZE 4
#define PROBE_HEADER_LEN (EVENT_LEN_OFFSET+4)
......@@ -66,6 +83,14 @@ static MYSQL* mysql = NULL;
static const char* dirname_for_local_load= 0;
static bool stop_passed= 0;
/*
check_header() will set the pointer below.
Why do we need here a pointer on an event instead of an event ?
This is because the event will be created (alloced) in read_log_event()
(which returns a pointer) in check_header().
*/
Format_description_log_event* description_event;
static int dump_local_log_entries(const char* logname);
static int dump_remote_log_entries(const char* logname);
static int dump_log_entries(const char* logname);
......@@ -305,8 +330,8 @@ int Load_log_processor::process(Append_block_log_event *ae)
/*
There is no Create_file event (a bad binlog or a big
--position). Assuming it's a big --position, we just do nothing and
print a warning.
--start-position). Assuming it's a big --start-position, we just do
nothing and print a warning.
*/
fprintf(stderr,"Warning: ignoring Append_block as there is no \
Create_file event for file_id: %u\n",ae->file_id);
......@@ -316,7 +341,13 @@ Create_file event for file_id: %u\n",ae->file_id);
Load_log_processor load_processor;
/*
Process an event
SYNOPSIS
process_event()
RETURN
0 ok and continue
1 error and terminate
......@@ -325,21 +356,34 @@ Load_log_processor load_processor;
TODO
This function returns 0 even in some error cases. This should be changed.
*/
int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format)
int process_event(LAST_EVENT_INFO *last_event_info, Log_event *ev,
my_off_t pos)
{
char ll_buff[21];
Log_event_type ev_type= ev->get_type_code();
DBUG_ENTER("process_event");
/*
Format events are not concerned by --offset and such, we always need to
read them to be able to process the wanted events.
*/
if ((rec_count >= offset) &&
((my_time_t)(ev->when) >= start_datetime))
((my_time_t)(ev->when) >= start_datetime) ||
(ev_type == FORMAT_DESCRIPTION_EVENT))
{
if (ev_type != FORMAT_DESCRIPTION_EVENT)
{
/*
We have found an event after start_datetime, from now on print
everything (in case the binlog has timestamps increasing and decreasing,
we do this to avoid cutting the middle).
everything (in case the binlog has timestamps increasing and
decreasing, we do this to avoid cutting the middle).
*/
start_datetime= 0;
offset= 0; // print everything and protect against cycling rec_count
}
if (((my_time_t)(ev->when) >= stop_datetime)
|| (pos >= stop_position_mot))
{
......@@ -349,7 +393,7 @@ int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format)
if (!short_form)
fprintf(result_file, "# at %s\n",llstr(pos,ll_buff));
switch (ev->get_type_code()) {
switch (ev_type) {
case QUERY_EVENT:
if (one_database)
{
......@@ -357,7 +401,7 @@ int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format)
if ((log_dbname != NULL) && (strcmp(log_dbname, database)))
goto end;
}
ev->print(result_file, short_form, last_db);
ev->print(result_file, short_form, last_event_info);
break;
case CREATE_FILE_EVENT:
{
......@@ -381,8 +425,9 @@ int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format)
filename and use LOCAL), prepared in the 'case EXEC_LOAD_EVENT'
below.
*/
ce->print(result_file, short_form, last_db, TRUE);
if (!old_format)
ce->print(result_file, short_form, last_event_info, TRUE);
// If this binlog is not 3.23 ; why this test??
if (description_event->binlog_version >= 3)
{
if (load_processor.process(ce))
break; // Error
......@@ -391,23 +436,23 @@ int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format)
break;
}
case APPEND_BLOCK_EVENT:
ev->print(result_file, short_form, last_db);
ev->print(result_file, short_form, last_event_info);
if (load_processor.process((Append_block_log_event*) ev))
break; // Error
break;
case EXEC_LOAD_EVENT:
{
ev->print(result_file, short_form, last_db);
ev->print(result_file, short_form, last_event_info);
Execute_load_log_event *exv= (Execute_load_log_event*)ev;
Create_file_log_event *ce= load_processor.grab_event(exv->file_id);
/*
if ce is 0, it probably means that we have not seen the Create_file
event (a bad binlog, or most probably --position is after the
event (a bad binlog, or most probably --start-position is after the
Create_file event). Print a warning comment.
*/
if (ce)
{
ce->print(result_file, short_form, last_db, TRUE);
ce->print(result_file, short_form, last_event_info, TRUE);
my_free((char*)ce->fname,MYF(MY_WME));
delete ce;
}
......@@ -416,8 +461,20 @@ int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format)
Create_file event for file_id: %u\n",exv->file_id);
break;
}
case FORMAT_DESCRIPTION_EVENT:
delete description_event;
description_event= (Format_description_log_event*) ev;
ev->print(result_file, short_form, last_event_info);
/*
We don't want this event to be deleted now, so let's hide it (I
(Guilhem) should later see if this triggers a non-serious Valgrind
error). Not serious error, because we will free description_event
later.
*/
ev= 0;
break;
default:
ev->print(result_file, short_form, last_db);
ev->print(result_file, short_form, last_event_info);
}
}
......@@ -558,7 +615,7 @@ static void die(const char* fmt, ...)
static void print_version()
{
printf("%s Ver 3.0 for %s at %s\n", my_progname, SYSTEM_TYPE, MACHINE_TYPE);
printf("%s Ver 3.1 for %s at %s\n", my_progname, SYSTEM_TYPE, MACHINE_TYPE);
NETWARE_SET_SCREEN_MODE(1);
}
......@@ -701,12 +758,17 @@ static int dump_log_entries(const char* logname)
}
static int check_master_version(MYSQL* mysql)
/*
This is not as smart as check_header() (used for local log); it will not work
for a binlog which mixes format. TODO: fix this.
*/
static int check_master_version(MYSQL* mysql,
Format_description_log_event
**description_event)
{
MYSQL_RES* res = 0;
MYSQL_ROW row;
const char* version;
int old_format = 0;
if (mysql_query(mysql, "SELECT VERSION()") ||
!(res = mysql_store_result(mysql)))
......@@ -731,11 +793,18 @@ static int check_master_version(MYSQL* mysql)
switch (*version) {
case '3':
old_format = 1;
*description_event= new Format_description_log_event(1);
break;
case '4':
*description_event= new Format_description_log_event(3);
case '5':
old_format = 0;
/*
The server is soon going to send us its Format_description log
event, unless it is a 5.0 server with 3.23 or 4.0 binlogs.
So we first assume that this is 4.0 (which is enough to read the
Format_desc event if one comes).
*/
*description_event= new Format_description_log_event(3);
break;
default:
sql_print_error("Master reported unrecognized MySQL version '%s'",
......@@ -745,18 +814,17 @@ static int check_master_version(MYSQL* mysql)
return 1;
}
mysql_free_result(res);
return old_format;
return 0;
}
static int dump_remote_log_entries(const char* logname)
{
char buf[128];
char last_db[FN_REFLEN+1] = "";
ulong len;
uint logname_len;
LAST_EVENT_INFO last_event_info;
uint len, logname_len;
NET* net;
int old_format;
int error= 0;
my_off_t old_off= start_position_mot;
char fname[FN_REFLEN+1];
......@@ -769,7 +837,18 @@ static int dump_remote_log_entries(const char* logname)
*/
mysql= safe_connect();
net= &mysql->net;
old_format = check_master_version(mysql);
if (check_master_version(mysql, &description_event))
{
fprintf(stderr, "Could not find server version");
DBUG_RETURN(1);
}
if (!description_event || !description_event->is_valid())
{
fprintf(stderr, "Invalid Format_description log event; \
could be out of memory");
DBUG_RETURN(1);
}
/*
COM_BINLOG_DUMP accepts only 4 bytes for the position, so we are forced to
......@@ -798,6 +877,8 @@ static int dump_remote_log_entries(const char* logname)
for (;;)
{
const char *error_msg;
Log_event *ev;
len = net_safe_read(mysql);
if (len == packet_error)
{
......@@ -810,9 +891,9 @@ static int dump_remote_log_entries(const char* logname)
break; // end of data
DBUG_PRINT("info",( "len= %u, net->read_pos[5] = %d\n",
len, net->read_pos[5]));
Log_event *ev = Log_event::read_log_event((const char*) net->read_pos + 1 ,
len - 1, &error_msg, old_format);
if (!ev)
if (!(ev= Log_event::read_log_event((const char*) net->read_pos + 1 ,
len - 1, &error_msg,
description_event)))
{
fprintf(stderr, "Could not construct log event object\n");
error= 1;
......@@ -820,25 +901,27 @@ static int dump_remote_log_entries(const char* logname)
}
Log_event_type type= ev->get_type_code();
if (!old_format || ( type != LOAD_EVENT && type != CREATE_FILE_EVENT))
if (description_event->binlog_version >= 3 ||
(type != LOAD_EVENT && type != CREATE_FILE_EVENT))
{
if (ev->get_type_code() == ROTATE_EVENT)
/*
If this is a Rotate event, maybe it's the end of the requested binlog;
in this case we are done (stop transfer).
This is suitable for binlogs, not relay logs (but for now we don't read
relay logs remotely because the server is not able to do that). If one
day we read relay logs remotely, then we will have a problem with the
detection below: relay logs contain Rotate events which are about the
binlogs, so which would trigger the end-detection below.
*/
if (type == ROTATE_EVENT)
{
Rotate_log_event *rev= (Rotate_log_event *)ev;
/*
mysqld is sending us all its binlogs after the requested one, but we
don't want them.
If this is a fake Rotate event, and not about our log, we can stop
transfer. If this a real Rotate event (so it's not about our log,
it's in our log describing the next log), we print it (because it's
part of our log) and then we will stop when we receive the fake one
soon.
This is suitable for binlogs, not relay logs (but for now we don't
read relay logs remotely because the server is not able to do
that). If one day we read relay logs remotely, then we will have a
problem with the detection below: relay logs contain Rotate events
which are about the binlogs, so which would trigger the end-detection
below.
*/
if (rev->when == 0)
{
......@@ -861,7 +944,7 @@ static int dump_remote_log_entries(const char* logname)
len= 1; // fake Rotate, so don't increment old_off
}
}
if ((error= process_event(last_db,ev,old_off,old_format)))
if ((error= process_event(&last_event_info,ev,old_off)))
{
error= ((error < 0) ? 0 : 1);
goto err;
......@@ -880,57 +963,128 @@ static int dump_remote_log_entries(const char* logname)
goto err;
}
if ((error= process_event(last_db,ev,old_off,old_format)))
if ((error= process_event(&last_event_info,ev,old_off)))
{
my_close(file,MYF(MY_WME));
error= ((error < 0) ? 0 : 1);
goto err;
}
if (load_processor.load_old_format_file(net,old_fname,old_len,file))
{
error= load_processor.load_old_format_file(net,old_fname,old_len,file);
my_close(file,MYF(MY_WME));
if (error)
{
error= 1;
goto err;
}
my_close(file,MYF(MY_WME));
}
/*
Let's adjust offset for remote log as for local log to produce
similar text.
*/
old_off+= len-1;
}
err:
mysql_close(mysql);
DBUG_RETURN(error);
}
static int check_header(IO_CACHE* file)
static void check_header(IO_CACHE* file,
Format_description_log_event **description_event)
{
byte header[BIN_LOG_HEADER_SIZE];
byte buf[PROBE_HEADER_LEN];
int old_format=0;
DBUG_ENTER("check_header");
my_off_t tmp_pos, pos;
my_off_t pos = my_b_tell(file);
*description_event= new Format_description_log_event(3);
pos= my_b_tell(file);
my_b_seek(file, (my_off_t)0);
if (my_b_read(file, header, sizeof(header)))
die("Failed reading header; Probably an empty file");
if (memcmp(header, BINLOG_MAGIC, sizeof(header)))
die("File is not a binary log file");
if (!my_b_read(file, buf, sizeof(buf)))
/*
Imagine we are running with --start-position=1000. We still need
to know the binlog format's. So we still need to find, if there is
one, the Format_desc event, or to know if this is a 3.23
binlog. So we need to first read the first events of the log,
those around offset 4. Even if we are reading a 3.23 binlog from
the start (no --start-position): we need to know the header length
(which is 13 in 3.23, 19 in 4.x) to be able to successfully print
the first event (Start_log_event_v3). So even in this case, we
need to "probe" the first bytes of the log *before* we do a real
read_log_event(). Because read_log_event() needs to know the
header's length to work fine.
*/
for(;;)
{
tmp_pos= my_b_tell(file); /* should be 4 the first time */
if (my_b_read(file, buf, sizeof(buf)))
{
if (file->error)
die("\
Could not read entry at offset %lu : Error in log format or read error",
tmp_pos);
/*
Otherwise this is just EOF : this log currently contains 0-2
events. Maybe it's going to be filled in the next
milliseconds; then we are going to have a problem if this a
3.23 log (imagine we are locally reading a 3.23 binlog which
is being written presently): we won't know it in
read_log_event() and will fail(). Similar problems could
happen with hot relay logs if --start-position is used (but a
--start-position which is posterior to the current size of the log).
These are rare problems anyway (reading a hot log + when we
read the first events there are not all there yet + when we
read a bit later there are more events + using a strange
--start-position).
*/
break;
}
else
{
DBUG_PRINT("info",("buf[4]=%d", buf[4]));
/* always test for a Start_v3, even if no --start-position */
if (buf[4] == START_EVENT_V3) /* This is 3.23 or 4.x */
{
if (buf[4] == START_EVENT)
if (uint4korr(buf + EVENT_LEN_OFFSET) <
(LOG_EVENT_MINIMAL_HEADER_LEN + START_V3_HEADER_LEN))
{
uint event_len;
event_len = uint4korr(buf + EVENT_LEN_OFFSET);
old_format = (event_len < (LOG_EVENT_HEADER_LEN + START_HEADER_LEN));
/* This is 3.23 (format 1) */
delete *description_event;
*description_event= new Format_description_log_event(1);
}
break;
}
else if (tmp_pos >= start_position)
break;
else if (buf[4] == FORMAT_DESCRIPTION_EVENT) /* This is 5.0 */
{
my_b_seek(file, tmp_pos); /* seek back to event's start */
if (!(*description_event= (Format_description_log_event*)
Log_event::read_log_event(file, *description_event)))
/* EOF can't be hit here normally, so it's a real error */
die("Could not read a Format_description_log_event event \
at offset %lu ; this could be a log format error or read error",
tmp_pos);
DBUG_PRINT("info",("Setting description_event"));
}
else if (buf[4] == ROTATE_EVENT)
{
my_b_seek(file, tmp_pos); /* seek back to event's start */
if (!Log_event::read_log_event(file, *description_event))
/* EOF can't be hit here normally, so it's a real error */
die("Could not read a Rotate_log_event event \
at offset %lu ; this could be a log format error or read error",
tmp_pos);
}
else
break;
}
}
my_b_seek(file, pos);
DBUG_RETURN(old_format);
}
......@@ -938,13 +1092,10 @@ static int dump_local_log_entries(const char* logname)
{
File fd = -1;
IO_CACHE cache,*file= &cache;
char last_db[FN_REFLEN+1];
LAST_EVENT_INFO last_event_info;
byte tmp_buff[BIN_LOG_HEADER_SIZE];
bool old_format = 0;
int error= 0;
last_db[0]= 0;
if (logname && logname[0] != '-')
{
if ((fd = my_open(logname, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0)
......@@ -953,16 +1104,16 @@ static int dump_local_log_entries(const char* logname)
MYF(MY_WME | MY_NABP)))
{
my_close(fd, MYF(MY_WME));
exit(1);
return 1;
}
old_format = check_header(file);
check_header(file, &description_event);
}
else
else // reading from stdin; TODO: check that it works
{
if (init_io_cache(file, fileno(result_file), 0, READ_CACHE, (my_off_t) 0,
0, MYF(MY_WME | MY_NABP | MY_DONT_CHECK_FILESIZE)))
return 1;
old_format = check_header(file);
check_header(file, &description_event);
if (start_position)
{
/* skip 'start_position' characters from stdout */
......@@ -982,22 +1133,20 @@ static int dump_local_log_entries(const char* logname)
file->seek_not_done=0;
}
if (!start_position)
{
// Skip header
if (my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE))
if (!description_event || !description_event->is_valid())
die("Invalid Format_description log event; could be out of memory");
if (!start_position && my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE))
{
error= 1;
goto end;
}
}
for (;;)
{
char llbuff[21];
my_off_t old_off = my_b_tell(file);
Log_event* ev = Log_event::read_log_event(file, old_format);
Log_event* ev = Log_event::read_log_event(file, description_event);
if (!ev)
{
if (file->error)
......@@ -1011,7 +1160,7 @@ static int dump_local_log_entries(const char* logname)
// file->error == 0 means EOF, that's OK, we break in this case
break;
}
if ((error= process_event(last_db,ev,old_off,false)))
if ((error= process_event(&last_event_info,ev,old_off)))
{
if (error < 0)
error= 0;
......@@ -1023,6 +1172,7 @@ static int dump_local_log_entries(const char* logname)
if (fd >= 0)
my_close(fd, MYF(MY_WME));
end_io_cache(file);
delete description_event;
return error;
}
......
......@@ -108,6 +108,8 @@ static uint opt_protocol= 0;
static char *default_charset= (char*) MYSQL_UNIVERSAL_CLIENT_CHARSET;
static CHARSET_INFO *charset_info= &my_charset_latin1;
const char *default_dbug_option="d:t:o,/tmp/mysqldump.trace";
/* do we met VIEWs during tables scaning */
my_bool was_views= 0;
const char *compatible_mode_names[]=
{
......@@ -367,6 +369,8 @@ static int dump_databases(char **);
static int dump_all_databases();
static char *quote_name(const char *name, char *buff, my_bool force);
static const char *check_if_ignore_table(const char *table_name);
static my_bool getViewStructure(char *table, char* db);
static my_bool dump_all_views_in_db(char *database);
#include <help_start.h>
......@@ -1035,6 +1039,7 @@ static uint getTableStructure(char *table, char* db)
{
/* Make an sql-file, if path was given iow. option -T was given */
char buff[20+FN_REFLEN];
MYSQL_FIELD *field;
sprintf(buff,"show create table %s", result_table);
if (mysql_query_with_error_report(sock, 0, buff))
......@@ -1068,8 +1073,16 @@ static uint getTableStructure(char *table, char* db)
check_io(sql_file);
}
tableRes=mysql_store_result(sock);
row=mysql_fetch_row(tableRes);
tableRes= mysql_store_result(sock);
field= mysql_fetch_field_direct(tableRes, 0);
if (strcmp(field->name, "View") == 0)
{
if (verbose)
fprintf(stderr, "-- It's a view, skipped\n");
was_views= 1;
DBUG_RETURN(0);
}
row= mysql_fetch_row(tableRes);
fprintf(sql_file, "%s;\n", row[1]);
check_io(sql_file);
mysql_free_result(tableRes);
......@@ -1213,6 +1226,14 @@ static uint getTableStructure(char *table, char* db)
sprintf(buff,"show keys from %s", result_table);
if (mysql_query_with_error_report(sock, &tableRes, buff))
{
if (mysql_errno(sock) == ER_WRONG_OBJECT)
{
/* it is VIEW */
fputs("\t\t<options Comment=\"view\" />\n", sql_file);
goto continue_xml;
}
fprintf(stderr, "%s: Can't get keys for table %s (%s)\n",
my_progname, result_table, mysql_error(sock));
if (path)
my_fclose(sql_file, MYF(MY_WME));
safe_exit(EX_MYSQLERR);
......@@ -1316,6 +1337,7 @@ static uint getTableStructure(char *table, char* db)
}
mysql_free_result(tableRes); /* Is always safe to free */
}
continue_xml:
if (!opt_xml)
fputs(";\n", sql_file);
else
......@@ -1845,6 +1867,21 @@ static int dump_all_databases()
if (dump_all_tables_in_db(row[0]))
result=1;
}
if (was_views)
{
if (mysql_query(sock, "SHOW DATABASES") ||
!(tableres = mysql_store_result(sock)))
{
my_printf_error(0, "Error: Couldn't execute 'SHOW DATABASES': %s",
MYF(0), mysql_error(sock));
return 1;
}
while ((row = mysql_fetch_row(tableres)))
{
if (dump_all_views_in_db(row[0]))
result=1;
}
}
return result;
}
/* dump_all_databases */
......@@ -1853,11 +1890,20 @@ static int dump_all_databases()
static int dump_databases(char **db_names)
{
int result=0;
for ( ; *db_names ; db_names++)
char **db;
for (db= db_names ; *db ; db++)
{
if (dump_all_tables_in_db(*db))
result=1;
}
if (!result && was_views)
{
for (db= db_names ; *db ; db++)
{
if (dump_all_tables_in_db(*db_names))
if (dump_all_views_in_db(*db))
result=1;
}
}
return result;
} /* dump_databases */
......@@ -1965,11 +2011,64 @@ static int dump_all_tables_in_db(char *database)
return 0;
} /* dump_all_tables_in_db */
/*
dump structure of views of database
SYNOPSIS
dump_all_views_in_db()
database database name
RETURN
0 OK
1 ERROR
*/
static my_bool dump_all_views_in_db(char *database)
{
char *table;
uint numrows;
char table_buff[NAME_LEN*2+3];
if (init_dumping(database))
return 1;
if (opt_xml)
print_xml_tag1(md_result_file, "", "database name=", database, "\n");
if (lock_tables)
{
DYNAMIC_STRING query;
init_dynamic_string(&query, "LOCK TABLES ", 256, 1024);
for (numrows= 0 ; (table= getTableName(1)); numrows++)
{
dynstr_append(&query, quote_name(table, table_buff, 1));
dynstr_append(&query, " READ /*!32311 LOCAL */,");
}
if (numrows && mysql_real_query(sock, query.str, query.length-1))
DBerror(sock, "when using LOCK TABLES");
/* We shall continue here, if --force was given */
dynstr_free(&query);
}
if (flush_logs)
{
if (mysql_refresh(sock, REFRESH_LOG))
DBerror(sock, "when doing refresh");
/* We shall continue here, if --force was given */
}
while ((table= getTableName(0)))
getViewStructure(table, database);
if (opt_xml)
{
fputs("</database>\n", md_result_file);
check_io(md_result_file);
}
if (lock_tables)
mysql_query(sock,"UNLOCK TABLES");
return 0;
} /* dump_all_tables_in_db */
static int dump_selected_tables(char *db, char **table_names, int tables)
{
uint numrows;
int i;
char table_buff[NAME_LEN*+3];
if (init_dumping(db))
......@@ -1977,7 +2076,6 @@ static int dump_selected_tables(char *db, char **table_names, int tables)
if (lock_tables)
{
DYNAMIC_STRING query;
int i;
init_dynamic_string(&query, "LOCK TABLES ", 256, 1024);
for (i=0 ; i < tables ; i++)
......@@ -1998,11 +2096,16 @@ static int dump_selected_tables(char *db, char **table_names, int tables)
}
if (opt_xml)
print_xml_tag1(md_result_file, "", "database name=", db, "\n");
for (; tables > 0 ; tables-- , table_names++)
for (i=0 ; i < tables ; i++)
{
numrows = getTableStructure(*table_names, db);
numrows = getTableStructure(table_names[i], db);
if (!dFlag && numrows > 0)
dumpTable(numrows, *table_names);
dumpTable(numrows, table_names[i]);
}
if (was_views)
{
for (i=0 ; i < tables ; i++)
getViewStructure(table_names[i], db);
}
if (opt_xml)
{
......@@ -2197,14 +2300,113 @@ static const char *check_if_ignore_table(const char *table_name)
mysql_free_result(res);
return 0; /* assume table is ok */
}
if (!(row[1]))
result= "VIEW";
else
{
if (strcmp(row[1], (result= "MRG_MyISAM")) &&
strcmp(row[1], (result= "MRG_ISAM")))
result= 0;
}
mysql_free_result(res);
return result;
}
/*
Getting VIEW structure
SYNOPSIS
getViewStructure()
table view name
db db name
RETURN
0 OK
1 ERROR
*/
static my_bool getViewStructure(char *table, char* db)
{
MYSQL_RES *tableRes;
MYSQL_ROW row;
MYSQL_FIELD *field;
char *result_table, *opt_quoted_table;
char table_buff[NAME_LEN*2+3];
char table_buff2[NAME_LEN*2+3];
char buff[20+FN_REFLEN];
FILE *sql_file = md_result_file;
DBUG_ENTER("getViewStructure");
if (tFlag)
DBUG_RETURN(0);
if (verbose)
fprintf(stderr, "-- Retrieving view structure for table %s...\n", table);
sprintf(insert_pat,"SET OPTION SQL_QUOTE_SHOW_CREATE=%d",
(opt_quoted || opt_keywords));
result_table= quote_name(table, table_buff, 1);
opt_quoted_table= quote_name(table, table_buff2, 0);
sprintf(buff,"show create table %s", result_table);
if (mysql_query(sock, buff))
{
fprintf(stderr, "%s: Can't get CREATE TABLE for view %s (%s)\n",
my_progname, result_table, mysql_error(sock));
safe_exit(EX_MYSQLERR);
DBUG_RETURN(0);
}
if (path)
{
char filename[FN_REFLEN], tmp_path[FN_REFLEN];
convert_dirname(tmp_path,path,NullS);
sql_file= my_fopen(fn_format(filename, table, tmp_path, ".sql", 4),
O_WRONLY, MYF(MY_WME));
if (!sql_file) /* If file couldn't be opened */
{
safe_exit(EX_MYSQLERR);
DBUG_RETURN(1);
}
write_header(sql_file, db);
}
tableRes= mysql_store_result(sock);
field= mysql_fetch_field_direct(tableRes, 0);
if (strcmp(field->name, "View") != 0)
{
if (verbose)
fprintf(stderr, "-- It's base table, skipped\n");
DBUG_RETURN(0);
}
if (!opt_xml && opt_comments)
{
fprintf(sql_file, "\n--\n-- View structure for view %s\n--\n\n",
result_table);
check_io(sql_file);
}
if (opt_drop)
{
fprintf(sql_file, "DROP VIEW IF EXISTS %s;\n", opt_quoted_table);
check_io(sql_file);
}
row= mysql_fetch_row(tableRes);
fprintf(sql_file, "%s;\n", row[1]);
check_io(sql_file);
mysql_free_result(tableRes);
if (sql_file != md_result_file)
{
fputs("\n", sql_file);
write_footer(sql_file);
my_fclose(sql_file, MYF(MY_WME));
}
DBUG_RETURN(0);
}
int main(int argc, char **argv)
{
compatible_mode_normal_str[0]= 0;
......
......@@ -54,11 +54,7 @@
static int NEAR_F delete_file(const char *name,const char *ext,int extflag);
ulong ha_read_count, ha_write_count, ha_delete_count, ha_update_count,
ha_read_key_count, ha_read_next_count, ha_read_prev_count,
ha_read_first_count, ha_read_last_count,
ha_commit_count, ha_rollback_count,
ha_read_rnd_count, ha_read_rnd_next_count, ha_discover_count;
ulong ha_read_count, ha_discover_count;
static SHOW_COMP_OPTION have_yes= SHOW_OPTION_YES;
......@@ -114,7 +110,7 @@ uint known_extensions_id= 0;
enum db_type ha_resolve_by_name(const char *name, uint namelen)
{
THD *thd=current_thd;
THD *thd= current_thd;
if (thd && !my_strcasecmp(&my_charset_latin1, name, "DEFAULT")) {
return (enum db_type) thd->variables.table_type;
}
......@@ -145,6 +141,7 @@ const char *ha_get_storage_engine(enum db_type db_type)
enum db_type ha_checktype(enum db_type database_type)
{
show_table_type_st *types;
THD *thd= current_thd;
for (types= sys_table_types; types->type; types++)
{
if ((database_type == types->db_type) &&
......@@ -163,12 +160,11 @@ enum db_type ha_checktype(enum db_type database_type)
break;
}
return
DB_TYPE_UNKNOWN != (enum db_type) current_thd->variables.table_type ?
(enum db_type) current_thd->variables.table_type :
DB_TYPE_UNKNOWN != (enum db_type) global_system_variables.table_type ?
(enum db_type) global_system_variables.table_type :
DB_TYPE_MYISAM;
return ((enum db_type) thd->variables.table_type != DB_TYPE_UNKNOWN ?
(enum db_type) thd->variables.table_type :
(enum db_type) global_system_variables.table_type !=
DB_TYPE_UNKNOWN ?
(enum db_type) global_system_variables.table_type : DB_TYPE_MYISAM);
} /* ha_checktype */
......@@ -565,7 +561,7 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans)
thd->variables.tx_isolation=thd->session_tx_isolation;
if (operation_done)
{
statistic_increment(ha_commit_count,&LOCK_status);
statistic_increment(thd->status_var.ha_commit_count,&LOCK_status);
thd->transaction.cleanup();
}
if (need_start_waiters)
......@@ -734,7 +730,7 @@ int ha_rollback_to_savepoint(THD *thd, char *savepoint_name)
operation_done=1;
#endif
if (operation_done)
statistic_increment(ha_rollback_count,&LOCK_status);
statistic_increment(thd->status_var.ha_rollback_count,&LOCK_status);
}
#endif /* USING_TRANSACTIONS */
......@@ -816,10 +812,14 @@ bool ha_flush_logs()
int ha_delete_table(enum db_type table_type, const char *path)
{
handler *file;
char tmp_path[FN_REFLEN];
handler *file=get_new_handler((TABLE*) 0, table_type);
if (!file)
/* DB_TYPE_UNKNOWN is used in ALTER TABLE when renaming only .frm files */
if (table_type == DB_TYPE_UNKNOWN ||
! (file=get_new_handler((TABLE*) 0, table_type)))
return ENOENT;
if (lower_case_table_names == 2 && !(file->table_flags() & HA_FILE_BASED))
{
/* Ensure that table handler get path in lower case */
......@@ -948,7 +948,7 @@ int handler::read_first_row(byte * buf, uint primary_key)
register int error;
DBUG_ENTER("handler::read_first_row");
statistic_increment(ha_read_first_count,&LOCK_status);
statistic_increment(current_thd->status_var.ha_read_first_count,&LOCK_status);
/*
If there is very few deleted rows in the table, find the first row by
......@@ -972,41 +972,163 @@ int handler::read_first_row(byte * buf, uint primary_key)
/*
Updates field with field_type NEXT_NUMBER according to following:
if field = 0 change field to the next free key in database.
Generate the next auto-increment number based on increment and offset
In most cases increment= offset= 1, in which case we get:
1,2,3,4,5,...
If increment=10 and offset=5 and previous number is 1, we get:
1,5,15,25,35,...
*/
inline ulonglong
next_insert_id(ulonglong nr,struct system_variables *variables)
{
nr= (((nr+ variables->auto_increment_increment -
variables->auto_increment_offset)) /
(ulonglong) variables->auto_increment_increment);
return (nr* (ulonglong) variables->auto_increment_increment +
variables->auto_increment_offset);
}
/*
Updates columns with type NEXT_NUMBER if:
- If column value is set to NULL (in which case
auto_increment_field_not_null is 0)
- If column is set to 0 and (sql_mode & MODE_NO_AUTO_VALUE_ON_ZERO) is not
set. In the future we will only set NEXT_NUMBER fields if one sets them
to NULL (or they are not included in the insert list).
There are two different cases when the above is true:
- thd->next_insert_id == 0 (This is the normal case)
In this case we set the set the column for the first row to the value
next_insert_id(get_auto_increment(column))) which is normally
max-used-column-value +1.
We call get_auto_increment() only for the first row in a multi-row
statement. For the following rows we generate new numbers based on the
last used number.
- thd->next_insert_id != 0. This happens when we have read a statement
from the binary log or when one has used SET LAST_INSERT_ID=#.
In this case we will set the column to the value of next_insert_id.
The next row will be given the id
next_insert_id(next_insert_id)
The idea is that generated auto_increment values are predictable and
independent of the column values in the table. This is needed to be
able to replicate into a table that already has rows with a higher
auto-increment value than the one that is inserted.
After we have already generated an auto-increment number and the user
inserts a column with a higher value than the last used one, we will
start counting from the inserted value.
thd->next_insert_id is cleared after it's been used for a statement.
*/
void handler::update_auto_increment()
{
longlong nr;
THD *thd;
ulonglong nr;
THD *thd= table->in_use;
struct system_variables *variables= &thd->variables;
DBUG_ENTER("handler::update_auto_increment");
if (table->next_number_field->val_int() != 0 ||
/*
We must save the previous value to be able to restore it if the
row was not inserted
*/
thd->prev_insert_id= thd->next_insert_id;
if ((nr= table->next_number_field->val_int()) != 0 ||
table->auto_increment_field_not_null &&
current_thd->variables.sql_mode & MODE_NO_AUTO_VALUE_ON_ZERO)
thd->variables.sql_mode & MODE_NO_AUTO_VALUE_ON_ZERO)
{
/* Clear flag for next row */
table->auto_increment_field_not_null= FALSE;
/* Mark that we didn't generate a new value **/
auto_increment_column_changed=0;
/* Update next_insert_id if we have already generated a value */
if (thd->clear_next_insert_id && nr >= thd->next_insert_id)
{
if (variables->auto_increment_increment != 1)
nr= next_insert_id(nr, variables);
else
nr++;
thd->next_insert_id= nr;
DBUG_PRINT("info",("next_insert_id: %lu", (ulong) nr));
}
DBUG_VOID_RETURN;
}
table->auto_increment_field_not_null= FALSE;
thd=current_thd;
if ((nr=thd->next_insert_id))
thd->next_insert_id=0; // Clear after use
else
nr=get_auto_increment();
if (!table->next_number_field->store(nr))
if (!(nr= thd->next_insert_id))
{
nr= get_auto_increment();
if (variables->auto_increment_increment != 1)
nr= next_insert_id(nr-1, variables);
/*
Update next row based on the found value. This way we don't have to
call the handler for every generated auto-increment value on a
multi-row statement
*/
thd->next_insert_id= nr;
}
DBUG_PRINT("info",("auto_increment: %lu", (ulong) nr));
/* Mark that we should clear next_insert_id before next stmt */
thd->clear_next_insert_id= 1;
if (!table->next_number_field->store((longlong) nr))
thd->insert_id((ulonglong) nr);
else
thd->insert_id(table->next_number_field->val_int());
/*
We can't set next_insert_id if the auto-increment key is not the
first key part, as there is no guarantee that the first parts will be in
sequence
*/
if (!table->next_number_key_offset)
{
/*
Set next insert id to point to next auto-increment value to be able to
handle multi-row statements
This works even if auto_increment_increment > 1
*/
thd->next_insert_id= next_insert_id(nr, variables);
}
else
thd->next_insert_id= 0;
/* Mark that we generated a new value */
auto_increment_column_changed=1;
DBUG_VOID_RETURN;
}
/*
restore_auto_increment
In case of error on write, we restore the last used next_insert_id value
because the previous value was not used.
*/
void handler::restore_auto_increment()
{
THD *thd= table->in_use;
if (thd->next_insert_id)
thd->next_insert_id= thd->prev_insert_id;
}
longlong handler::get_auto_increment()
ulonglong handler::get_auto_increment()
{
longlong nr;
ulonglong nr;
int error;
(void) extra(HA_EXTRA_KEYREAD);
......@@ -1018,7 +1140,8 @@ longlong handler::get_auto_increment()
else
{
byte key[MAX_KEY_LENGTH];
key_copy(key,table,table->next_number_index,
key_copy(key, table->record[0],
table->key_info + table->next_number_index,
table->next_number_key_offset);
error=index_read(table->record[1], key, table->next_number_key_offset,
HA_READ_PREFIX_LAST);
......@@ -1027,8 +1150,8 @@ longlong handler::get_auto_increment()
if (error)
nr=1;
else
nr=(longlong) table->next_number_field->
val_int_offset(table->rec_buff_length)+1;
nr=((ulonglong) table->next_number_field->
val_int_offset(table->rec_buff_length)+1);
index_end();
(void) extra(HA_EXTRA_NO_KEYREAD);
return nr;
......@@ -1271,7 +1394,7 @@ int ha_create_table(const char *name, HA_CREATE_INFO *create_info,
char name_buff[FN_REFLEN];
DBUG_ENTER("ha_create_table");
if (openfrm(name,"",0,(uint) READ_ALL, 0, &table))
if (openfrm(current_thd, name,"",0,(uint) READ_ALL, 0, &table))
DBUG_RETURN(1);
if (update_create_info)
{
......@@ -1336,7 +1459,7 @@ int ha_create_table_from_engine(THD* thd,
if ((error = writefrm(path, frmblob, frmlen)))
goto err_end;
if (openfrm(path,"",0,(uint) READ_ALL, 0, &table))
if (openfrm(thd, path,"",0,(uint) READ_ALL, 0, &table))
DBUG_RETURN(1);
update_create_info_from_table(&create_info, &table);
......
......@@ -65,6 +65,7 @@ static SYMBOL symbols[] = {
{ "AGAINST", SYM(AGAINST)},
{ "AGGREGATE", SYM(AGGREGATE_SYM)},
{ "ALL", SYM(ALL)},
{ "ALGORITHM", SYM(ALGORITHM_SYM)},
{ "ALTER", SYM(ALTER)},
{ "ANALYZE", SYM(ANALYZE_SYM)},
{ "AND", SYM(AND_SYM)},
......@@ -72,6 +73,7 @@ static SYMBOL symbols[] = {
{ "AS", SYM(AS)},
{ "ASC", SYM(ASC)},
{ "ASCII", SYM(ASCII_SYM)},
{ "ASENSITIVE", SYM(ASENSITIVE_SYM)},
{ "AUTO_INCREMENT", SYM(AUTO_INC)},
{ "AVG", SYM(AVG_SYM)},
{ "AVG_ROW_LENGTH", SYM(AVG_ROW_LENGTH)},
......@@ -93,7 +95,9 @@ static SYMBOL symbols[] = {
{ "BY", SYM(BY)},
{ "BYTE", SYM(BYTE_SYM)},
{ "CACHE", SYM(CACHE_SYM)},
{ "CALL", SYM(CALL_SYM)},
{ "CASCADE", SYM(CASCADE)},
{ "CASCADED", SYM(CASCADED)},
{ "CASE", SYM(CASE_SYM)},
{ "CHANGE", SYM(CHANGE)},
{ "CHANGED", SYM(CHANGED)},
......@@ -114,8 +118,12 @@ static SYMBOL symbols[] = {
{ "COMMITTED", SYM(COMMITTED_SYM)},
{ "COMPRESSED", SYM(COMPRESSED_SYM)},
{ "CONCURRENT", SYM(CONCURRENT)},
{ "CONDITION", SYM(CONDITION_SYM)},
{ "CONNECTION", SYM(CONNECTION_SYM)},
{ "CONSISTENT", SYM(CONSISTENT_SYM)},
{ "CONSTRAINT", SYM(CONSTRAINT)},
{ "CONTAINS", SYM(CONTAINS_SYM)},
{ "CONTINUE", SYM(CONTINUE_SYM)},
{ "CONVERT", SYM(CONVERT_SYM)},
{ "CREATE", SYM(CREATE)},
{ "CROSS", SYM(CROSS)},
......@@ -124,6 +132,7 @@ static SYMBOL symbols[] = {
{ "CURRENT_TIME", SYM(CURTIME)},
{ "CURRENT_TIMESTAMP", SYM(NOW_SYM)},
{ "CURRENT_USER", SYM(CURRENT_USER)},
{ "CURSOR", SYM(CURSOR_SYM)},
{ "DATA", SYM(DATA_SYM)},
{ "DATABASE", SYM(DATABASE)},
{ "DATABASES", SYM(DATABASES)},
......@@ -137,13 +146,16 @@ static SYMBOL symbols[] = {
{ "DEALLOCATE", SYM(DEALLOCATE_SYM)},
{ "DEC", SYM(DECIMAL_SYM)},
{ "DECIMAL", SYM(DECIMAL_SYM)},
{ "DECLARE", SYM(DECLARE_SYM)},
{ "DEFAULT", SYM(DEFAULT)},
{ "DEFINER", SYM(DEFINER_SYM)},
{ "DELAYED", SYM(DELAYED_SYM)},
{ "DELAY_KEY_WRITE", SYM(DELAY_KEY_WRITE_SYM)},
{ "DELETE", SYM(DELETE_SYM)},
{ "DESC", SYM(DESC)},
{ "DESCRIBE", SYM(DESCRIBE)},
{ "DES_KEY_FILE", SYM(DES_KEY_FILE)},
{ "DETERMINISTIC", SYM(DETERMINISTIC_SYM)},
{ "DIRECTORY", SYM(DIRECTORY_SYM)},
{ "DISABLE", SYM(DISABLE_SYM)},
{ "DISCARD", SYM(DISCARD)},
......@@ -157,7 +169,9 @@ static SYMBOL symbols[] = {
{ "DUMPFILE", SYM(DUMPFILE)},
{ "DUPLICATE", SYM(DUPLICATE_SYM)},
{ "DYNAMIC", SYM(DYNAMIC_SYM)},
{ "EACH", SYM(EACH_SYM)},
{ "ELSE", SYM(ELSE)},
{ "ELSEIF", SYM(ELSEIF_SYM)},
{ "ENABLE", SYM(ENABLE_SYM)},
{ "ENCLOSED", SYM(ENCLOSED)},
{ "END", SYM(END)},
......@@ -170,11 +184,13 @@ static SYMBOL symbols[] = {
{ "EVENTS", SYM(EVENTS_SYM)},
{ "EXECUTE", SYM(EXECUTE_SYM)},
{ "EXISTS", SYM(EXISTS)},
{ "EXIT", SYM(EXIT_SYM)},
{ "EXPANSION", SYM(EXPANSION_SYM)},
{ "EXPLAIN", SYM(DESCRIBE)},
{ "EXTENDED", SYM(EXTENDED_SYM)},
{ "FALSE", SYM(FALSE_SYM)},
{ "FAST", SYM(FAST_SYM)},
{ "FETCH", SYM(FETCH_SYM)},
{ "FIELDS", SYM(COLUMNS)},
{ "FILE", SYM(FILE_SYM)},
{ "FIRST", SYM(FIRST_SYM)},
......@@ -186,14 +202,17 @@ static SYMBOL symbols[] = {
{ "FOR", SYM(FOR_SYM)},
{ "FORCE", SYM(FORCE_SYM)},
{ "FOREIGN", SYM(FOREIGN)},
{ "FOUND", SYM(FOUND_SYM)},
{ "FRAC_SECOND", SYM(FRAC_SECOND_SYM)},
{ "FROM", SYM(FROM)},
{ "FULL", SYM(FULL)},
{ "FULLTEXT", SYM(FULLTEXT_SYM)},
{ "FUNCTION", SYM(UDF_SYM)},
{ "FUNCTION", SYM(FUNCTION_SYM)},
{ "GEOMETRY", SYM(GEOMETRY_SYM)},
{ "GEOMETRYCOLLECTION",SYM(GEOMETRYCOLLECTION)},
{ "GET_FORMAT", SYM(GET_FORMAT)},
{ "GLOBAL", SYM(GLOBAL_SYM)},
{ "GOTO", SYM(GOTO_SYM)},
{ "GRANT", SYM(GRANT)},
{ "GRANTS", SYM(GRANTS)},
{ "GROUP", SYM(GROUP)},
......@@ -218,6 +237,8 @@ static SYMBOL symbols[] = {
{ "INNER", SYM(INNER_SYM)},
{ "INNOBASE", SYM(INNOBASE_SYM)},
{ "INNODB", SYM(INNOBASE_SYM)},
{ "INOUT", SYM(INOUT_SYM)},
{ "INSENSITIVE", SYM(INSENSITIVE_SYM)},
{ "INSERT", SYM(INSERT)},
{ "INSERT_METHOD", SYM(INSERT_METHOD)},
{ "INT", SYM(INT_SYM)},
......@@ -233,12 +254,17 @@ static SYMBOL symbols[] = {
{ "IS", SYM(IS)},
{ "ISOLATION", SYM(ISOLATION)},
{ "ISSUER", SYM(ISSUER_SYM)},
{ "ITERATE", SYM(ITERATE_SYM)},
{ "INVOKER", SYM(INVOKER_SYM)},
{ "JOIN", SYM(JOIN_SYM)},
{ "KEY", SYM(KEY_SYM)},
{ "KEYS", SYM(KEYS)},
{ "KILL", SYM(KILL_SYM)},
{ "LABEL", SYM(LABEL_SYM)},
{ "LANGUAGE", SYM(LANGUAGE_SYM)},
{ "LAST", SYM(LAST_SYM)},
{ "LEADING", SYM(LEADING)},
{ "LEAVE", SYM(LEAVE_SYM)},
{ "LEAVES", SYM(LEAVES)},
{ "LEFT", SYM(LEFT)},
{ "LEVEL", SYM(LEVEL_SYM)},
......@@ -256,6 +282,7 @@ static SYMBOL symbols[] = {
{ "LONG", SYM(LONG_SYM)},
{ "LONGBLOB", SYM(LONGBLOB)},
{ "LONGTEXT", SYM(LONGTEXT)},
{ "LOOP", SYM(LOOP_SYM)},
{ "LOW_PRIORITY", SYM(LOW_PRIORITY)},
{ "MASTER", SYM(MASTER_SYM)},
{ "MASTER_CONNECT_RETRY", SYM(MASTER_CONNECT_RETRY_SYM)},
......@@ -281,6 +308,7 @@ static SYMBOL symbols[] = {
{ "MEDIUMBLOB", SYM(MEDIUMBLOB)},
{ "MEDIUMINT", SYM(MEDIUMINT)},
{ "MEDIUMTEXT", SYM(MEDIUMTEXT)},
{ "MERGE", SYM(MERGE_SYM)},
{ "MICROSECOND", SYM(MICROSECOND_SYM)},
{ "MIDDLEINT", SYM(MEDIUMINT)}, /* For powerbuilder */
{ "MINUTE", SYM(MINUTE_SYM)},
......@@ -289,11 +317,13 @@ static SYMBOL symbols[] = {
{ "MIN_ROWS", SYM(MIN_ROWS)},
{ "MOD", SYM(MOD_SYM)},
{ "MODE", SYM(MODE_SYM)},
{ "MODIFIES", SYM(MODIFIES_SYM)},
{ "MODIFY", SYM(MODIFY_SYM)},
{ "MONTH", SYM(MONTH_SYM)},
{ "MULTILINESTRING", SYM(MULTILINESTRING)},
{ "MULTIPOINT", SYM(MULTIPOINT)},
{ "MULTIPOLYGON", SYM(MULTIPOLYGON)},
{ "NAME", SYM(NAME_SYM)},
{ "NAMES", SYM(NAMES_SYM)},
{ "NATIONAL", SYM(NATIONAL_SYM)},
{ "NATURAL", SYM(NATURAL)},
......@@ -319,6 +349,7 @@ static SYMBOL symbols[] = {
{ "OPTIONALLY", SYM(OPTIONALLY)},
{ "OR", SYM(OR_SYM)},
{ "ORDER", SYM(ORDER_SYM)},
{ "OUT", SYM(OUT_SYM)},
{ "OUTER", SYM(OUTER)},
{ "OUTFILE", SYM(OUTFILE)},
{ "PACK_KEYS", SYM(PACK_KEYS_SYM)},
......@@ -335,6 +366,7 @@ static SYMBOL symbols[] = {
{ "PROCESS" , SYM(PROCESS)},
{ "PROCESSLIST", SYM(PROCESSLIST_SYM)},
{ "PURGE", SYM(PURGE)},
{ "QUARTER", SYM(QUARTER_SYM)},
{ "QUERY", SYM(QUERY_SYM)},
{ "QUICK", SYM(QUICK)},
{ "RAID0", SYM(RAID_0_SYM)},
......@@ -342,6 +374,7 @@ static SYMBOL symbols[] = {
{ "RAID_CHUNKSIZE", SYM(RAID_CHUNKSIZE)},
{ "RAID_TYPE", SYM(RAID_TYPE)},
{ "READ", SYM(READ_SYM)},
{ "READS", SYM(READS_SYM)},
{ "REAL", SYM(REAL)},
{ "REFERENCES", SYM(REFERENCES)},
{ "REGEXP", SYM(REGEXP)},
......@@ -354,11 +387,13 @@ static SYMBOL symbols[] = {
{ "REPEATABLE", SYM(REPEATABLE_SYM)},
{ "REPLACE", SYM(REPLACE)},
{ "REPLICATION", SYM(REPLICATION)},
{ "REPEAT", SYM(REPEAT_SYM)},
{ "REQUIRE", SYM(REQUIRE_SYM)},
{ "RESET", SYM(RESET_SYM)},
{ "RESTORE", SYM(RESTORE_SYM)},
{ "RESTRICT", SYM(RESTRICT)},
{ "RETURNS", SYM(UDF_RETURNS_SYM)},
{ "RETURN", SYM(RETURN_SYM)},
{ "RETURNS", SYM(RETURNS_SYM)},
{ "REVOKE", SYM(REVOKE)},
{ "RIGHT", SYM(RIGHT)},
{ "RLIKE", SYM(REGEXP)}, /* Like in mSQL2 */
......@@ -369,9 +404,13 @@ static SYMBOL symbols[] = {
{ "ROW_FORMAT", SYM(ROW_FORMAT_SYM)},
{ "RTREE", SYM(RTREE_SYM)},
{ "SAVEPOINT", SYM(SAVEPOINT_SYM)},
{ "SCHEMA", SYM(DATABASE)},
{ "SCHEMAS", SYM(DATABASES)},
{ "SECOND", SYM(SECOND_SYM)},
{ "SECOND_MICROSECOND", SYM(SECOND_MICROSECOND_SYM)},
{ "SECURITY", SYM(SECURITY_SYM)},
{ "SELECT", SYM(SELECT_SYM)},
{ "SENSITIVE", SYM(SENSITIVE_SYM)},
{ "SEPARATOR", SYM(SEPARATOR_SYM)},
{ "SERIAL", SYM(SERIAL_SYM)},
{ "SERIALIZABLE", SYM(SERIALIZABLE_SYM)},
......@@ -389,6 +428,11 @@ static SYMBOL symbols[] = {
{ "SONAME", SYM(UDF_SONAME_SYM)},
{ "SOUNDS", SYM(SOUNDS_SYM)},
{ "SPATIAL", SYM(SPATIAL_SYM)},
{ "SPECIFIC", SYM(SPECIFIC_SYM)},
{ "SQL", SYM(SQL_SYM)},
{ "SQLEXCEPTION", SYM(SQLEXCEPTION_SYM)},
{ "SQLSTATE", SYM(SQLSTATE_SYM)},
{ "SQLWARNING", SYM(SQLWARNING_SYM)},
{ "SQL_BIG_RESULT", SYM(SQL_BIG_RESULT)},
{ "SQL_BUFFER_RESULT", SYM(SQL_BUFFER_RESULT)},
{ "SQL_CACHE", SYM(SQL_CACHE_SYM)},
......@@ -396,6 +440,15 @@ static SYMBOL symbols[] = {
{ "SQL_NO_CACHE", SYM(SQL_NO_CACHE_SYM)},
{ "SQL_SMALL_RESULT", SYM(SQL_SMALL_RESULT)},
{ "SQL_THREAD", SYM(SQL_THREAD)},
{ "SQL_TSI_FRAC_SECOND", SYM(FRAC_SECOND_SYM)},
{ "SQL_TSI_SECOND", SYM(SECOND_SYM)},
{ "SQL_TSI_MINUTE", SYM(MINUTE_SYM)},
{ "SQL_TSI_HOUR", SYM(HOUR_SYM)},
{ "SQL_TSI_DAY", SYM(DAY_SYM)},
{ "SQL_TSI_WEEK", SYM(WEEK_SYM)},
{ "SQL_TSI_MONTH", SYM(MONTH_SYM)},
{ "SQL_TSI_QUARTER", SYM(QUARTER_SYM)},
{ "SQL_TSI_YEAR", SYM(YEAR_SYM)},
{ "SSL", SYM(SSL_SYM)},
{ "START", SYM(START_SYM)},
{ "STARTING", SYM(STARTING)},
......@@ -411,22 +464,28 @@ static SYMBOL symbols[] = {
{ "TABLES", SYM(TABLES)},
{ "TABLESPACE", SYM(TABLESPACE)},
{ "TEMPORARY", SYM(TEMPORARY)},
{ "TEMPTABLE", SYM(TEMPTABLE_SYM)},
{ "TERMINATED", SYM(TERMINATED)},
{ "TEXT", SYM(TEXT_SYM)},
{ "THEN", SYM(THEN_SYM)},
{ "TIME", SYM(TIME_SYM)},
{ "TIMESTAMP", SYM(TIMESTAMP)},
{ "TIMESTAMPADD", SYM(TIMESTAMP_ADD)},
{ "TIMESTAMPDIFF", SYM(TIMESTAMP_DIFF)},
{ "TINYBLOB", SYM(TINYBLOB)},
{ "TINYINT", SYM(TINYINT)},
{ "TINYTEXT", SYM(TINYTEXT)},
{ "TO", SYM(TO_SYM)},
{ "TRAILING", SYM(TRAILING)},
{ "TRANSACTION", SYM(TRANSACTION_SYM)},
{ "TRIGGER", SYM(TRIGGER_SYM)},
{ "TRUE", SYM(TRUE_SYM)},
{ "TRUNCATE", SYM(TRUNCATE_SYM)},
{ "TYPE", SYM(TYPE_SYM)},
{ "TYPES", SYM(TYPES_SYM)},
{ "UNCOMMITTED", SYM(UNCOMMITTED_SYM)},
{ "UNDEFINED", SYM(UNDEFINED_SYM)},
{ "UNDO", SYM(UNDO_SYM)},
{ "UNICODE", SYM(UNICODE_SYM)},
{ "UNION", SYM(UNION_SYM)},
{ "UNIQUE", SYM(UNIQUE_SYM)},
......@@ -451,8 +510,11 @@ static SYMBOL symbols[] = {
{ "VARIABLES", SYM(VARIABLES)},
{ "VARYING", SYM(VARYING)},
{ "WARNINGS", SYM(WARNINGS)},
{ "WEEK", SYM(WEEK_SYM)},
{ "WHEN", SYM(WHEN_SYM)},
{ "WHERE", SYM(WHERE)},
{ "WHILE", SYM(WHILE_SYM)},
{ "VIEW", SYM(VIEW_SYM)},
{ "WITH", SYM(WITH)},
{ "WORK", SYM(WORK_SYM)},
{ "WRITE", SYM(WRITE_SYM)},
......@@ -499,7 +561,6 @@ static SYMBOL sql_functions[] = {
{ "CONCAT", SYM(CONCAT)},
{ "CONCAT_WS", SYM(CONCAT_WS)},
{ "CONNECTION_ID", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_connection_id)},
{ "CONTAINS", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_contains)},
{ "CONV", F_SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_conv)},
{ "CONVERT_TZ", SYM(CONVERT_TZ_SYM)},
{ "COUNT", SYM(COUNT_SYM)},
......@@ -636,14 +697,13 @@ static SYMBOL sql_functions[] = {
{ "POSITION", SYM(POSITION_SYM)},
{ "POW", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_pow)},
{ "POWER", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_pow)},
{ "QUARTER", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_quarter)},
{ "QUOTE", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_quote)},
{ "RADIANS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_radians)},
{ "RAND", SYM(RAND)},
{ "RELEASE_LOCK", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_release_lock)},
{ "REPEAT", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_repeat)},
{ "REVERSE", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_reverse)},
{ "ROUND", SYM(ROUND)},
{ "ROW_COUNT", SYM(ROW_COUNT_SYM)},
{ "RPAD", F_SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_rpad)},
{ "RTRIM", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_rtrim)},
{ "SEC_TO_TIME", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sec_to_time)},
......@@ -686,7 +746,6 @@ static SYMBOL sql_functions[] = {
{ "UUID", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_uuid)},
{ "VARIANCE", SYM(VARIANCE_SYM)},
{ "VERSION", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_version)},
{ "WEEK", SYM(WEEK_SYM)},
{ "WEEKDAY", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_weekday)},
{ "WEEKOFYEAR", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_weekofyear)},
{ "WITHIN", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_within)},
......
......@@ -35,7 +35,7 @@
#include "message.h"
#endif
MYSQL_LOG mysql_log,mysql_update_log,mysql_slow_log,mysql_bin_log;
MYSQL_LOG mysql_log, mysql_slow_log, mysql_bin_log;
ulong sync_binlog_counter= 0;
static bool test_if_number(const char *str,
......@@ -129,7 +129,8 @@ static int find_uniq_filename(char *name)
MYSQL_LOG::MYSQL_LOG()
:bytes_written(0), last_time(0), query_start(0), name(0),
file_id(1), open_count(1), log_type(LOG_CLOSED), write_error(0), inited(0),
need_start_event(1)
need_start_event(1), description_event_for_exec(0),
description_event_for_queue(0)
{
/*
We don't want to initialize LOCK_Log here as such initialization depends on
......@@ -157,6 +158,8 @@ void MYSQL_LOG::cleanup()
{
inited= 0;
close(LOG_CLOSE_INDEX);
delete description_event_for_queue;
delete description_event_for_exec;
(void) pthread_mutex_destroy(&LOCK_log);
(void) pthread_mutex_destroy(&LOCK_index);
(void) pthread_cond_destroy(&update_cond);
......@@ -226,7 +229,8 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
const char *new_name, const char *index_file_name_arg,
enum cache_type io_cache_type_arg,
bool no_auto_events_arg,
ulong max_size_arg)
ulong max_size_arg,
bool null_created_arg)
{
char buff[512];
File file= -1, index_file_nr= -1;
......@@ -324,8 +328,8 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
if (my_b_safe_write(&log_file, (byte*) BINLOG_MAGIC,
BIN_LOG_HEADER_SIZE))
goto err;
bytes_written += BIN_LOG_HEADER_SIZE;
write_file_name_to_index_file=1;
bytes_written+= BIN_LOG_HEADER_SIZE;
write_file_name_to_index_file= 1;
}
if (!my_b_inited(&index_file))
......@@ -355,10 +359,50 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
}
if (need_start_event && !no_auto_events)
{
need_start_event=0;
Start_log_event s;
s.set_log_pos(this);
s.write(&log_file);
/*
In 4.x we set need_start_event=0 here, but in 5.0 we want a Start event
even if this is not the very first binlog.
*/
Format_description_log_event s(BINLOG_VERSION);
if (!s.is_valid())
goto err;
if (null_created_arg)
s.created= 0;
if (s.write(&log_file))
goto err;
bytes_written+= s.data_written;
}
if (description_event_for_queue &&
description_event_for_queue->binlog_version>=4)
{
/*
This is a relay log written to by the I/O slave thread.
Write the event so that others can later know the format of this relay
log.
Note that this event is very close to the original event from the
master (it has binlog version of the master, event types of the
master), so this is suitable to parse the next relay log's event. It
has been produced by
Format_description_log_event::Format_description_log_event(char*
buf,).
Why don't we want to write the description_event_for_queue if this
event is for format<4 (3.23 or 4.x): this is because in that case, the
description_event_for_queue describes the data received from the
master, but not the data written to the relay log (*conversion*),
which is in format 4 (slave's).
*/
/*
Set 'created' to 0, so that in next relay logs this event does not
trigger cleaning actions on the slave in
Format_description_log_event::exec_event().
*/
description_event_for_queue->created= 0;
/* Don't set log_pos in event header */
description_event_for_queue->artificial_event=1;
if (description_event_for_queue->write(&log_file))
goto err;
bytes_written+= description_event_for_queue->data_written;
}
if (flush_io_cache(&log_file) ||
my_sync(log_file.file, MYF(MY_WME)))
......@@ -654,7 +698,7 @@ bool MYSQL_LOG::reset_logs(THD* thd)
if (!thd->slave_thread)
need_start_event=1;
open(save_name, save_log_type, 0, index_file_name,
io_cache_type, no_auto_events, max_size);
io_cache_type, no_auto_events, max_size, 0);
my_free((gptr) save_name, MYF(0));
err:
......@@ -836,22 +880,18 @@ int MYSQL_LOG::purge_logs(const char *to_log,
while ((strcmp(to_log,log_info.log_file_name) || (exit_loop=included)) &&
!log_in_use(log_info.log_file_name))
{
ulong tmp;
LINT_INIT(tmp);
ulong file_size= 0;
if (decrease_log_space) //stat the file we want to delete
{
MY_STAT s;
if (my_stat(log_info.log_file_name,&s,MYF(0)))
tmp= s.st_size;
else
{
/*
If we could not stat, we can't know the amount
of space that deletion will free. In most cases,
deletion won't work either, so it's not a problem.
*/
tmp= 0;
}
if (my_stat(log_info.log_file_name,&s,MYF(0)))
file_size= s.st_size;
}
/*
It's not fatal if we can't delete a log file ;
......@@ -859,7 +899,7 @@ int MYSQL_LOG::purge_logs(const char *to_log,
*/
DBUG_PRINT("info",("purging %s",log_info.log_file_name));
if (!my_delete(log_info.log_file_name, MYF(0)) && decrease_log_space)
*decrease_log_space-= tmp;
*decrease_log_space-= file_size;
if (find_next_log(&log_info, 0) || exit_loop)
break;
}
......@@ -1024,9 +1064,8 @@ void MYSQL_LOG::new_file(bool need_lock)
*/
THD *thd = current_thd; /* may be 0 if we are reacting to SIGHUP */
Rotate_log_event r(thd,new_name+dirname_length(new_name));
r.set_log_pos(this);
r.write(&log_file);
bytes_written += r.get_event_len();
bytes_written += r.data_written;
}
/*
Update needs to be signalled even if there is no rotate event
......@@ -1044,8 +1083,17 @@ void MYSQL_LOG::new_file(bool need_lock)
Note that at this point, log_type != LOG_CLOSED (important for is_open()).
*/
/*
new_file() is only used for rotation (in FLUSH LOGS or because size >
max_binlog_size or max_relay_log_size).
If this is a binary log, the Format_description_log_event at the beginning of
the new file should have created=0 (to distinguish with the
Format_description_log_event written at server startup, which should
trigger temp tables deletion on slaves.
*/
open(old_name, save_log_type, new_name_ptr, index_file_name, io_cache_type,
no_auto_events, max_size);
no_auto_events, max_size, 1);
if (this == &mysql_bin_log)
report_pos_in_innodb();
my_free(old_name,MYF(0));
......@@ -1076,7 +1124,7 @@ bool MYSQL_LOG::append(Log_event* ev)
error=1;
goto err;
}
bytes_written += ev->get_event_len();
bytes_written+= ev->data_written;
DBUG_PRINT("info",("max_size: %lu",max_size));
if ((uint) my_b_append_tell(&log_file) > max_size)
{
......@@ -1129,7 +1177,7 @@ bool MYSQL_LOG::appendv(const char* buf, uint len,...)
/*
Write to normal (not rotable) log
This is the format for the 'normal', 'slow' and 'update' logs.
This is the format for the 'normal' log.
*/
bool MYSQL_LOG::write(THD *thd,enum enum_server_command command,
......@@ -1220,11 +1268,15 @@ bool MYSQL_LOG::write(THD *thd,enum enum_server_command command,
inline bool sync_binlog(IO_CACHE *cache)
{
return (sync_binlog_period &&
(sync_binlog_period == ++sync_binlog_counter) &&
(sync_binlog_counter= 0, my_sync(cache->file, MYF(MY_WME))));
if (sync_binlog_period == ++sync_binlog_counter && sync_binlog_period)
{
sync_binlog_counter= 0;
return my_sync(cache->file, MYF(MY_WME));
}
return 0;
}
/*
Write an event to the binary log
*/
......@@ -1292,7 +1344,7 @@ bool MYSQL_LOG::write(Log_event* event_info)
if (thd)
{
#if MYSQL_VERSION_ID < 50000
/* NOTE: CHARSET AND TZ REPL WILL BE REWRITTEN SHORTLY */
/*
To make replication of charsets working in 4.1 we are writing values
of charset related variables before every statement in the binlog,
......@@ -1318,7 +1370,6 @@ COLLATION_CONNECTION=%u,COLLATION_DATABASE=%u,COLLATION_SERVER=%u",
(uint) thd->variables.collation_database->number,
(uint) thd->variables.collation_server->number);
Query_log_event e(thd, buf, written, 0);
e.set_log_pos(this);
if (e.write(file))
goto err;
}
......@@ -1334,31 +1385,26 @@ COLLATION_CONNECTION=%u,COLLATION_DATABASE=%u,COLLATION_SERVER=%u",
thd->variables.time_zone->get_name()->ptr(),
"'", NullS);
Query_log_event e(thd, buf, buf_end - buf, 0);
e.set_log_pos(this);
if (e.write(file))
goto err;
}
#endif
if (thd->last_insert_id_used)
{
Intvar_log_event e(thd,(uchar) LAST_INSERT_ID_EVENT,
thd->current_insert_id);
e.set_log_pos(this);
if (e.write(file))
goto err;
}
if (thd->insert_id_used)
{
Intvar_log_event e(thd,(uchar) INSERT_ID_EVENT,thd->last_insert_id);
e.set_log_pos(this);
if (e.write(file))
goto err;
}
if (thd->rand_used)
{
Rand_log_event e(thd,thd->rand_saved_seed1,thd->rand_saved_seed2);
e.set_log_pos(this);
if (e.write(file))
goto err;
}
......@@ -1374,7 +1420,6 @@ COLLATION_CONNECTION=%u,COLLATION_DATABASE=%u,COLLATION_SERVER=%u",
user_var_event->length,
user_var_event->type,
user_var_event->charset_number);
e.set_log_pos(this);
if (e.write(file))
goto err;
}
......@@ -1386,48 +1431,17 @@ COLLATION_CONNECTION=%u,COLLATION_DATABASE=%u,COLLATION_SERVER=%u",
p= strmov(strmov(buf, "SET CHARACTER SET "),
thd->variables.convert_set->name);
Query_log_event e(thd, buf, (ulong) (p - buf), 0);
e.set_log_pos(this);
if (e.write(file))
goto err;
}
#endif
/*
If the user has set FOREIGN_KEY_CHECKS=0 we wrap every SQL
command in the binlog inside:
SET FOREIGN_KEY_CHECKS=0;
<command>;
SET FOREIGN_KEY_CHECKS=1;
*/
if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS)
{
Query_log_event e(thd, "SET FOREIGN_KEY_CHECKS=0", 24, 0);
e.set_log_pos(this);
if (e.write(file))
goto err;
}
}
/* Write the SQL command */
event_info->set_log_pos(this);
if (event_info->write(file))
goto err;
/* Write log events to reset the 'run environment' of the SQL command */
if (thd)
{
if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS)
{
Query_log_event e(thd, "SET FOREIGN_KEY_CHECKS=1", 24, 0);
e.set_log_pos(this);
if (e.write(file))
goto err;
}
}
/*
Tell for transactional table handlers up to which position in the
binlog file we wrote. The table handler can store this info, and
......@@ -1604,7 +1618,6 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, bool commit_or_rollback)
master's binlog, which would result in wrong positions being shown to
the user, MASTER_POS_WAIT undue waiting etc.
*/
qinfo.set_log_pos(this);
if (qinfo.write(&log_file))
goto err;
}
......@@ -1630,7 +1643,6 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, bool commit_or_rollback)
commit_or_rollback ? "COMMIT" : "ROLLBACK",
commit_or_rollback ? 6 : 8,
TRUE);
qinfo.set_log_pos(this);
if (qinfo.write(&log_file) || flush_io_cache(&log_file) ||
sync_binlog(&log_file))
goto err;
......@@ -1692,8 +1704,7 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, bool commit_or_rollback)
/*
Write update log in a format suitable for incremental backup
This is also used by the slow query log.
Write to the slow query log.
*/
bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
......@@ -1877,8 +1888,8 @@ void MYSQL_LOG::close(uint exiting)
(exiting & LOG_CLOSE_STOP_EVENT))
{
Stop_log_event s;
s.set_log_pos(this);
s.write(&log_file);
bytes_written+= s.data_written;
signal_update();
}
#endif /* HAVE_REPLICATION */
......
......@@ -180,10 +180,12 @@ static void cleanup_load_tmpdir()
write_str()
*/
static bool write_str(IO_CACHE *file, char *str, byte length)
static bool write_str(IO_CACHE *file, char *str, uint length)
{
return (my_b_safe_write(file, &length, 1) ||
my_b_safe_write(file, (byte*) str, (int) length));
byte tmp[1];
tmp[0]= (byte) length;
return (my_b_safe_write(file, tmp, sizeof(tmp)) ||
my_b_safe_write(file, (byte*) str, length));
}
......@@ -191,17 +193,18 @@ static bool write_str(IO_CACHE *file, char *str, byte length)
read_str()
*/
static inline int read_str(char * &buf, char *buf_end, char * &str,
uint8 &len)
static inline int read_str(char **buf, char *buf_end, char **str,
uint8 *len)
{
if (buf + (uint) (uchar) *buf >= buf_end)
if (*buf + ((uint) (uchar) **buf) >= buf_end)
return 1;
len = (uint8) *buf;
str= buf+1;
buf+= (uint) len+1;
*len= (uint8) **buf;
*str= (*buf)+1;
(*buf)+= (uint) *len+1;
return 0;
}
/*
Transforms a string into "" or its expression in 0x... form.
*/
......@@ -225,9 +228,25 @@ static char *str_to_hex(char *to, char *from, uint len)
return p; // pointer to end 0 of 'to'
}
/*
Prints a "session_var=value" string. Used by mysqlbinlog to print some SET
commands just before it prints a query.
*/
static void print_set_option(FILE* file, uint32 bits_changed, uint32 option,
uint32 flags, const char* name, bool* need_comma)
{
if (bits_changed & option)
{
if (*need_comma)
fprintf(file,", ");
fprintf(file,"%s=%d", name, (bool)(flags & option));
*need_comma= 1;
}
}
/**************************************************************************
Log_event methods
Log_event methods (= the parent class of all events)
**************************************************************************/
/*
......@@ -237,7 +256,7 @@ static char *str_to_hex(char *to, char *from, uint len)
const char* Log_event::get_type_str()
{
switch(get_type_code()) {
case START_EVENT: return "Start";
case START_EVENT_V3: return "Start_v3";
case STOP_EVENT: return "Stop";
case QUERY_EVENT: return "Query";
case ROTATE_EVENT: return "Rotate";
......@@ -251,6 +270,7 @@ const char* Log_event::get_type_str()
case EXEC_LOAD_EVENT: return "Exec_load";
case RAND_EVENT: return "RAND";
case USER_VAR_EVENT: return "User var";
case FORMAT_DESCRIPTION_EVENT: return "Format_desc";
default: return "Unknown"; /* impossible */
}
}
......@@ -262,8 +282,7 @@ const char* Log_event::get_type_str()
#ifndef MYSQL_CLIENT
Log_event::Log_event(THD* thd_arg, uint16 flags_arg, bool using_trans)
:log_pos(0), temp_buf(0), exec_time(0), cached_event_len(0),
flags(flags_arg), thd(thd_arg)
:log_pos(0), temp_buf(0), exec_time(0), flags(flags_arg), thd(thd_arg)
{
server_id= thd->server_id;
when= thd->start_time;
......@@ -280,7 +299,7 @@ Log_event::Log_event(THD* thd_arg, uint16 flags_arg, bool using_trans)
*/
Log_event::Log_event()
:temp_buf(0), exec_time(0), cached_event_len(0), flags(0), cache_stmt(0),
:temp_buf(0), exec_time(0), flags(0), cache_stmt(0),
thd(0)
{
server_id= ::server_id;
......@@ -294,24 +313,70 @@ Log_event::Log_event()
Log_event::Log_event()
*/
Log_event::Log_event(const char* buf, bool old_format)
:temp_buf(0), cached_event_len(0), cache_stmt(0)
Log_event::Log_event(const char* buf,
const Format_description_log_event* description_event)
:temp_buf(0), cache_stmt(0)
{
#ifndef MYSQL_CLIENT
thd = 0;
#endif
when = uint4korr(buf);
server_id = uint4korr(buf + SERVER_ID_OFFSET);
if (old_format)
if (description_event->binlog_version==1)
{
log_pos=0;
flags=0;
log_pos= 0;
flags= 0;
return;
}
else
/* 4.0 or newer */
log_pos= uint4korr(buf + LOG_POS_OFFSET);
/*
If the log is 4.0 (so here it can only be a 4.0 relay log read by the SQL
thread or a 4.0 master binlog read by the I/O thread), log_pos is the
beginning of the event: we transform it into the end of the event, which is
more useful.
But how do you know that the log is 4.0: you know it if description_event
is version 3 *and* you are not reading a Format_desc (remember that
mysqlbinlog starts by assuming that 5.0 logs are in 4.0 format, until it
finds a Format_desc).
*/
if (description_event->binlog_version==3 &&
buf[EVENT_TYPE_OFFSET]<FORMAT_DESCRIPTION_EVENT && log_pos)
{
log_pos = uint4korr(buf + LOG_POS_OFFSET);
flags = uint2korr(buf + FLAGS_OFFSET);
/*
If log_pos=0, don't change it. log_pos==0 is a marker to mean
"don't change rli->group_master_log_pos" (see
inc_group_relay_log_pos()). As it is unreal log_pos, adding the event
len's is nonsense. For example, a fake Rotate event should
not have its log_pos (which is 0) changed or it will modify
Exec_master_log_pos in SHOW SLAVE STATUS, displaying a nonsense value
of (a non-zero offset which does not exist in the master's binlog, so
which will cause problems if the user uses this value in
CHANGE MASTER).
*/
log_pos+= uint4korr(buf + EVENT_LEN_OFFSET);
}
#ifndef MYSQL_CLIENT
thd = 0;
#endif
DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));
flags= uint2korr(buf + FLAGS_OFFSET);
if ((buf[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT) ||
(buf[EVENT_TYPE_OFFSET] == ROTATE_EVENT))
{
/*
These events always have a header which stops here (i.e. their header is
FROZEN).
*/
/*
Initialization to zero of all other Log_event members as they're not
specified. Currently there are no such members; in the future there will
be an event UID (but Format_description and Rotate don't need this UID,
as they are not propagated through --log-slave-updates (remember the UID
is used to not play a query twice when you have two masters which are
slaves of a 3rd master). Then we are done.
*/
return;
}
/* otherwise, go on with reading the header from buf (nothing now) */
}
#ifndef MYSQL_CLIENT
......@@ -364,10 +429,10 @@ int Log_event::exec_event(struct st_relay_log_info* rli)
has already been updated.
*/
if ((thd->options & OPTION_BEGIN) && opt_using_transactions)
rli->inc_event_relay_log_pos(get_event_len());
rli->inc_event_relay_log_pos();
else
{
rli->inc_group_relay_log_pos(get_event_len(),log_pos);
rli->inc_group_relay_log_pos(log_pos);
flush_relay_log_info(rli);
/*
Note that Rotate_log_event::exec_event() does not call this function,
......@@ -429,49 +494,95 @@ void Log_event::init_show_field_list(List<Item>* field_list)
field_list->push_back(new Item_empty_string("Event_type", 20));
field_list->push_back(new Item_return_int("Server_id", 10,
MYSQL_TYPE_LONG));
field_list->push_back(new Item_return_int("Orig_log_pos", 11,
field_list->push_back(new Item_return_int("End_log_pos", 11,
MYSQL_TYPE_LONGLONG));
field_list->push_back(new Item_empty_string("Info", 20));
}
#endif /* !MYSQL_CLIENT */
/*
Log_event::write()
*/
int Log_event::write(IO_CACHE* file)
bool Log_event::write_header(IO_CACHE* file, ulong event_data_length)
{
return (write_header(file) || write_data(file)) ? -1 : 0;
}
byte header[LOG_EVENT_HEADER_LEN];
DBUG_ENTER("Log_event::write_header");
/* Store number of bytes that will be written by this event */
data_written= event_data_length + sizeof(header);
/*
Log_event::write_header()
*/
/*
log_pos != 0 if this is relay-log event. In this case we should not
change the position
*/
int Log_event::write_header(IO_CACHE* file)
{
char buf[LOG_EVENT_HEADER_LEN];
char* pos = buf;
int4store(pos, (ulong) when); // timestamp
pos += 4;
*pos++ = get_type_code(); // event type code
int4store(pos, server_id);
pos += 4;
long tmp=get_data_size() + LOG_EVENT_HEADER_LEN;
int4store(pos, tmp);
pos += 4;
int4store(pos, log_pos);
pos += 4;
int2store(pos, flags);
pos += 2;
return (my_b_safe_write(file, (byte*) buf, (uint) (pos - buf)));
if (is_artificial_event())
{
/*
We should not do any cleanup on slave when reading this. We
mark this by setting log_pos to 0. Start_log_event_v3() will
detect this on reading and set artificial_event=1 for the event.
*/
log_pos= 0;
}
else if (!log_pos)
{
/*
Calculate position of end of event
Note that with a SEQ_READ_APPEND cache, my_b_tell() does not
work well. So this will give slightly wrong positions for the
Format_desc/Rotate/Stop events which the slave writes to its
relay log. For example, the initial Format_desc will have
end_log_pos=91 instead of 95. Because after writing the first 4
bytes of the relay log, my_b_tell() still reports 0. Because
my_b_append() does not update the counter which my_b_tell()
later uses (one should probably use my_b_append_tell() to work
around this). To get right positions even when writing to the
relay log, we use the (new) my_b_safe_tell().
Note that this raises a question on the correctness of all these
DBUG_ASSERT(my_b_tell()=rli->event_relay_log_pos).
If in a transaction, the log_pos which we calculate below is not
very good (because then my_b_safe_tell() returns start position
of the BEGIN, so it's like the statement was at the BEGIN's
place), but it's not a very serious problem (as the slave, when
it is in a transaction, does not take those end_log_pos into
account (as it calls inc_event_relay_log_pos()). To be fixed
later, so that it looks less strange. But not bug.
*/
log_pos= my_b_safe_tell(file)+data_written;
}
/*
Header will be of size LOG_EVENT_HEADER_LEN for all events, except for
FORMAT_DESCRIPTION_EVENT and ROTATE_EVENT, where it will be
LOG_EVENT_MINIMAL_HEADER_LEN (remember these 2 have a frozen header,
because we read them before knowing the format).
*/
int4store(header, (ulong) when); // timestamp
header[EVENT_TYPE_OFFSET]= get_type_code();
int4store(header+ SERVER_ID_OFFSET, server_id);
int4store(header+ EVENT_LEN_OFFSET, data_written);
int4store(header+ LOG_POS_OFFSET, log_pos);
int2store(header+ FLAGS_OFFSET, flags);
DBUG_RETURN(my_b_safe_write(file, header, sizeof(header)) != 0);
}
/*
Log_event::read_log_event()
This needn't be format-tolerant, because we only read
LOG_EVENT_MINIMAL_HEADER_LEN (we just want to read the event's length).
*/
#ifndef MYSQL_CLIENT
......@@ -480,7 +591,7 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet,
{
ulong data_len;
int result=0;
char buf[LOG_EVENT_HEADER_LEN];
char buf[LOG_EVENT_MINIMAL_HEADER_LEN];
DBUG_ENTER("read_log_event");
if (log_lock)
......@@ -500,24 +611,25 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet,
goto end;
}
data_len= uint4korr(buf + EVENT_LEN_OFFSET);
if (data_len < LOG_EVENT_HEADER_LEN ||
if (data_len < LOG_EVENT_MINIMAL_HEADER_LEN ||
data_len > current_thd->variables.max_allowed_packet)
{
DBUG_PRINT("error",("data_len: %ld", data_len));
result= ((data_len < LOG_EVENT_HEADER_LEN) ? LOG_READ_BOGUS :
result= ((data_len < LOG_EVENT_MINIMAL_HEADER_LEN) ? LOG_READ_BOGUS :
LOG_READ_TOO_LARGE);
goto end;
}
packet->append(buf, sizeof(buf));
data_len-= LOG_EVENT_HEADER_LEN;
data_len-= LOG_EVENT_MINIMAL_HEADER_LEN;
if (data_len)
{
if (packet->append(file, data_len))
{
/*
Here we should never hit EOF in a non-error condition.
Here if we hit EOF it's really an error: as data_len is >=0
there's supposed to be more bytes available.
EOF means we are reading the event partially, which should
never happen.
never happen: either we read badly or the binlog is truncated.
*/
result= file->error >= 0 ? LOG_READ_TRUNC: LOG_READ_IO;
/* Implicit goto end; */
......@@ -545,24 +657,42 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet,
Log_event::read_log_event()
NOTE:
Allocates memory; The caller is responsible for clean-up
Allocates memory; The caller is responsible for clean-up.
*/
#ifndef MYSQL_CLIENT
Log_event* Log_event::read_log_event(IO_CACHE* file,
pthread_mutex_t* log_lock,
bool old_format)
const Format_description_log_event *description_event)
#else
Log_event* Log_event::read_log_event(IO_CACHE* file, bool old_format)
Log_event* Log_event::read_log_event(IO_CACHE* file,
const Format_description_log_event *description_event)
#endif
{
char head[LOG_EVENT_HEADER_LEN];
uint header_size= old_format ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN;
DBUG_ASSERT(description_event);
char head[LOG_EVENT_MINIMAL_HEADER_LEN];
/*
First we only want to read at most LOG_EVENT_MINIMAL_HEADER_LEN, just to
check the event for sanity and to know its length; no need to really parse
it. We say "at most" because this could be a 3.23 master, which has header
of 13 bytes, whereas LOG_EVENT_MINIMAL_HEADER_LEN is 19 bytes (it's "minimal"
over the set {MySQL >=4.0}).
*/
uint header_size= min(description_event->common_header_len,
LOG_EVENT_MINIMAL_HEADER_LEN);
LOCK_MUTEX;
DBUG_PRINT("info", ("my_b_tell=%lu", my_b_tell(file)));
if (my_b_read(file, (byte *) head, header_size))
{
DBUG_PRINT("info", ("Log_event::read_log_event(IO_CACHE*,Format_desc*) \
failed my_b_read"));
UNLOCK_MUTEX;
/*
No error here; it could be that we are at the file's end. However if the
next my_b_read() fails (below), it will be an error as we were able to
read the first bytes.
*/
return 0;
}
......@@ -596,7 +726,8 @@ Log_event* Log_event::read_log_event(IO_CACHE* file, bool old_format)
error = "read error";
goto err;
}
if ((res = read_log_event(buf, data_len, &error, old_format)))
if ((res= read_log_event(buf, data_len, &error,
description_event)))
res->register_temp_buf(buf);
err:
......@@ -623,13 +754,18 @@ Error in Log_event::read_log_event(): '%s', data_len: %d, event_type: %d",
/*
Log_event::read_log_event()
Binlog format tolerance is in (buf, event_len, description_event)
constructors.
*/
Log_event* Log_event::read_log_event(const char* buf, int event_len,
const char **error, bool old_format)
Log_event* Log_event::read_log_event(const char* buf, uint event_len,
const char **error,
const Format_description_log_event *description_event)
{
DBUG_ENTER("Log_event::read_log_event");
Log_event* ev;
DBUG_ENTER("Log_event::read_log_event(char*,...)");
DBUG_ASSERT(description_event);
DBUG_PRINT("info", ("binlog_version: %d", description_event->binlog_version));
if (event_len < EVENT_LEN_OFFSET ||
(uint) event_len != uint4korr(buf+EVENT_LEN_OFFSET))
{
......@@ -637,74 +773,87 @@ Log_event* Log_event::read_log_event(const char* buf, int event_len,
DBUG_RETURN(NULL); // general sanity check - will fail on a partial read
}
Log_event* ev = NULL;
switch(buf[EVENT_TYPE_OFFSET]) {
case QUERY_EVENT:
ev = new Query_log_event(buf, event_len, old_format);
ev = new Query_log_event(buf, event_len, description_event);
break;
case LOAD_EVENT:
ev = new Create_file_log_event(buf, event_len, old_format);
ev = new Create_file_log_event(buf, event_len, description_event);
break;
case NEW_LOAD_EVENT:
ev = new Load_log_event(buf, event_len, old_format);
ev = new Load_log_event(buf, event_len, description_event);
break;
case ROTATE_EVENT:
ev = new Rotate_log_event(buf, event_len, old_format);
ev = new Rotate_log_event(buf, event_len, description_event);
break;
#ifdef HAVE_REPLICATION
case SLAVE_EVENT:
case SLAVE_EVENT: /* can never happen (unused event) */
ev = new Slave_log_event(buf, event_len);
break;
#endif /* HAVE_REPLICATION */
case CREATE_FILE_EVENT:
ev = new Create_file_log_event(buf, event_len, old_format);
ev = new Create_file_log_event(buf, event_len, description_event);
break;
case APPEND_BLOCK_EVENT:
ev = new Append_block_log_event(buf, event_len);
ev = new Append_block_log_event(buf, event_len, description_event);
break;
case DELETE_FILE_EVENT:
ev = new Delete_file_log_event(buf, event_len);
ev = new Delete_file_log_event(buf, event_len, description_event);
break;
case EXEC_LOAD_EVENT:
ev = new Execute_load_log_event(buf, event_len);
ev = new Execute_load_log_event(buf, event_len, description_event);
break;
case START_EVENT:
ev = new Start_log_event(buf, old_format);
case START_EVENT_V3: /* this is sent only by MySQL <=4.x */
ev = new Start_log_event_v3(buf, description_event);
break;
#ifdef HAVE_REPLICATION
case STOP_EVENT:
ev = new Stop_log_event(buf, old_format);
ev = new Stop_log_event(buf, description_event);
break;
#endif /* HAVE_REPLICATION */
case INTVAR_EVENT:
ev = new Intvar_log_event(buf, old_format);
ev = new Intvar_log_event(buf, description_event);
break;
case RAND_EVENT:
ev = new Rand_log_event(buf, old_format);
ev = new Rand_log_event(buf, description_event);
break;
case USER_VAR_EVENT:
ev = new User_var_log_event(buf, old_format);
ev = new User_var_log_event(buf, description_event);
break;
case FORMAT_DESCRIPTION_EVENT:
ev = new Format_description_log_event(buf, event_len, description_event);
break;
default:
DBUG_PRINT("error",("Unknown evernt code: %d",(int) buf[EVENT_TYPE_OFFSET]));
ev= NULL;
break;
}
/*
is_valid() are small event-specific sanity tests which are important; for
example there are some my_malloc() in constructors
(e.g. Query_log_event::Query_log_event(char*...)); when these my_malloc()
fail we can't return an error out of the constructor (because constructor
is "void") ; so instead we leave the pointer we wanted to allocate
(e.g. 'query') to 0 and we test it in is_valid(). Same for
Format_description_log_event, member 'post_header_len'.
*/
if (!ev || !ev->is_valid())
{
DBUG_PRINT("error",("Found invalid event in binary log"));
delete ev;
#ifdef MYSQL_CLIENT
if (!force_opt)
if (!force_opt) /* then mysqlbinlog dies */
{
*error= "Found invalid event in binary log";
DBUG_RETURN(0);
}
ev= new Unknown_log_event(buf, old_format);
ev= new Unknown_log_event(buf, description_event);
#else
*error= "Found invalid event in binary log";
DBUG_RETURN(0);
#endif
}
ev->cached_event_len = event_len;
DBUG_RETURN(ev);
}
......@@ -719,7 +868,7 @@ void Log_event::print_header(FILE* file)
char llbuff[22];
fputc('#', file);
print_timestamp(file);
fprintf(file, " server id %d log_pos %s ", server_id,
fprintf(file, " server id %d end_log_pos %s ", server_id,
llstr(log_pos,llbuff));
}
......@@ -751,19 +900,6 @@ void Log_event::print_timestamp(FILE* file, time_t* ts)
#endif /* MYSQL_CLIENT */
/*
Log_event::set_log_pos()
*/
#ifndef MYSQL_CLIENT
void Log_event::set_log_pos(MYSQL_LOG* log)
{
if (!log_pos)
log_pos = my_b_tell(&log->log_file);
}
#endif /* !MYSQL_CLIENT */
/**************************************************************************
Query_log_event methods
**************************************************************************/
......@@ -772,10 +908,15 @@ void Log_event::set_log_pos(MYSQL_LOG* log)
/*
Query_log_event::pack_info()
This (which is used only for SHOW BINLOG EVENTS) could be updated to
print SET @@session_var=. But this is not urgent, as SHOW BINLOG EVENTS is
only an information, it does not produce suitable queries to replay (for
example it does not print LOAD DATA INFILE).
*/
void Query_log_event::pack_info(Protocol *protocol)
{
// TODO: show the catalog ??
char *buf, *pos;
if (!(buf= my_malloc(9 + db_len + q_len, MYF(MY_WME))))
return;
......@@ -799,31 +940,27 @@ void Query_log_event::pack_info(Protocol *protocol)
/*
Query_log_event::write()
*/
int Query_log_event::write(IO_CACHE* file)
{
return query ? Log_event::write(file) : -1;
}
/*
Query_log_event::write_data()
NOTES:
In this event we have to modify the header to have the correct
EVENT_LEN_OFFSET as we don't yet know how many status variables we
will print!
*/
int Query_log_event::write_data(IO_CACHE* file)
bool Query_log_event::write(IO_CACHE* file)
{
char buf[QUERY_HEADER_LEN];
uchar buf[QUERY_HEADER_LEN+1+4+1+8+1+1+FN_REFLEN+5], *start, *start_of_status;
ulong event_length;
if (!query)
return -1;
return 1; // Something wrong with event
/*
We want to store the thread id:
(- as an information for the user when he reads the binlog)
- if the query uses temporary table: for the slave SQL thread to know to
which master connection the temp table belongs.
Now imagine we (write_data()) are called by the slave SQL thread (we are
Now imagine we (write()) are called by the slave SQL thread (we are
logging a query executed by this thread; the slave runs with
--log-slave-updates). Then this query will be logged with
thread_id=the_thread_id_of_the_SQL_thread. Imagine that 2 temp tables of
......@@ -860,9 +997,77 @@ int Query_log_event::write_data(IO_CACHE* file)
buf[Q_DB_LEN_OFFSET] = (char) db_len;
int2store(buf + Q_ERR_CODE_OFFSET, error_code);
return (my_b_safe_write(file, (byte*) buf, QUERY_HEADER_LEN) ||
/*
You MUST always write status vars in increasing order of code. This
guarantees that a slightly older slave will be able to parse those he
knows.
*/
start_of_status= start= buf+QUERY_HEADER_LEN;
if (flags2_inited)
{
*(start++)= Q_FLAGS2_CODE;
int4store(start, flags2);
start+= 4;
}
if (sql_mode_inited)
{
*(start++)= Q_SQL_MODE_CODE;
int8store(start, sql_mode);
start+= 8;
}
if (catalog_len >= 0) // i.e. "catalog inited" (false for 4.0 events)
{
*(start++)= Q_CATALOG_CODE;
*(start++)= (uchar) catalog_len;
bmove(start, catalog, catalog_len);
start+= catalog_len;
/*
We write a \0 at the end. As we also have written the length, it's
apparently useless; but in fact it enables us to just do
catalog= a_pointer_to_the_buffer_of_the_read_event
later in the slave SQL thread.
If we didn't have the \0, we would need to memdup to build the catalog in
the slave SQL thread.
And still the interest of having the length too is that in the slave SQL
thread we immediately know at which position the catalog ends (no need to
search for '\0'. In other words: length saves search, \0 saves mem alloc,
at the cost of 1 redundant byte on the disk.
Note that this is only a fix until we change 'catalog' to LEX_STRING
(then we won't need the \0).
*/
*(start++)= '\0';
}
if (auto_increment_increment != 1)
{
*start++= Q_AUTO_INCREMENT;
int2store(start, auto_increment_increment);
int2store(start+2, auto_increment_offset);
start+= 4;
}
/*
Here there could be code like
if (command-line-option-which-says-"log_this_variable")
{
*(start++)= Q_THIS_VARIABLE_CODE;
int4store(start, this_variable);
start+= 4;
}
*/
/* Store length of status variables */
status_vars_len= (uint) (start-start_of_status);
int2store(buf + Q_STATUS_VARS_LEN_OFFSET, status_vars_len);
/*
Calculate length of whole event
The "1" below is the \0 in the db's length
*/
event_length= (uint) (start-buf) + db_len + 1 + q_len;
return (write_header(file, event_length) ||
my_b_safe_write(file, (byte*) buf, (uint) (start-buf)) ||
my_b_safe_write(file, (db) ? (byte*) db : (byte*)"", db_len + 1) ||
my_b_safe_write(file, (byte*) query, q_len)) ? -1 : 0;
my_b_safe_write(file, (byte*) query, q_len)) ? 1 : 0;
}
......@@ -875,60 +1080,162 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
ulong query_length, bool using_trans)
:Log_event(thd_arg, !thd_arg->tmp_table_used ?
0 : LOG_EVENT_THREAD_SPECIFIC_F, using_trans),
data_buf(0), query(query_arg),
data_buf(0), query(query_arg), catalog(thd_arg->catalog),
db(thd_arg->db), q_len((uint32) query_length),
error_code(thd_arg->killed ?
error_code((thd_arg->killed != THD::NOT_KILLED) ?
((thd_arg->system_thread & SYSTEM_THREAD_DELAYED_INSERT) ?
0 : ER_SERVER_SHUTDOWN) : thd_arg->net.last_errno),
0 : thd->killed_errno()) : thd_arg->net.last_errno),
thread_id(thd_arg->thread_id),
/* save the original thread id; we already know the server id */
slave_proxy_id(thd_arg->variables.pseudo_thread_id)
slave_proxy_id(thd_arg->variables.pseudo_thread_id),
flags2_inited(1), sql_mode_inited(1), flags2(0),
sql_mode(thd_arg->variables.sql_mode),
auto_increment_increment(thd_arg->variables.auto_increment_increment),
auto_increment_offset(thd_arg->variables.auto_increment_offset)
{
time_t end_time;
time(&end_time);
exec_time = (ulong) (end_time - thd->start_time);
catalog_len = (catalog) ? (uint32) strlen(catalog) : 0;
status_vars_len= 1+4+1+8+1+1+catalog_len+1;
db_len = (db) ? (uint32) strlen(db) : 0;
/*
If we don't use flags2 for anything else than options contained in
thd->options, it would be more efficient to flags2=thd_arg->options
(OPTIONS_WRITTEN_TO_BINLOG would be used only at reading time).
But it's likely that we don't want to use 32 bits for 3 bits; in the future
we will probably want to reclaim the 29 bits. So we need the &.
*/
flags2= thd_arg->options & OPTIONS_WRITTEN_TO_BIN_LOG;
DBUG_PRINT("info",("Query_log_event has flags2=%lu sql_mode=%lu",flags2,sql_mode));
}
#endif /* MYSQL_CLIENT */
/*
Query_log_event::Query_log_event()
This is used by the SQL slave thread to prepare the event before execution.
*/
Query_log_event::Query_log_event(const char* buf, int event_len,
bool old_format)
:Log_event(buf, old_format),data_buf(0), query(NULL), db(NULL)
Query_log_event::Query_log_event(const char* buf, uint event_len,
const Format_description_log_event *description_event)
:Log_event(buf, description_event), data_buf(0), query(NullS), catalog(NullS),
db(NullS), catalog_len(0), status_vars_len(0),
flags2_inited(0), sql_mode_inited(0)
{
ulong data_len;
if (old_format)
uint32 tmp;
uint8 common_header_len, post_header_len;
const char *start, *end;
DBUG_ENTER("Query_log_event::Query_log_event(char*,...)");
common_header_len= description_event->common_header_len;
post_header_len= description_event->post_header_len[QUERY_EVENT-1];
DBUG_PRINT("info",("event_len=%ld, common_header_len=%d, post_header_len=%d",
event_len, common_header_len, post_header_len));
/*
We test if the event's length is sensible, and if so we compute data_len.
We cannot rely on QUERY_HEADER_LEN here as it would not be format-tolerant.
We use QUERY_HEADER_MINIMAL_LEN which is the same for 3.23, 4.0 & 5.0.
*/
if (event_len < (uint)(common_header_len + post_header_len))
DBUG_VOID_RETURN;
data_len = event_len - (common_header_len + post_header_len);
buf+= common_header_len;
slave_proxy_id= thread_id = uint4korr(buf + Q_THREAD_ID_OFFSET);
exec_time = uint4korr(buf + Q_EXEC_TIME_OFFSET);
db_len = (uint)buf[Q_DB_LEN_OFFSET];
error_code = uint2korr(buf + Q_ERR_CODE_OFFSET);
/* If auto_increment is not set by query_event, they should not be used */
auto_increment_increment= auto_increment_offset= 1;
/*
5.0 format starts here.
Depending on the format, we may or not have affected/warnings etc
The remnent post-header to be parsed has length:
*/
tmp= post_header_len - QUERY_HEADER_MINIMAL_LEN;
if (tmp)
{
if ((uint)event_len < OLD_HEADER_LEN + QUERY_HEADER_LEN)
return;
data_len = event_len - (QUERY_HEADER_LEN + OLD_HEADER_LEN);
buf += OLD_HEADER_LEN;
status_vars_len= uint2korr(buf + Q_STATUS_VARS_LEN_OFFSET);
data_len-= status_vars_len;
DBUG_PRINT("info", ("Query_log_event has status_vars_len: %u",
(uint) status_vars_len));
tmp-= 2;
}
else
/* we have parsed everything we know in the post header */
#ifndef DBUG_OFF
if (tmp) /* this is probably a master newer than us */
DBUG_PRINT("info", ("Query_log_event has longer post header than we know\
(%d more bytes)", tmp));
#endif
/* variable-part: the status vars; only in MySQL 5.0 */
start= (char*) (buf+post_header_len);
end= (char*) (start+status_vars_len);
for (const uchar* pos= (const uchar*) start; pos < (const uchar*) end;)
{
if ((uint)event_len < QUERY_EVENT_OVERHEAD)
return;
data_len = event_len - QUERY_EVENT_OVERHEAD;
buf += LOG_EVENT_HEADER_LEN;
switch (*pos++) {
case Q_FLAGS2_CODE:
flags2_inited= 1;
flags2= uint4korr(pos);
DBUG_PRINT("info",("In Query_log_event, read flags2: %lu", flags2));
pos+= 4;
break;
case Q_SQL_MODE_CODE:
{
#ifndef DBUG_OFF
char buff[22];
#endif
sql_mode_inited= 1;
sql_mode= (ulong) uint8korr(pos); // QQ: Fix when sql_mode is ulonglong
DBUG_PRINT("info",("In Query_log_event, read sql_mode: %s",
llstr(sql_mode, buff)));
pos+= 8;
break;
}
case Q_CATALOG_CODE:
catalog_len= *pos;
if (catalog_len)
catalog= (char*) pos+1; // Will be copied later
pos+= catalog_len+2;
break;
case Q_AUTO_INCREMENT:
auto_increment_increment= uint2korr(pos);
auto_increment_offset= uint2korr(pos+2);
pos+= 4;
break;
default:
/* That's why you must write status vars in growing order of code */
DBUG_PRINT("info",("Query_log_event has unknown status vars (first has\
code: %u), skipping the rest of them", (uint) *(pos-1)));
pos= (const uchar*) end; // Break look
}
}
exec_time = uint4korr(buf + Q_EXEC_TIME_OFFSET);
error_code = uint2korr(buf + Q_ERR_CODE_OFFSET);
if (!(data_buf = (char*) my_malloc(data_len + 1, MYF(MY_WME))))
return;
/* A 2nd variable part; this is common to all versions */
memcpy(data_buf, buf + Q_DATA_OFFSET, data_len);
slave_proxy_id= thread_id= uint4korr(buf + Q_THREAD_ID_OFFSET);
db = data_buf;
db_len = (uint)buf[Q_DB_LEN_OFFSET];
query=data_buf + db_len + 1;
q_len = data_len - 1 - db_len;
*((char*)query+q_len) = 0;
if (!(start= data_buf = (char*) my_malloc(catalog_len + data_len +2, MYF(MY_WME))))
DBUG_VOID_RETURN;
if (catalog) // If catalog is given
{
memcpy((char*) start, catalog, catalog_len+1); // Copy name and end \0
catalog= start;
start+= catalog_len+1;
}
memcpy((char*) start, end, data_len); // Copy db and query
((char*) start)[data_len]= '\0'; // End query with \0 (For safetly)
db= start;
query= start + db_len + 1;
q_len= data_len - db_len -1;
/* This is used to detect wrong parsing. Could be removed in the future. */
DBUG_PRINT("info", ("catalog: '%s' len: %u db: '%s' len: %u q_len: %lu",
catalog, (uint) catalog_len, db, (uint) db_len,q_len));
DBUG_VOID_RETURN;
}
......@@ -937,9 +1244,14 @@ Query_log_event::Query_log_event(const char* buf, int event_len,
*/
#ifdef MYSQL_CLIENT
void Query_log_event::print(FILE* file, bool short_form, char* last_db)
void Query_log_event::print(FILE* file, bool short_form,
LAST_EVENT_INFO* last_event_info)
{
// TODO: print the catalog ??
char buff[40],*end; // Enough for SET TIMESTAMP
bool different_db= 1;
uint32 tmp;
if (!short_form)
{
print_header(file);
......@@ -947,12 +1259,10 @@ void Query_log_event::print(FILE* file, bool short_form, char* last_db)
(ulong) thread_id, (ulong) exec_time, error_code);
}
bool different_db= 1;
if (db && last_db)
if (db)
{
if (different_db= memcmp(last_db, db, db_len + 1))
memcpy(last_db, db, db_len + 1);
if ((different_db = memcmp(last_event_info->db, db, db_len + 1)))
memcpy(last_event_info->db, db, db_len + 1);
}
if (db && db[0] && different_db)
......@@ -963,8 +1273,76 @@ void Query_log_event::print(FILE* file, bool short_form, char* last_db)
my_fwrite(file, (byte*) buff, (uint) (end-buff),MYF(MY_NABP | MY_WME));
if (flags & LOG_EVENT_THREAD_SPECIFIC_F)
fprintf(file,"SET @@session.pseudo_thread_id=%lu;\n",(ulong)thread_id);
/*
Now the session variables;
it's more efficient to pass SQL_MODE as a number instead of a
comma-separated list.
FOREIGN_KEY_CHECKS, SQL_AUTO_IS_NULL, UNIQUE_CHECKS are session-only
variables (they have no global version; they're not listed in sql_class.h),
The tests below work for pure binlogs or pure relay logs. Won't work for
mixed relay logs but we don't create mixed relay logs (that is, there is no
relay log with a format change except within the 3 first events, which
mysqlbinlog handles gracefully). So this code should always be good.
*/
if (likely(flags2_inited)) /* likely as this will mainly read 5.0 logs */
{
/* tmp is a bitmask of bits which have changed. */
if (likely(last_event_info->flags2_inited))
/* All bits which have changed */
tmp= (last_event_info->flags2) ^ flags2;
else /* that's the first Query event we read */
{
last_event_info->flags2_inited= 1;
tmp= ~((uint32)0); /* all bits have changed */
}
if (unlikely(tmp)) /* some bits have changed */
{
bool need_comma= 0;
fprintf(file, "SET ");
print_set_option(file, tmp, OPTION_NO_FOREIGN_KEY_CHECKS, ~flags2,
"@@session.foreign_key_checks", &need_comma);
print_set_option(file, tmp, OPTION_AUTO_IS_NULL, flags2,
"@@session.sql_auto_is_null", &need_comma);
print_set_option(file, tmp, OPTION_RELAXED_UNIQUE_CHECKS, ~flags2,
"@@session.unique_checks", &need_comma);
fprintf(file,";\n");
last_event_info->flags2= flags2;
}
}
/*
If flags2_inited==0, this is an event from 3.23 or 4.0; nothing to print
(remember we don't produce mixed relay logs so there cannot be 5.0 events
before that one so there is nothing to reset).
*/
if (likely(sql_mode_inited))
{
if (unlikely(!last_event_info->sql_mode_inited)) /* first Query event */
{
last_event_info->sql_mode_inited= 1;
/* force a difference to force write */
last_event_info->sql_mode= ~sql_mode;
}
if (unlikely(last_event_info->sql_mode != sql_mode))
{
fprintf(file,"SET @@session.sql_mode=%lu;\n",(ulong)sql_mode);
last_event_info->sql_mode= sql_mode;
}
}
if (last_event_info->auto_increment_increment != auto_increment_increment ||
last_event_info->auto_increment_offset != auto_increment_offset)
{
fprintf(file,"SET @@session.auto_increment_increment=%lu, @@session.auto_increment_offset=%lu;\n",
auto_increment_increment,auto_increment_offset);
last_event_info->auto_increment_increment= auto_increment_increment;
last_event_info->auto_increment_offset= auto_increment_offset;
}
my_fwrite(file, (byte*) query, q_len, MYF(MY_NABP | MY_WME));
fprintf(file, ";\n");
fputs(";\n", file);
}
#endif /* MYSQL_CLIENT */
......@@ -977,20 +1355,30 @@ void Query_log_event::print(FILE* file, bool short_form, char* last_db)
int Query_log_event::exec_event(struct st_relay_log_info* rli)
{
int expected_error,actual_error= 0;
thd->db_length= db_len;
thd->db= (char*) rewrite_db(db, &thd->db_length);
/*
Colleagues: please never free(thd->catalog) in MySQL. This would lead to
bugs as here thd->catalog is a part of an alloced block, not an entire
alloced block (see Query_log_event::exec_event()). Same for thd->db.
Thank you.
*/
thd->catalog= (char*) catalog;
thd->db= (char*) rewrite_db(db); // thd->db_length is set later if needed
thd->variables.auto_increment_increment= auto_increment_increment;
thd->variables.auto_increment_offset= auto_increment_offset;
/*
InnoDB internally stores the master log position it has processed so far;
position to store is of the END of the current log event.
InnoDB internally stores the master log position it has executed so far,
i.e. the position just after the COMMIT event.
When InnoDB will want to store, the positions in rli won't have
been updated yet, so group_master_log_* will point to old BEGIN
and event_master_log* will point to the beginning of current COMMIT.
But log_pos of the COMMIT Query event is what we want, i.e. the pos of the
END of the current log event (COMMIT). We save it in rli so that InnoDB can
access it.
*/
#if MYSQL_VERSION_ID < 50000
rli->future_group_master_log_pos= log_pos + get_event_len() -
(rli->mi->old_format ? (LOG_EVENT_HEADER_LEN - OLD_HEADER_LEN) : 0);
#else
/* In 5.0 we store the end_log_pos in the relay log so no problem */
rli->future_group_master_log_pos= log_pos;
#endif
DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));
clear_all_errors(thd, rli);
if (db_ok(thd->db, replicate_do_db, replicate_ignore_db))
......@@ -1002,9 +1390,33 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli)
thd->query_id = query_id++;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
thd->variables.pseudo_thread_id= thread_id; // for temp tables
mysql_log.write(thd,COM_QUERY,"%s",thd->query);
DBUG_PRINT("query",("%s",thd->query));
if (flags2_inited)
/*
all bits of thd->options which are 1 in OPTIONS_WRITTEN_TO_BIN_LOG must
take their value from flags2.
*/
thd->options= flags2|(thd->options & ~(ulong)OPTIONS_WRITTEN_TO_BIN_LOG);
/*
else, we are in a 3.23/4.0 binlog; we previously received a
Rotate_log_event which reset thd->options and sql_mode, so nothing to do.
*/
/*
We do not replicate IGNORE_DIR_IN_CREATE. That is, if the master is a
slave which runs with SQL_MODE=IGNORE_DIR_IN_CREATE, this should not
force us to ignore the dir too. Imagine you are a ring of machines, and
one has a disk problem so that you temporarily need IGNORE_DIR_IN_CREATE
on this machine; you don't want it to propagate elsewhere (you don't want
all slaves to start ignoring the dirs).
*/
if (sql_mode_inited)
thd->variables.sql_mode=
(ulong) ((thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE) |
(sql_mode & ~(ulong) MODE_NO_DIR_IN_CREATE));
if (ignored_error_code((expected_error= error_code)) ||
!check_expected_error(thd,rli,expected_error))
mysql_parse(thd, thd->query, q_len);
......@@ -1053,7 +1465,7 @@ Default database: '%s'. Query: '%s'",
expected_error,
actual_error ? thd->net.last_error: "no error",
actual_error,
print_slave_db_safe(thd->db), query);
print_slave_db_safe(db), query);
thd->query_error= 1;
}
/*
......@@ -1077,11 +1489,43 @@ Default database: '%s'. Query: '%s'",
print_slave_db_safe(thd->db), query);
thd->query_error= 1;
}
/*
TODO: compare the values of "affected rows" around here. Something
like:
if ((uint32) affected_in_event != (uint32) affected_on_slave)
{
sql_print_error("Slave: did not get the expected number of affected \
rows running query from master - expected %d, got %d (this numbers \
should have matched modulo 4294967296).", 0, ...);
thd->query_error = 1;
}
We may also want an option to tell the slave to ignore "affected"
mismatch. This mismatch could be implemented with a new ER_ code, and
to ignore it you would use --slave-skip-errors...
To do the comparison we need to know the value of "affected" which the
above mysql_parse() computed. And we need to know the value of
"affected" in the master's binlog. Both will be implemented later. The
important thing is that we now have the format ready to log the values
of "affected" in the binlog. So we can release 5.0.0 before effectively
logging "affected" and effectively comparing it.
*/
} /* End of if (db_ok(... */
end:
VOID(pthread_mutex_lock(&LOCK_thread_count));
thd->db= 0; // prevent db from being freed
/*
Probably we have set thd->query, thd->db, thd->catalog to point to places
in the data_buf of this event. Now the event is going to be deleted
probably, so data_buf will be freed, so the thd->... listed above will be
pointers to freed memory.
So we must set them to 0, so that those bad pointers values are not later
used. Note that "cleanup" queries (automatic DO RELEASE_LOCK() and DROP
TEMPORARY TABLE don't suffer from these assignments to 0 as DROP TEMPORARY
TABLE uses the db.table syntax).
*/
thd->db= thd->catalog= 0; // prevent db from being freed
thd->query= 0; // just to be sure
thd->query_length= thd->db_length =0;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
......@@ -1094,22 +1538,30 @@ Default database: '%s'. Query: '%s'",
updating query.
*/
return (thd->query_error ? thd->query_error :
(thd->one_shot_set ? (rli->inc_event_relay_log_pos(get_event_len()),0) :
(thd->one_shot_set ? (rli->inc_event_relay_log_pos(),0) :
Log_event::exec_event(rli)));
}
#endif
/**************************************************************************
Start_log_event methods
Start_log_event_v3 methods
**************************************************************************/
#ifndef MYSQL_CLIENT
Start_log_event_v3::Start_log_event_v3() :Log_event(), binlog_version(BINLOG_VERSION), artificial_event(0)
{
created= when;
memcpy(server_version, ::server_version, ST_SERVER_VER_LEN);
}
#endif
/*
Start_log_event::pack_info()
Start_log_event_v3::pack_info()
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
void Start_log_event::pack_info(Protocol *protocol)
void Start_log_event_v3::pack_info(Protocol *protocol)
{
char buf[12 + ST_SERVER_VER_LEN + 14 + 22], *pos;
pos= strmov(buf, "Server ver: ");
......@@ -1122,15 +1574,14 @@ void Start_log_event::pack_info(Protocol *protocol)
/*
Start_log_event::print()
Start_log_event_v3::print()
*/
#ifdef MYSQL_CLIENT
void Start_log_event::print(FILE* file, bool short_form, char* last_db)
void Start_log_event_v3::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
{
if (short_form)
return;
if (!short_form)
{
print_header(file);
fprintf(file, "\tStart: binlog v %d, server v %s created ", binlog_version,
server_version);
......@@ -1138,41 +1589,54 @@ void Start_log_event::print(FILE* file, bool short_form, char* last_db)
if (created)
fprintf(file," at startup");
fputc('\n', file);
}
#ifdef WHEN_WE_HAVE_THE_RESET_CONNECTION_SQL_COMMAND
/*
This is for mysqlbinlog: like in replication, we want to delete the stale
tmp files left by an unclean shutdown of mysqld (temporary tables). Probably
this can be done with RESET CONNECTION (syntax to be defined).
*/
fprintf(file,"RESET CONNECTION;\n");
#endif
fflush(file);
}
#endif /* MYSQL_CLIENT */
/*
Start_log_event::Start_log_event()
Start_log_event_v3::Start_log_event_v3()
*/
Start_log_event::Start_log_event(const char* buf,
bool old_format)
:Log_event(buf, old_format)
Start_log_event_v3::Start_log_event_v3(const char* buf,
const Format_description_log_event* description_event)
:Log_event(buf, description_event)
{
buf += (old_format) ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN;
binlog_version = uint2korr(buf+ST_BINLOG_VER_OFFSET);
buf+= description_event->common_header_len;
binlog_version= uint2korr(buf+ST_BINLOG_VER_OFFSET);
memcpy(server_version, buf+ST_SERVER_VER_OFFSET,
ST_SERVER_VER_LEN);
created = uint4korr(buf+ST_CREATED_OFFSET);
created= uint4korr(buf+ST_CREATED_OFFSET);
/* We use log_pos to mark if this was an artificial event or not */
artificial_event= (log_pos == 0);
}
/*
Start_log_event::write_data()
Start_log_event_v3::write()
*/
int Start_log_event::write_data(IO_CACHE* file)
bool Start_log_event_v3::write(IO_CACHE* file)
{
char buff[START_HEADER_LEN];
char buff[START_V3_HEADER_LEN];
int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version);
memcpy(buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN);
int4store(buff + ST_CREATED_OFFSET,created);
return (my_b_safe_write(file, (byte*) buff, sizeof(buff)) ? -1 : 0);
return (write_header(file, sizeof(buff)) ||
my_b_safe_write(file, (byte*) buff, sizeof(buff)));
}
/*
Start_log_event::exec_event()
Start_log_event_v3::exec_event()
The master started
......@@ -1191,23 +1655,29 @@ int Start_log_event::write_data(IO_CACHE* file)
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
int Start_log_event::exec_event(struct st_relay_log_info* rli)
int Start_log_event_v3::exec_event(struct st_relay_log_info* rli)
{
DBUG_ENTER("Start_log_event::exec_event");
DBUG_ENTER("Start_log_event_v3::exec_event");
/*
If the I/O thread has not started, mi->old_format is BINLOG_FORMAT_CURRENT
(that's what the MASTER_INFO constructor does), so the test below is not
perfect at all.
*/
switch (rli->mi->old_format) {
case BINLOG_FORMAT_CURRENT:
switch (rli->relay_log.description_event_for_exec->binlog_version)
{
case 3:
case 4:
/*
This is 4.x, so a Start_log_event is only at master startup,
so we are sure the master has restarted and cleared his temp tables.
This can either be 4.x (then a Start_log_event_v3 is only at master
startup so we are sure the master has restarted and cleared his temp
tables; the event always has 'created'>0) or 5.0 (then we have to test
'created').
*/
if (created)
{
close_temporary_tables(thd);
cleanup_load_tmpdir();
}
/*
As a transaction NEVER spans on 2 or more binlogs:
if we have an active transaction at this point, the master died while
......@@ -1215,8 +1685,12 @@ int Start_log_event::exec_event(struct st_relay_log_info* rli)
cache to the binlog. As the write was started, the transaction had been
committed on the master, so we lack of information to replay this
transaction on the slave; all we can do is stop with error.
Note: this event could be sent by the master to inform us of the format
of its binlog; in other words maybe it is not at its original place when
it comes to us; we'll know this by checking log_pos ("artificial" events
have log_pos == 0).
*/
if (thd->options & OPTION_BEGIN)
if (!artificial_event && (thd->options & OPTION_BEGIN))
{
slave_print_error(rli, 0, "\
Rolling back unfinished transaction (no COMMIT or ROLLBACK) from relay log. \
......@@ -1230,33 +1704,274 @@ binary log.");
Now the older formats; in that case load_tmpdir is cleaned up by the I/O
thread.
*/
case BINLOG_FORMAT_323_LESS_57:
case 1:
if (strncmp(rli->relay_log.description_event_for_exec->server_version,
"3.23.57",7) >= 0 && created)
{
/*
Cannot distinguish a Start_log_event generated at master startup and
one generated by master FLUSH LOGS, so cannot be sure temp tables
have to be dropped. So do nothing.
Can distinguish, based on the value of 'created': this event was
generated at master startup.
*/
break;
case BINLOG_FORMAT_323_GEQ_57:
close_temporary_tables(thd);
}
/*
Can distinguish, based on the value of 'created',
which was generated at master startup.
Otherwise, can't distinguish a Start_log_event generated at
master startup and one generated by master FLUSH LOGS, so cannot
be sure temp tables have to be dropped. So do nothing.
*/
if (created)
close_temporary_tables(thd);
break;
default:
/* this case is impossible */
return 1;
DBUG_RETURN(1);
}
DBUG_RETURN(Log_event::exec_event(rli));
}
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
/**************************************************************************
/***************************************************************************
Format_description_log_event methods
****************************************************************************/
/*
Format_description_log_event 1st ctor.
SYNOPSIS
Format_description_log_event::Format_description_log_event
binlog_version the binlog version for which we want to build
an event. Can be 1 (=MySQL 3.23), 3 (=4.0.x
x>=2 and 4.1) or 4 (MySQL 5.0). Note that the
old 4.0 (binlog version 2) is not supported;
it should not be used for replication with
5.0.
DESCRIPTION
Ctor. Can be used to create the event to write to the binary log (when the
server starts or when FLUSH LOGS), or to create artificial events to parse
binlogs from MySQL 3.23 or 4.x.
When in a client, only the 2nd use is possible.
TODO
Update this code with the new event for LOAD DATA, once they are pushed (in
4.1 or 5.0). If it's in 5.0, only the "case 4" block should be updated.
*/
Format_description_log_event::
Format_description_log_event(uint8 binlog_ver,
const char* server_ver)
:Start_log_event_v3()
{
created= when;
binlog_version= binlog_ver;
switch (binlog_ver) {
case 4: /* MySQL 5.0 */
memcpy(server_version, ::server_version, ST_SERVER_VER_LEN);
common_header_len= LOG_EVENT_HEADER_LEN;
number_of_event_types= LOG_EVENT_TYPES;
/* we'll catch my_malloc() error in is_valid() */
post_header_len=(uint8*) my_malloc(number_of_event_types*sizeof(uint8),
MYF(0));
/*
This long list of assignments is not beautiful, but I see no way to
make it nicer, as the right members are #defines, not array members, so
it's impossible to write a loop.
*/
if (post_header_len)
{
post_header_len[START_EVENT_V3-1]= START_V3_HEADER_LEN;
post_header_len[QUERY_EVENT-1]= QUERY_HEADER_LEN;
post_header_len[STOP_EVENT-1]= 0;
post_header_len[ROTATE_EVENT-1]= ROTATE_HEADER_LEN;
post_header_len[INTVAR_EVENT-1]= 0;
post_header_len[LOAD_EVENT-1]= LOAD_HEADER_LEN;
post_header_len[SLAVE_EVENT-1]= 0;
post_header_len[CREATE_FILE_EVENT-1]= CREATE_FILE_HEADER_LEN;
post_header_len[APPEND_BLOCK_EVENT-1]= APPEND_BLOCK_HEADER_LEN;
post_header_len[EXEC_LOAD_EVENT-1]= EXEC_LOAD_HEADER_LEN;
post_header_len[DELETE_FILE_EVENT-1]= DELETE_FILE_HEADER_LEN;
post_header_len[NEW_LOAD_EVENT-1]= post_header_len[LOAD_EVENT-1];
post_header_len[RAND_EVENT-1]= 0;
post_header_len[USER_VAR_EVENT-1]= 0;
post_header_len[FORMAT_DESCRIPTION_EVENT-1]= FORMAT_DESCRIPTION_HEADER_LEN;
}
break;
case 1: /* 3.23 */
case 3: /* 4.0.x x>=2 */
/*
We build an artificial (i.e. not sent by the master) event, which
describes what those old master versions send.
*/
if (binlog_ver==1)
strmov(server_version, server_ver ? server_ver : "3.23");
else
strmov(server_version, server_ver ? server_ver : "4.0");
common_header_len= binlog_ver==1 ? OLD_HEADER_LEN :
LOG_EVENT_MINIMAL_HEADER_LEN;
/*
The first new event in binlog version 4 is Format_desc. So any event type
after that does not exist in older versions. We use the events known by
version 3, even if version 1 had only a subset of them (this is not a
problem: it uses a few bytes for nothing but unifies code; it does not
make the slave detect less corruptions).
*/
number_of_event_types= FORMAT_DESCRIPTION_EVENT - 1;
post_header_len=(uint8*) my_malloc(number_of_event_types*sizeof(uint8),
MYF(0));
if (post_header_len)
{
post_header_len[START_EVENT_V3-1]= START_V3_HEADER_LEN;
post_header_len[QUERY_EVENT-1]= QUERY_HEADER_MINIMAL_LEN;
post_header_len[STOP_EVENT-1]= 0;
post_header_len[ROTATE_EVENT-1]= (binlog_ver==1) ? 0 : ROTATE_HEADER_LEN;
post_header_len[INTVAR_EVENT-1]= 0;
post_header_len[LOAD_EVENT-1]= LOAD_HEADER_LEN;
post_header_len[SLAVE_EVENT-1]= 0;
post_header_len[CREATE_FILE_EVENT-1]= CREATE_FILE_HEADER_LEN;
post_header_len[APPEND_BLOCK_EVENT-1]= APPEND_BLOCK_HEADER_LEN;
post_header_len[EXEC_LOAD_EVENT-1]= EXEC_LOAD_HEADER_LEN;
post_header_len[DELETE_FILE_EVENT-1]= DELETE_FILE_HEADER_LEN;
post_header_len[NEW_LOAD_EVENT-1]= post_header_len[LOAD_EVENT-1];
post_header_len[RAND_EVENT-1]= 0;
post_header_len[USER_VAR_EVENT-1]= 0;
}
break;
default: /* Includes binlog version 2 i.e. 4.0.x x<=1 */
post_header_len= 0; /* will make is_valid() fail */
break;
}
}
/*
The problem with this constructor is that the fixed header may have a
length different from this version, but we don't know this length as we
have not read the Format_description_log_event which says it, yet. This
length is in the post-header of the event, but we don't know where the
post-header starts.
So this type of event HAS to:
- either have the header's length at the beginning (in the header, at a
fixed position which will never be changed), not in the post-header. That
would make the header be "shifted" compared to other events.
- or have a header of size LOG_EVENT_MINIMAL_HEADER_LEN (19), in all future
versions, so that we know for sure.
I (Guilhem) chose the 2nd solution. Rotate has the same constraint (because
it is sent before Format_description_log_event).
*/
Format_description_log_event::
Format_description_log_event(const char* buf,
uint event_len,
const
Format_description_log_event*
description_event)
:Start_log_event_v3(buf, description_event)
{
DBUG_ENTER("Format_description_log_event::Format_description_log_event(char*,...)");
buf+= LOG_EVENT_MINIMAL_HEADER_LEN;
if ((common_header_len=buf[ST_COMMON_HEADER_LEN_OFFSET]) < OLD_HEADER_LEN)
DBUG_VOID_RETURN; /* sanity check */
number_of_event_types=
event_len-(LOG_EVENT_MINIMAL_HEADER_LEN+ST_COMMON_HEADER_LEN_OFFSET+1);
DBUG_PRINT("info", ("common_header_len=%d number_of_event_types=%d",
common_header_len, number_of_event_types));
/* If alloc fails, we'll detect it in is_valid() */
post_header_len= (uint8*) my_memdup((byte*)buf+ST_COMMON_HEADER_LEN_OFFSET+1,
number_of_event_types*
sizeof(*post_header_len),
MYF(0));
DBUG_VOID_RETURN;
}
bool Format_description_log_event::write(IO_CACHE* file)
{
/*
We don't call Start_log_event_v3::write() because this would make 2
my_b_safe_write().
*/
byte buff[FORMAT_DESCRIPTION_HEADER_LEN];
int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version);
memcpy((char*) buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN);
int4store(buff + ST_CREATED_OFFSET,created);
buff[ST_COMMON_HEADER_LEN_OFFSET]= LOG_EVENT_HEADER_LEN;
memcpy((char*) buff+ST_COMMON_HEADER_LEN_OFFSET+1, (byte*) post_header_len,
LOG_EVENT_TYPES);
return (write_header(file, sizeof(buff)) ||
my_b_safe_write(file, buff, sizeof(buff)));
}
/*
SYNOPSIS
Format_description_log_event::exec_event()
IMPLEMENTATION
Save the information which describes the binlog's format, to be able to
read all coming events.
Call Start_log_event_v3::exec_event().
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
int Format_description_log_event::exec_event(struct st_relay_log_info* rli)
{
DBUG_ENTER("Format_description_log_event::exec_event");
/* save the information describing this binlog */
delete rli->relay_log.description_event_for_exec;
rli->relay_log.description_event_for_exec= this;
/*
If this event comes from ourselves, there is no cleaning task to perform,
we don't call Start_log_event_v3::exec_event() (this was just to update the
log's description event).
*/
if (server_id == (uint32) ::server_id)
{
/*
Do not modify rli->group_master_log_pos, as this event did not exist on
the master. That is, just update the *relay log* coordinates; this is
done by passing log_pos=0 to inc_group_relay_log_pos, like we do in
Stop_log_event::exec_event().
If in a transaction, don't touch group_* coordinates.
*/
if (thd->options & OPTION_BEGIN)
rli->inc_event_relay_log_pos();
else
{
rli->inc_group_relay_log_pos(0);
flush_relay_log_info(rli);
}
DBUG_RETURN(0);
}
/*
If the event was not requested by the slave i.e. the master sent it while
the slave asked for a position >4, the event will make
rli->group_master_log_pos advance. Say that the slave asked for position
1000, and the Format_desc event's end is 95. Then in the beginning of
replication rli->group_master_log_pos will be 0, then 95, then jump to first
really asked event (which is >95). So this is ok.
*/
DBUG_RETURN(Start_log_event_v3::exec_event(rli));
}
#endif
/**************************************************************************
Load_log_event methods
**************************************************************************/
General note about Load_log_event: the binlogging of LOAD DATA INFILE is
going to be changed in 5.0 (or maybe in 4.1; not decided yet).
However, the 5.0 slave could still have to read such events (from a 4.x
master), convert them (which just means maybe expand the header, when 5.0
servers have a UID in events) (remember that whatever is after the header
will be like in 4.x, as this event's format is not modified in 5.0 as we
will use new types of events to log the new LOAD DATA INFILE features).
To be able to read/convert, we just need to not assume that the common
header is of length LOG_EVENT_HEADER_LEN (we must use the description
event).
Note that I (Guilhem) manually tested replication of a big LOAD DATA INFILE
between 3.23 and 5.0, and between 4.0 and 5.0, and it works fine (and the
positions displayed in SHOW SLAVE STATUS then are fine too).
**************************************************************************/
/*
Load_log_event::pack_info()
......@@ -1363,7 +2078,7 @@ void Load_log_event::pack_info(Protocol *protocol)
Load_log_event::write_data_header()
*/
int Load_log_event::write_data_header(IO_CACHE* file)
bool Load_log_event::write_data_header(IO_CACHE* file)
{
char buf[LOAD_HEADER_LEN];
int4store(buf + L_THREAD_ID_OFFSET, slave_proxy_id);
......@@ -1372,7 +2087,7 @@ int Load_log_event::write_data_header(IO_CACHE* file)
buf[L_TBL_LEN_OFFSET] = (char)table_name_len;
buf[L_DB_LEN_OFFSET] = (char)db_len;
int4store(buf + L_NUM_FIELDS_OFFSET, num_fields);
return my_b_safe_write(file, (byte*)buf, LOAD_HEADER_LEN);
return my_b_safe_write(file, (byte*)buf, LOAD_HEADER_LEN) != 0;
}
......@@ -1380,7 +2095,7 @@ int Load_log_event::write_data_header(IO_CACHE* file)
Load_log_event::write_data_body()
*/
int Load_log_event::write_data_body(IO_CACHE* file)
bool Load_log_event::write_data_body(IO_CACHE* file)
{
if (sql_ex.write_data(file))
return 1;
......@@ -1483,6 +2198,7 @@ Load_log_event::Load_log_event(THD *thd_arg, sql_exchange *ex,
}
#endif /* !MYSQL_CLIENT */
/*
Load_log_event::Load_log_event()
......@@ -1491,15 +2207,25 @@ Load_log_event::Load_log_event(THD *thd_arg, sql_exchange *ex,
constructed event.
*/
Load_log_event::Load_log_event(const char *buf, int event_len,
bool old_format)
:Log_event(buf, old_format), num_fields(0), fields(0),
field_lens(0), field_block_len(0),
Load_log_event::Load_log_event(const char *buf, uint event_len,
const Format_description_log_event *description_event)
:Log_event(buf, description_event), num_fields(0), fields(0),
field_lens(0),field_block_len(0),
table_name(0), db(0), fname(0), local_fname(FALSE)
{
DBUG_ENTER("Load_log_event");
if (event_len) // derived class, will call copy_log_event() itself
copy_log_event(buf, event_len, old_format);
/*
I (Guilhem) manually tested replication of LOAD DATA INFILE for 3.23->5.0,
4.0->5.0 and 5.0->5.0 and it works.
*/
if (event_len)
copy_log_event(buf, event_len,
((buf[EVENT_TYPE_OFFSET] == LOAD_EVENT) ?
LOAD_HEADER_LEN +
description_event->common_header_len :
LOAD_HEADER_LEN + LOG_EVENT_HEADER_LEN),
description_event);
/* otherwise it's a derived class, will call copy_log_event() itself */
DBUG_VOID_RETURN;
}
......@@ -1509,14 +2235,14 @@ Load_log_event::Load_log_event(const char *buf, int event_len,
*/
int Load_log_event::copy_log_event(const char *buf, ulong event_len,
bool old_format)
int body_offset,
const Format_description_log_event *description_event)
{
DBUG_ENTER("Load_log_event::copy_log_event");
uint data_len;
char* buf_end = (char*)buf + event_len;
uint header_len= old_format ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN;
const char* data_head = buf + header_len;
DBUG_ENTER("Load_log_event::copy_log_event");
/* this is the beginning of the post-header */
const char* data_head = buf + description_event->common_header_len;
slave_proxy_id= thread_id= uint4korr(data_head + L_THREAD_ID_OFFSET);
exec_time = uint4korr(data_head + L_EXEC_TIME_OFFSET);
skip_lines = uint4korr(data_head + L_SKIP_LINES_OFFSET);
......@@ -1524,17 +2250,13 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len,
db_len = (uint)data_head[L_DB_LEN_OFFSET];
num_fields = uint4korr(data_head + L_NUM_FIELDS_OFFSET);
int body_offset = ((buf[EVENT_TYPE_OFFSET] == LOAD_EVENT) ?
LOAD_HEADER_LEN + header_len :
get_data_body_offset());
if ((int) event_len < body_offset)
DBUG_RETURN(1);
/*
Sql_ex.init() on success returns the pointer to the first byte after
the sql_ex structure, which is the start of field lengths array.
*/
if (!(field_lens=(uchar*)sql_ex.init((char*)buf + body_offset,
if (!(field_lens= (uchar*)sql_ex.init((char*)buf + body_offset,
buf_end,
buf[EVENT_TYPE_OFFSET] != LOAD_EVENT)))
DBUG_RETURN(1);
......@@ -1551,6 +2273,12 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len,
fname = db + db_len + 1;
fname_len = strlen(fname);
// null termination is accomplished by the caller doing buf[event_len]=0
/*
In 5.0 this event will have the same format, as we are planning to log LOAD
DATA INFILE in a completely different way (as a plain-text query) since 4.1
or 5.0 (Dmitri's WL#874)
*/
DBUG_RETURN(0);
}
......@@ -1560,13 +2288,13 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len,
*/
#ifdef MYSQL_CLIENT
void Load_log_event::print(FILE* file, bool short_form, char* last_db)
void Load_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
{
print(file, short_form, last_db, 0);
print(file, short_form, last_event_info, 0);
}
void Load_log_event::print(FILE* file, bool short_form, char* last_db,
void Load_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info,
bool commented)
{
DBUG_ENTER("Load_log_event::print");
......@@ -1578,7 +2306,7 @@ void Load_log_event::print(FILE* file, bool short_form, char* last_db,
}
bool different_db= 1;
if (db && last_db)
if (db)
{
/*
If the database is different from the one of the previous statement, we
......@@ -1586,9 +2314,9 @@ void Load_log_event::print(FILE* file, bool short_form, char* last_db,
But if commented, the "use" is going to be commented so we should not
update the last_db.
*/
if ((different_db= memcmp(last_db, db, db_len + 1)) &&
if ((different_db= memcmp(last_event_info->db, db, db_len + 1)) &&
!commented)
memcpy(last_db, db, db_len + 1);
memcpy(last_event_info->db, db, db_len + 1);
}
if (db && db[0] && different_db)
......@@ -1715,12 +2443,9 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli,
mysql_init_query(thd, 0, 0);
if (!use_rli_only_for_errors)
{
#if MYSQL_VERSION_ID < 50000
rli->future_group_master_log_pos= log_pos + get_event_len() -
(rli->mi->old_format ? (LOG_EVENT_HEADER_LEN - OLD_HEADER_LEN) : 0);
#else
/* Saved for InnoDB, see comment in Query_log_event::exec_event() */
rli->future_group_master_log_pos= log_pos;
#endif
DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));
}
/*
......@@ -1832,7 +2557,7 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli,
thd->net.pkt_nr = net->pkt_nr;
}
if (mysql_load(thd, &ex, &tables, field_list, handle_dup, net != 0,
TL_WRITE))
TL_WRITE, 0))
thd->query_error = 1;
if (thd->cuted_fields)
{
......@@ -1863,7 +2588,7 @@ Slave: load data infile on table '%s' at log position %s in log \
thd->net.vio = 0;
char *save_db= thd->db;
VOID(pthread_mutex_lock(&LOCK_thread_count));
thd->db= 0;
thd->db= thd->catalog= 0;
thd->query= 0;
thd->query_length= thd->db_length= 0;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
......@@ -1930,12 +2655,12 @@ void Rotate_log_event::pack_info(Protocol *protocol)
*/
#ifdef MYSQL_CLIENT
void Rotate_log_event::print(FILE* file, bool short_form, char* last_db)
void Rotate_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
{
char buf[22];
if (short_form)
return;
print_header(file);
fprintf(file, "\tRotate to ");
if (new_log_ident)
......@@ -1952,31 +2677,22 @@ void Rotate_log_event::print(FILE* file, bool short_form, char* last_db)
Rotate_log_event::Rotate_log_event()
*/
Rotate_log_event::Rotate_log_event(const char* buf, int event_len,
bool old_format)
:Log_event(buf, old_format),new_log_ident(NULL),alloced(0)
Rotate_log_event::Rotate_log_event(const char* buf, uint event_len,
const Format_description_log_event* description_event)
:Log_event(buf, description_event) ,new_log_ident(NULL),alloced(0)
{
DBUG_ENTER("Rotate_log_event::Rotate_log_event(char*,...)");
// The caller will ensure that event_len is what we have at EVENT_LEN_OFFSET
int header_size = (old_format) ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN;
uint8 header_size= description_event->common_header_len;
uint8 post_header_len= description_event->post_header_len[ROTATE_EVENT-1];
uint ident_offset;
DBUG_ENTER("Rotate_log_event");
if (event_len < header_size)
DBUG_VOID_RETURN;
buf += header_size;
if (old_format)
{
ident_len = (uint)(event_len - OLD_HEADER_LEN);
pos = 4;
ident_offset = 0;
}
else
{
ident_len = (uint)(event_len - ROTATE_EVENT_OVERHEAD);
pos = uint8korr(buf + R_POS_OFFSET);
ident_offset = ROTATE_HEADER_LEN;
}
pos = post_header_len ? uint8korr(buf + R_POS_OFFSET) : 4;
ident_len = (uint)(event_len -
(header_size+post_header_len));
ident_offset = post_header_len;
set_if_smaller(ident_len,FN_REFLEN-1);
if (!(new_log_ident= my_strdup_with_length((byte*) buf +
ident_offset,
......@@ -1989,14 +2705,15 @@ Rotate_log_event::Rotate_log_event(const char* buf, int event_len,
/*
Rotate_log_event::write_data()
Rotate_log_event::write()
*/
int Rotate_log_event::write_data(IO_CACHE* file)
bool Rotate_log_event::write(IO_CACHE* file)
{
char buf[ROTATE_HEADER_LEN];
int8store(buf + R_POS_OFFSET, pos);
return (my_b_safe_write(file, (byte*)buf, ROTATE_HEADER_LEN) ||
return (write_header(file, ROTATE_HEADER_LEN + ident_len) ||
my_b_safe_write(file, (byte*)buf, ROTATE_HEADER_LEN) ||
my_b_safe_write(file, (byte*)new_log_ident, (uint) ident_len));
}
......@@ -2004,14 +2721,15 @@ int Rotate_log_event::write_data(IO_CACHE* file)
/*
Rotate_log_event::exec_event()
Got a rotate log even from the master
Got a rotate log event from the master
IMPLEMENTATION
This is mainly used so that we can later figure out the logname and
position for the master.
We can't rotate the slave as this will cause infinitive rotations
We can't rotate the slave's BINlog as this will cause infinitive rotations
in a A -> B -> A setup.
The NOTES below is a wrong comment which will disappear when 4.1 is merged.
RETURN VALUES
0 ok
......@@ -2023,7 +2741,7 @@ int Rotate_log_event::exec_event(struct st_relay_log_info* rli)
DBUG_ENTER("Rotate_log_event::exec_event");
pthread_mutex_lock(&rli->data_lock);
rli->event_relay_log_pos += get_event_len();
rli->event_relay_log_pos= my_b_tell(rli->cur_log);
/*
If we are in a transaction: the only normal case is when the I/O thread was
copying a big transaction, then it was stopped and restarted: we have this
......@@ -2035,15 +2753,28 @@ int Rotate_log_event::exec_event(struct st_relay_log_info* rli)
COMMIT or ROLLBACK
In that case, we don't want to touch the coordinates which correspond to
the beginning of the transaction.
Starting from 5.0.0, there also are some rotates from the slave itself, in
the relay log.
*/
if (!(thd->options & OPTION_BEGIN))
{
memcpy(rli->group_master_log_name, new_log_ident, ident_len+1);
rli->notify_group_master_log_name_update();
rli->group_master_log_pos = pos;
rli->group_relay_log_pos = rli->event_relay_log_pos;
DBUG_PRINT("info", ("group_master_log_pos: %lu",
rli->group_master_log_pos= pos;
rli->group_relay_log_pos= rli->event_relay_log_pos;
DBUG_PRINT("info", ("group_master_log_name: '%s' group_master_log_pos:\
%lu",
rli->group_master_log_name,
(ulong) rli->group_master_log_pos));
/*
Reset thd->options and sql_mode, because this could be the signal of a
master's downgrade from 5.0 to 4.0.
However, no need to reset description_event_for_exec: indeed, if the next
master is 5.0 (even 5.0.1) we will soon get a Format_desc; if the next
master is 4.0 then the events are in the slave's format (conversion).
*/
set_slave_thread_options(thd);
thd->variables.sql_mode= global_system_variables.sql_mode;
}
pthread_mutex_unlock(&rli->data_lock);
pthread_cond_broadcast(&rli->data_cond);
......@@ -2077,12 +2808,13 @@ void Intvar_log_event::pack_info(Protocol *protocol)
Intvar_log_event::Intvar_log_event()
*/
Intvar_log_event::Intvar_log_event(const char* buf, bool old_format)
:Log_event(buf, old_format)
Intvar_log_event::Intvar_log_event(const char* buf,
const Format_description_log_event* description_event)
:Log_event(buf, description_event)
{
buf += (old_format) ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN;
type = buf[I_TYPE_OFFSET];
val = uint8korr(buf+I_VAL_OFFSET);
buf+= description_event->common_header_len;
type= buf[I_TYPE_OFFSET];
val= uint8korr(buf+I_VAL_OFFSET);
}
......@@ -2101,15 +2833,16 @@ const char* Intvar_log_event::get_var_type_name()
/*
Intvar_log_event::write_data()
Intvar_log_event::write()
*/
int Intvar_log_event::write_data(IO_CACHE* file)
bool Intvar_log_event::write(IO_CACHE* file)
{
char buf[9];
buf[I_TYPE_OFFSET] = type;
byte buf[9];
buf[I_TYPE_OFFSET]= (byte) type;
int8store(buf + I_VAL_OFFSET, val);
return my_b_safe_write(file, (byte*) buf, sizeof(buf));
return (write_header(file, sizeof(buf)) ||
my_b_safe_write(file, buf, sizeof(buf)));
}
......@@ -2118,7 +2851,8 @@ int Intvar_log_event::write_data(IO_CACHE* file)
*/
#ifdef MYSQL_CLIENT
void Intvar_log_event::print(FILE* file, bool short_form, char* last_db)
void Intvar_log_event::print(FILE* file, bool short_form,
LAST_EVENT_INFO* last_event_info)
{
char llbuff[22];
const char *msg;
......@@ -2161,7 +2895,7 @@ int Intvar_log_event::exec_event(struct st_relay_log_info* rli)
thd->next_insert_id = val;
break;
}
rli->inc_event_relay_log_pos(get_event_len());
rli->inc_event_relay_log_pos();
return 0;
}
#endif
......@@ -2184,26 +2918,28 @@ void Rand_log_event::pack_info(Protocol *protocol)
#endif
Rand_log_event::Rand_log_event(const char* buf, bool old_format)
:Log_event(buf, old_format)
Rand_log_event::Rand_log_event(const char* buf,
const Format_description_log_event* description_event)
:Log_event(buf, description_event)
{
buf += (old_format) ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN;
seed1 = uint8korr(buf+RAND_SEED1_OFFSET);
seed2 = uint8korr(buf+RAND_SEED2_OFFSET);
buf+= description_event->common_header_len;
seed1= uint8korr(buf+RAND_SEED1_OFFSET);
seed2= uint8korr(buf+RAND_SEED2_OFFSET);
}
int Rand_log_event::write_data(IO_CACHE* file)
bool Rand_log_event::write(IO_CACHE* file)
{
char buf[16];
byte buf[16];
int8store(buf + RAND_SEED1_OFFSET, seed1);
int8store(buf + RAND_SEED2_OFFSET, seed2);
return my_b_safe_write(file, (byte*) buf, sizeof(buf));
return (write_header(file, sizeof(buf)) ||
my_b_safe_write(file, buf, sizeof(buf)));
}
#ifdef MYSQL_CLIENT
void Rand_log_event::print(FILE* file, bool short_form, char* last_db)
void Rand_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
{
char llbuff[22],llbuff2[22];
if (!short_form)
......@@ -2223,7 +2959,7 @@ int Rand_log_event::exec_event(struct st_relay_log_info* rli)
{
thd->rand.seed1= (ulong) seed1;
thd->rand.seed2= (ulong) seed2;
rli->inc_event_relay_log_pos(get_event_len());
rli->inc_event_relay_log_pos();
return 0;
}
#endif /* !MYSQL_CLIENT */
......@@ -2294,10 +3030,12 @@ void User_var_log_event::pack_info(Protocol* protocol)
#endif /* !MYSQL_CLIENT */
User_var_log_event::User_var_log_event(const char* buf, bool old_format)
:Log_event(buf, old_format)
User_var_log_event::
User_var_log_event(const char* buf,
const Format_description_log_event* description_event)
:Log_event(buf, description_event)
{
buf+= (old_format) ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN;
buf+= description_event->common_header_len;
name_len= uint4korr(buf);
name= (char *) buf + UV_NAME_LEN_SIZE;
buf+= UV_NAME_LEN_SIZE + name_len;
......@@ -2321,13 +3059,14 @@ User_var_log_event::User_var_log_event(const char* buf, bool old_format)
}
int User_var_log_event::write_data(IO_CACHE* file)
bool User_var_log_event::write(IO_CACHE* file)
{
char buf[UV_NAME_LEN_SIZE];
char buf1[UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE];
char buf2[8], *pos= buf2;
uint buf1_length;
ulong event_length;
int4store(buf, name_len);
......@@ -2359,7 +3098,12 @@ int User_var_log_event::write_data(IO_CACHE* file)
return 0;
}
}
return (my_b_safe_write(file, (byte*) buf, sizeof(buf)) ||
/* Length of the whole event */
event_length= sizeof(buf)+ name_len + buf1_length + val_len;
return (write_header(file, event_length) ||
my_b_safe_write(file, (byte*) buf, sizeof(buf)) ||
my_b_safe_write(file, (byte*) name, name_len) ||
my_b_safe_write(file, (byte*) buf1, buf1_length) ||
my_b_safe_write(file, (byte*) pos, val_len));
......@@ -2371,7 +3115,7 @@ int User_var_log_event::write_data(IO_CACHE* file)
*/
#ifdef MYSQL_CLIENT
void User_var_log_event::print(FILE* file, bool short_form, char* last_db)
void User_var_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
{
if (!short_form)
{
......@@ -2504,7 +3248,7 @@ int User_var_log_event::exec_event(struct st_relay_log_info* rli)
e.update_hash(val, val_len, type, charset, DERIVATION_NONE);
free_root(thd->mem_root,0);
rli->inc_event_relay_log_pos(get_event_len());
rli->inc_event_relay_log_pos();
return 0;
}
#endif /* !MYSQL_CLIENT */
......@@ -2516,7 +3260,7 @@ int User_var_log_event::exec_event(struct st_relay_log_info* rli)
#ifdef HAVE_REPLICATION
#ifdef MYSQL_CLIENT
void Unknown_log_event::print(FILE* file, bool short_form, char* last_db)
void Unknown_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
{
if (short_form)
return;
......@@ -2546,7 +3290,7 @@ void Slave_log_event::pack_info(Protocol *protocol)
#ifndef MYSQL_CLIENT
Slave_log_event::Slave_log_event(THD* thd_arg,
struct st_relay_log_info* rli)
:Log_event(thd_arg, 0, 0), mem_pool(0), master_host(0)
:Log_event(thd_arg, 0, 0) , mem_pool(0), master_host(0)
{
DBUG_ENTER("Slave_log_event");
if (!rli->inited) // QQ When can this happen ?
......@@ -2587,7 +3331,7 @@ Slave_log_event::~Slave_log_event()
#ifdef MYSQL_CLIENT
void Slave_log_event::print(FILE* file, bool short_form, char* last_db)
void Slave_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
{
char llbuff[22];
if (short_form)
......@@ -2607,12 +3351,15 @@ int Slave_log_event::get_data_size()
}
int Slave_log_event::write_data(IO_CACHE* file)
bool Slave_log_event::write(IO_CACHE* file)
{
ulong event_length= get_data_size();
int8store(mem_pool + SL_MASTER_POS_OFFSET, master_pos);
int2store(mem_pool + SL_MASTER_PORT_OFFSET, master_port);
// log and host are already there
return my_b_safe_write(file, (byte*)mem_pool, get_data_size());
return (write_header(file, event_length) ||
my_b_safe_write(file, (byte*) mem_pool, event_length));
}
......@@ -2633,12 +3380,13 @@ void Slave_log_event::init_from_mem_pool(int data_size)
}
Slave_log_event::Slave_log_event(const char* buf, int event_len)
:Log_event(buf,0),mem_pool(0),master_host(0)
/* This code is not used, so has not been updated to be format-tolerant */
Slave_log_event::Slave_log_event(const char* buf, uint event_len)
:Log_event(buf,0) /*unused event*/ ,mem_pool(0),master_host(0)
{
event_len -= LOG_EVENT_HEADER_LEN;
if (event_len < 0)
if (event_len < LOG_EVENT_HEADER_LEN)
return;
event_len -= LOG_EVENT_HEADER_LEN;
if (!(mem_pool = (char*) my_malloc(event_len + 1, MYF(MY_WME))))
return;
memcpy(mem_pool, buf + LOG_EVENT_HEADER_LEN, event_len);
......@@ -2666,7 +3414,7 @@ int Slave_log_event::exec_event(struct st_relay_log_info* rli)
*/
#ifdef MYSQL_CLIENT
void Stop_log_event::print(FILE* file, bool short_form, char* last_db)
void Stop_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
{
if (short_form)
return;
......@@ -2688,7 +3436,7 @@ void Stop_log_event::print(FILE* file, bool short_form, char* last_db)
We used to clean up slave_load_tmpdir, but this is useless as it has been
cleared at the end of LOAD DATA INFILE.
So we have nothing to do here.
The place were we must do this cleaning is in Start_log_event::exec_event(),
The place were we must do this cleaning is in Start_log_event_v3::exec_event(),
not here. Because if we come here, the master was sane.
*/
......@@ -2702,8 +3450,13 @@ int Stop_log_event::exec_event(struct st_relay_log_info* rli)
could give false triggers in MASTER_POS_WAIT() that we have reached
the target position when in fact we have not.
*/
rli->inc_group_relay_log_pos(get_event_len(), 0);
if (thd->options & OPTION_BEGIN)
rli->inc_event_relay_log_pos();
else
{
rli->inc_group_relay_log_pos(0);
flush_relay_log_info(rli);
}
return 0;
}
#endif /* !MYSQL_CLIENT */
......@@ -2740,10 +3493,10 @@ Create_file_log_event(THD* thd_arg, sql_exchange* ex,
Create_file_log_event::write_data_body()
*/
int Create_file_log_event::write_data_body(IO_CACHE* file)
bool Create_file_log_event::write_data_body(IO_CACHE* file)
{
int res;
if ((res = Load_log_event::write_data_body(file)) || fake_base)
bool res;
if ((res= Load_log_event::write_data_body(file)) || fake_base)
return res;
return (my_b_safe_write(file, (byte*) "", 1) ||
my_b_safe_write(file, (byte*) block, block_len));
......@@ -2754,14 +3507,14 @@ int Create_file_log_event::write_data_body(IO_CACHE* file)
Create_file_log_event::write_data_header()
*/
int Create_file_log_event::write_data_header(IO_CACHE* file)
bool Create_file_log_event::write_data_header(IO_CACHE* file)
{
int res;
if ((res = Load_log_event::write_data_header(file)) || fake_base)
return res;
bool res;
byte buf[CREATE_FILE_HEADER_LEN];
if ((res= Load_log_event::write_data_header(file)) || fake_base)
return res;
int4store(buf + CF_FILE_ID_OFFSET, file_id);
return my_b_safe_write(file, buf, CREATE_FILE_HEADER_LEN);
return my_b_safe_write(file, buf, CREATE_FILE_HEADER_LEN) != 0;
}
......@@ -2769,12 +3522,12 @@ int Create_file_log_event::write_data_header(IO_CACHE* file)
Create_file_log_event::write_base()
*/
int Create_file_log_event::write_base(IO_CACHE* file)
bool Create_file_log_event::write_base(IO_CACHE* file)
{
int res;
fake_base = 1; // pretend we are Load event
res = write(file);
fake_base = 0;
bool res;
fake_base= 1; // pretend we are Load event
res= write(file);
fake_base= 0;
return res;
}
......@@ -2783,28 +3536,43 @@ int Create_file_log_event::write_base(IO_CACHE* file)
Create_file_log_event ctor
*/
Create_file_log_event::Create_file_log_event(const char* buf, int len,
bool old_format)
:Load_log_event(buf,0,old_format),fake_base(0),block(0),inited_from_old(0)
Create_file_log_event::Create_file_log_event(const char* buf, uint len,
const Format_description_log_event* description_event)
:Load_log_event(buf,0,description_event),fake_base(0),block(0),inited_from_old(0)
{
int block_offset;
DBUG_ENTER("Create_file_log_event");
/*
We must make copy of 'buf' as this event may have to live over a
rotate log entry when used in mysqlbinlog
*/
DBUG_ENTER("Create_file_log_event::Create_file_log_event(char*,...)");
uint block_offset;
uint header_len= description_event->common_header_len;
uint8 load_header_len= description_event->post_header_len[LOAD_EVENT-1];
uint8 create_file_header_len= description_event->post_header_len[CREATE_FILE_EVENT-1];
if (!(event_buf= my_memdup((byte*) buf, len, MYF(MY_WME))) ||
(copy_log_event(event_buf, len, old_format)))
copy_log_event(event_buf,len,
((buf[EVENT_TYPE_OFFSET] == LOAD_EVENT) ?
load_header_len + header_len :
(fake_base ? (header_len+load_header_len) :
(header_len+load_header_len) +
create_file_header_len)),
description_event))
DBUG_VOID_RETURN;
if (!old_format)
if (description_event->binlog_version!=1)
{
file_id = uint4korr(buf + LOG_EVENT_HEADER_LEN +
+ LOAD_HEADER_LEN + CF_FILE_ID_OFFSET);
// + 1 for \0 terminating fname
block_offset = (LOG_EVENT_HEADER_LEN + Load_log_event::get_data_size() +
CREATE_FILE_HEADER_LEN + 1);
file_id= uint4korr(buf +
header_len +
load_header_len + CF_FILE_ID_OFFSET);
/*
Note that it's ok to use get_data_size() below, because it is computed
with values we have already read from this event (because we called
copy_log_event()); we are not using slave's format info to decode
master's format, we are really using master's format info.
Anyway, both formats should be identical (except the common_header_len)
as these Load events are not changed between 4.0 and 5.0 (as logging of
LOAD DATA INFILE does not use Load_log_event in 5.0).
The + 1 is for \0 terminating fname
*/
block_offset= (description_event->common_header_len +
Load_log_event::get_data_size() +
create_file_header_len + 1);
if (len < block_offset)
return;
block = (char*)buf + block_offset;
......@@ -2825,18 +3593,18 @@ Create_file_log_event::Create_file_log_event(const char* buf, int len,
#ifdef MYSQL_CLIENT
void Create_file_log_event::print(FILE* file, bool short_form,
char* last_db, bool enable_local)
LAST_EVENT_INFO* last_event_info, bool enable_local)
{
if (short_form)
{
if (enable_local && check_fname_outside_temp_buf())
Load_log_event::print(file, 1, last_db);
Load_log_event::print(file, 1, last_event_info);
return;
}
if (enable_local)
{
Load_log_event::print(file, 1, last_db, !check_fname_outside_temp_buf());
Load_log_event::print(file, 1, last_event_info, !check_fname_outside_temp_buf());
/*
That one is for "file_id: etc" below: in mysqlbinlog we want the #, in
SHOW BINLOG EVENTS we don't.
......@@ -2849,9 +3617,9 @@ void Create_file_log_event::print(FILE* file, bool short_form,
void Create_file_log_event::print(FILE* file, bool short_form,
char* last_db)
LAST_EVENT_INFO* last_event_info)
{
print(file,short_form,last_db,0);
print(file,short_form,last_event_info,0);
}
#endif /* MYSQL_CLIENT */
......@@ -2968,28 +3736,34 @@ Append_block_log_event::Append_block_log_event(THD* thd_arg, const char* db_arg,
Append_block_log_event ctor
*/
Append_block_log_event::Append_block_log_event(const char* buf, int len)
:Log_event(buf, 0),block(0)
Append_block_log_event::Append_block_log_event(const char* buf, uint len,
const Format_description_log_event* description_event)
:Log_event(buf, description_event),block(0)
{
DBUG_ENTER("Append_block_log_event");
if ((uint)len < APPEND_BLOCK_EVENT_OVERHEAD)
DBUG_ENTER("Append_block_log_event::Append_block_log_event(char*,...)");
uint8 common_header_len= description_event->common_header_len;
uint8 append_block_header_len=
description_event->post_header_len[APPEND_BLOCK_EVENT-1];
uint total_header_len= common_header_len+append_block_header_len;
if (len < total_header_len)
DBUG_VOID_RETURN;
file_id = uint4korr(buf + LOG_EVENT_HEADER_LEN + AB_FILE_ID_OFFSET);
block = (char*)buf + APPEND_BLOCK_EVENT_OVERHEAD;
block_len = len - APPEND_BLOCK_EVENT_OVERHEAD;
file_id= uint4korr(buf + common_header_len + AB_FILE_ID_OFFSET);
block= (char*)buf + total_header_len;
block_len= len - total_header_len;
DBUG_VOID_RETURN;
}
/*
Append_block_log_event::write_data()
Append_block_log_event::write()
*/
int Append_block_log_event::write_data(IO_CACHE* file)
bool Append_block_log_event::write(IO_CACHE* file)
{
byte buf[APPEND_BLOCK_HEADER_LEN];
int4store(buf + AB_FILE_ID_OFFSET, file_id);
return (my_b_safe_write(file, buf, APPEND_BLOCK_HEADER_LEN) ||
return (write_header(file, APPEND_BLOCK_HEADER_LEN + block_len) ||
my_b_safe_write(file, buf, APPEND_BLOCK_HEADER_LEN) ||
my_b_safe_write(file, (byte*) block, block_len));
}
......@@ -3000,7 +3774,7 @@ int Append_block_log_event::write_data(IO_CACHE* file)
#ifdef MYSQL_CLIENT
void Append_block_log_event::print(FILE* file, bool short_form,
char* last_db)
LAST_EVENT_INFO* last_event_info)
{
if (short_form)
return;
......@@ -3086,24 +3860,28 @@ Delete_file_log_event::Delete_file_log_event(THD *thd_arg, const char* db_arg,
Delete_file_log_event ctor
*/
Delete_file_log_event::Delete_file_log_event(const char* buf, int len)
:Log_event(buf, 0),file_id(0)
Delete_file_log_event::Delete_file_log_event(const char* buf, uint len,
const Format_description_log_event* description_event)
:Log_event(buf, description_event),file_id(0)
{
if ((uint)len < DELETE_FILE_EVENT_OVERHEAD)
uint8 common_header_len= description_event->common_header_len;
uint8 delete_file_header_len= description_event->post_header_len[DELETE_FILE_EVENT-1];
if (len < (uint)(common_header_len + delete_file_header_len))
return;
file_id = uint4korr(buf + LOG_EVENT_HEADER_LEN + AB_FILE_ID_OFFSET);
file_id= uint4korr(buf + common_header_len + DF_FILE_ID_OFFSET);
}
/*
Delete_file_log_event::write_data()
Delete_file_log_event::write()
*/
int Delete_file_log_event::write_data(IO_CACHE* file)
bool Delete_file_log_event::write(IO_CACHE* file)
{
byte buf[DELETE_FILE_HEADER_LEN];
int4store(buf + DF_FILE_ID_OFFSET, file_id);
return my_b_safe_write(file, buf, DELETE_FILE_HEADER_LEN);
return (write_header(file, sizeof(buf)) ||
my_b_safe_write(file, buf, sizeof(buf)));
}
......@@ -3113,7 +3891,7 @@ int Delete_file_log_event::write_data(IO_CACHE* file)
#ifdef MYSQL_CLIENT
void Delete_file_log_event::print(FILE* file, bool short_form,
char* last_db)
LAST_EVENT_INFO* last_event_info)
{
if (short_form)
return;
......@@ -3176,24 +3954,28 @@ Execute_load_log_event::Execute_load_log_event(THD *thd_arg, const char* db_arg,
Execute_load_log_event ctor
*/
Execute_load_log_event::Execute_load_log_event(const char* buf, int len)
:Log_event(buf, 0), file_id(0)
Execute_load_log_event::Execute_load_log_event(const char* buf, uint len,
const Format_description_log_event* description_event)
:Log_event(buf, description_event), file_id(0)
{
if ((uint)len < EXEC_LOAD_EVENT_OVERHEAD)
uint8 common_header_len= description_event->common_header_len;
uint8 exec_load_header_len= description_event->post_header_len[EXEC_LOAD_EVENT-1];
if (len < (uint)(common_header_len+exec_load_header_len))
return;
file_id = uint4korr(buf + LOG_EVENT_HEADER_LEN + EL_FILE_ID_OFFSET);
file_id= uint4korr(buf + common_header_len + EL_FILE_ID_OFFSET);
}
/*
Execute_load_log_event::write_data()
Execute_load_log_event::write()
*/
int Execute_load_log_event::write_data(IO_CACHE* file)
bool Execute_load_log_event::write(IO_CACHE* file)
{
byte buf[EXEC_LOAD_HEADER_LEN];
int4store(buf + EL_FILE_ID_OFFSET, file_id);
return my_b_safe_write(file, buf, EXEC_LOAD_HEADER_LEN);
return (write_header(file, sizeof(buf)) ||
my_b_safe_write(file, buf, sizeof(buf)));
}
......@@ -3203,7 +3985,7 @@ int Execute_load_log_event::write_data(IO_CACHE* file)
#ifdef MYSQL_CLIENT
void Execute_load_log_event::print(FILE* file, bool short_form,
char* last_db)
LAST_EVENT_INFO* last_event_info)
{
if (short_form)
return;
......@@ -3251,7 +4033,7 @@ int Execute_load_log_event::exec_event(struct st_relay_log_info* rli)
}
if (!(lev = (Load_log_event*)Log_event::read_log_event(&file,
(pthread_mutex_t*)0,
(bool)0)) ||
rli->relay_log.description_event_for_exec)) ||
lev->get_type_code() != NEW_LOAD_EVENT)
{
slave_print_error(rli,0, "Error in Exec_load event: file '%s' appears corrupted", fname);
......@@ -3266,15 +4048,7 @@ int Execute_load_log_event::exec_event(struct st_relay_log_info* rli)
mysql_load()).
*/
#if MYSQL_VERSION_ID < 40100
rli->future_master_log_pos= log_pos + get_event_len() -
(rli->mi->old_format ? (LOG_EVENT_HEADER_LEN - OLD_HEADER_LEN) : 0);
#elif MYSQL_VERSION_ID < 50000
rli->future_group_master_log_pos= log_pos + get_event_len() -
(rli->mi->old_format ? (LOG_EVENT_HEADER_LEN - OLD_HEADER_LEN) : 0);
#else
rli->future_group_master_log_pos= log_pos;
#endif
if (lev->exec_event(0,rli,1))
{
/*
......@@ -3332,15 +4106,15 @@ int Execute_load_log_event::exec_event(struct st_relay_log_info* rli)
sql_ex_info::write_data()
*/
int sql_ex_info::write_data(IO_CACHE* file)
bool sql_ex_info::write_data(IO_CACHE* file)
{
if (new_format())
{
return (write_str(file, field_term, field_term_len) ||
write_str(file, enclosed, enclosed_len) ||
write_str(file, line_term, line_term_len) ||
write_str(file, line_start, line_start_len) ||
write_str(file, escaped, escaped_len) ||
return (write_str(file, field_term, (uint) field_term_len) ||
write_str(file, enclosed, (uint) enclosed_len) ||
write_str(file, line_term, (uint) line_term_len) ||
write_str(file, line_start, (uint) line_start_len) ||
write_str(file, escaped, (uint) escaped_len) ||
my_b_safe_write(file,(byte*) &opt_flags,1));
}
else
......@@ -3353,7 +4127,7 @@ int sql_ex_info::write_data(IO_CACHE* file)
old_ex.escaped= *escaped;
old_ex.opt_flags= opt_flags;
old_ex.empty_flags=empty_flags;
return my_b_safe_write(file, (byte*) &old_ex, sizeof(old_ex));
return my_b_safe_write(file, (byte*) &old_ex, sizeof(old_ex)) != 0;
}
}
......@@ -3375,11 +4149,11 @@ char* sql_ex_info::init(char* buf,char* buf_end,bool use_new_format)
the case when we have old format because we will be reusing net buffer
to read the actual file before we write out the Create_file event.
*/
if (read_str(buf, buf_end, field_term, field_term_len) ||
read_str(buf, buf_end, enclosed, enclosed_len) ||
read_str(buf, buf_end, line_term, line_term_len) ||
read_str(buf, buf_end, line_start, line_start_len) ||
read_str(buf, buf_end, escaped, escaped_len))
if (read_str(&buf, buf_end, &field_term, &field_term_len) ||
read_str(&buf, buf_end, &enclosed, &enclosed_len) ||
read_str(&buf, buf_end, &line_term, &line_term_len) ||
read_str(&buf, buf_end, &line_start, &line_start_len) ||
read_str(&buf, buf_end, &escaped, &escaped_len))
return 0;
opt_flags = *buf++;
}
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -35,6 +35,10 @@
#include "sql_acl.h"
#include "lex_symbol.h"
#include "item_create.h"
#include "sp_head.h"
#include "sp_pcontext.h"
#include "sp_rcontext.h"
#include "sp.h"
#include <myisam.h>
#include <myisammrg.h>
......@@ -83,10 +87,14 @@ inline Item *or_or_concat(THD *thd, Item* A, Item* B)
enum Item_udftype udf_type;
CHARSET_INFO *charset;
thr_lock_type lock_type;
interval_type interval;
interval_type interval, interval_time_st;
timestamp_type date_time_type;
st_select_lex *select_lex;
chooser_compare_func_creator boolfunc2creator;
struct sp_cond_type *spcondtype;
struct { int vars, conds, hndlrs, curs; } spblock;
sp_name *spname;
struct st_lex *lex;
}
%{
......@@ -127,6 +135,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token AVG_SYM
%token BEGIN_SYM
%token BINLOG_SYM
%token CALL_SYM
%token CHANGE
%token CLIENT_SYM
%token COMMENT_SYM
......@@ -136,7 +145,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token CREATE
%token CROSS
%token CUBE_SYM
%token DEFINER_SYM
%token DELETE_SYM
%token DETERMINISTIC_SYM
%token DUAL_SYM
%token DO_SYM
%token DROP
......@@ -167,6 +178,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token SHOW
%token SLAVE
%token SNAPSHOT_SYM
%token SQL_SYM
%token SQL_THREAD
%token START_SYM
%token STD_SYM
......@@ -182,6 +194,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token ACTION
%token AGGREGATE_SYM
%token ALGORITHM_SYM
%token ALL
%token AND_SYM
%token AS
......@@ -200,6 +213,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token BYTE_SYM
%token CACHE_SYM
%token CASCADE
%token CASCADED
%token CAST_SYM
%token CHARSET
%token CHECKSUM_SYM
......@@ -210,11 +224,16 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token COLUMNS
%token COLUMN_SYM
%token CONCURRENT
%token CONDITION_SYM
%token CONNECTION_SYM
%token CONSTRAINT
%token CONTAINS_SYM
%token CONTINUE_SYM
%token CONVERT_SYM
%token CURRENT_USER
%token DATABASES
%token DATA_SYM
%token DECLARE_SYM
%token DEFAULT
%token DELAYED_SYM
%token DELAY_KEY_WRITE_SYM
......@@ -226,20 +245,24 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token DISTINCT
%token DUPLICATE_SYM
%token DYNAMIC_SYM
%token EACH_SYM
%token ENABLE_SYM
%token ENCLOSED
%token ESCAPED
%token DIRECTORY_SYM
%token ESCAPE_SYM
%token EXISTS
%token EXIT_SYM
%token EXTENDED_SYM
%token FALSE_SYM
%token FETCH_SYM
%token FILE_SYM
%token FIRST_SYM
%token FIXED_SYM
%token FLOAT_NUM
%token FORCE_SYM
%token FOREIGN
%token FOUND_SYM
%token FROM
%token FULL
%token FULLTEXT_SYM
......@@ -262,8 +285,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token INFILE
%token INNER_SYM
%token INNOBASE_SYM
%token INOUT_SYM
%token INTO
%token IN_SYM
%token INVOKER_SYM
%token ISOLATION
%token JOIN_SYM
%token KEYS
......@@ -273,14 +298,17 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token LEAVES
%token LEVEL_SYM
%token LEX_HOSTNAME
%token LANGUAGE_SYM
%token LIKE
%token LINES
%token LOCAL_SYM
%token LOCATOR_SYM
%token LOG_SYM
%token LOGS_SYM
%token LONG_NUM
%token LONG_SYM
%token LOW_PRIORITY
%token MERGE_SYM
%token MASTER_HOST_SYM
%token MASTER_USER_SYM
%token MASTER_LOG_FILE_SYM
......@@ -305,6 +333,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token MEDIUM_SYM
%token MIN_ROWS
%token NAMES_SYM
%token NAME_SYM
%token NATIONAL_SYM
%token NATURAL
%token NDBCLUSTER_SYM
......@@ -325,6 +354,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token OR_SYM
%token OR_OR_CONCAT
%token ORDER_SYM
%token OUT_SYM
%token OUTER
%token OUTFILE
%token DUMPFILE
......@@ -341,6 +371,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token RAID_CHUNKS
%token RAID_CHUNKSIZE
%token READ_SYM
%token READS_SYM
%token REAL_NUM
%token REFERENCES
%token REGEXP
......@@ -356,6 +387,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token ROW_FORMAT_SYM
%token ROW_SYM
%token RTREE_SYM
%token SECURITY_SYM
%token SET
%token SEPARATOR_SYM
%token SERIAL_SYM
......@@ -364,6 +396,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token SIMPLE_SYM
%token SHUTDOWN
%token SPATIAL_SYM
%token SPECIFIC_SYM
%token SQLEXCEPTION_SYM
%token SQLSTATE_SYM
%token SQLWARNING_SYM
%token SSL_SYM
%token STARTING
%token STATUS_SYM
......@@ -374,11 +410,13 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token TABLE_SYM
%token TABLESPACE
%token TEMPORARY
%token TEMPTABLE_SYM
%token TERMINATED
%token TEXT_STRING
%token TO_SYM
%token TRAILING
%token TRANSACTION_SYM
%token TRIGGER_SYM
%token TRUE_SYM
%token TYPE_SYM
%token TYPES_SYM
......@@ -386,11 +424,15 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token FUNC_ARG1
%token FUNC_ARG2
%token FUNC_ARG3
%token UDF_RETURNS_SYM
%token RETURN_SYM
%token RETURNS_SYM
%token UDF_SONAME_SYM
%token UDF_SYM
%token UDF_RETURNS_SYM
%token FUNCTION_SYM
%token UNCOMMITTED_SYM
%token UNDEFINED_SYM
%token UNDERSCORE_CHARSET
%token UNDO_SYM
%token UNICODE_SYM
%token UNION_SYM
%token UNIQUE_SYM
......@@ -401,6 +443,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token VALUE_SYM
%token VALUES
%token VARIABLES
%token VIEW_SYM
%token WHERE
%token WITH
%token WRITE_SYM
......@@ -408,6 +451,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token X509_SYM
%token XOR
%token COMPRESSED_SYM
%token ROW_COUNT_SYM
%token ERRORS
%token WARNINGS
......@@ -444,6 +488,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token STRING_SYM
%token TEXT_SYM
%token TIMESTAMP
%token TIMESTAMP_ADD
%token TIMESTAMP_DIFF
%token TIME_SYM
%token TINYBLOB
%token TINYINT
......@@ -490,6 +536,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token FIELD_FUNC
%token FORMAT_SYM
%token FOR_SYM
%token FRAC_SECOND_SYM
%token FROM_UNIXTIME
%token GEOMCOLLFROMTEXT
%token GEOMFROMTEXT
......@@ -518,6 +565,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token MINUTE_SECOND_SYM
%token MINUTE_SYM
%token MODE_SYM
%token MODIFIES_SYM
%token MODIFY_SYM
%token MONTH_SYM
%token MLINEFROMTEXT
......@@ -535,6 +583,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token POLYGON
%token POSITION_SYM
%token PROCEDURE
%token QUARTER_SYM
%token RAND
%token REPLACE
%token RIGHT
......@@ -546,12 +595,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token SUBSTRING
%token SUBSTRING_INDEX
%token TRIM
%token UDA_CHAR_SUM
%token UDA_FLOAT_SUM
%token UDA_INT_SUM
%token UDF_CHAR_FUNC
%token UDF_FLOAT_FUNC
%token UDF_INT_FUNC
%token UNIQUE_USERS
%token UNIX_TIMESTAMP
%token USER
......@@ -575,6 +618,20 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token SQL_SMALL_RESULT
%token SQL_BUFFER_RESULT
%token CURSOR_SYM
%token ELSEIF_SYM
%token ITERATE_SYM
%token GOTO_SYM
%token LABEL_SYM
%token LEAVE_SYM
%token LOOP_SYM
%token REPEAT_SYM
%token UNTIL_SYM
%token WHILE_SYM
%token ASENSITIVE_SYM
%token INSENSITIVE_SYM
%token SENSITIVE_SYM
%token ISSUER_SYM
%token SUBJECT_SYM
%token CIPHER_SYM
......@@ -600,6 +657,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text
UNDERSCORE_CHARSET IDENT_sys TEXT_STRING_sys TEXT_STRING_literal
NCHAR_STRING opt_component key_cache_name
sp_opt_label
%type <lex_str_ptr>
opt_table_alias
......@@ -634,17 +692,20 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <item>
literal text_literal insert_ident order_ident
simple_ident select_item2 expr opt_expr opt_else sum_expr in_sum_expr
table_wild no_in_expr expr_expr simple_expr no_and_expr
table_wild no_in_expr expr_expr simple_expr no_and_expr udf_expr
using_list expr_or_default set_expr_or_default interval_expr
param_marker singlerow_subselect singlerow_subselect_init
exists_subselect exists_subselect_init geometry_function
signed_literal now_or_signed_literal opt_escape
sp_opt_default
simple_ident_nospvar simple_ident_q
%type <item_num>
NUM_literal
%type <item_list>
expr_list udf_expr_list when_list ident_list ident_list_arg
expr_list udf_expr_list udf_expr_list2 when_list
ident_list ident_list_arg
%type <key_type>
key_type opt_unique_or_fulltext constraint_key_type
......@@ -660,14 +721,13 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <table_list>
join_table_list join_table
%type <udf>
UDF_CHAR_FUNC UDF_FLOAT_FUNC UDF_INT_FUNC
UDA_CHAR_SUM UDA_FLOAT_SUM UDA_INT_SUM
table_factor table_ref
%type <date_time_type> date_time_type;
%type <interval> interval
%type <interval_time_st> interval_time_st
%type <db_type> storage_engines
%type <row_type> row_types
......@@ -711,7 +771,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
select_item_list select_item values_list no_braces
opt_limit_clause delete_limit_clause fields opt_values values
procedure_list procedure_list2 procedure_item
when_list2 expr_list2 handler
when_list2 expr_list2 udf_expr_list3 handler
opt_precision opt_ignore opt_column opt_restrict
grant revoke set lock unlock string_list field_options field_option
field_opt_list opt_binary table_lock_list table_lock
......@@ -732,8 +792,17 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
subselect_end select_var_list select_var_list_init help opt_len
opt_extended_describe
prepare prepare_src execute deallocate
statement sp_suid opt_view_list view_list or_replace algorithm
sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic
END_OF_INPUT
%type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt
%type <num> sp_decl_idents sp_opt_inout sp_handler_type sp_hcond_list
%type <spcondtype> sp_cond sp_hcond
%type <spblock> sp_decls sp_decl
%type <lex> sp_cursor_stmt
%type <spname> sp_name
%type <NONE>
'-' '+' '*' '/' '%' '(' ')'
',' '!' '{' '}' '&' '|' AND_SYM OR_SYM OR_OR_CONCAT BETWEEN_SYM CASE_SYM
......@@ -759,10 +828,16 @@ query:
| verb_clause END_OF_INPUT {};
verb_clause:
statement
| begin
;
/* Verb clauses, except begin */
statement:
alter
| analyze
| backup
| begin
| call
| change
| check
| checksum
......@@ -907,177 +982,1425 @@ help:
change:
CHANGE MASTER_SYM TO_SYM
{
LEX *lex = Lex;
lex->sql_command = SQLCOM_CHANGE_MASTER;
bzero((char*) &lex->mi, sizeof(lex->mi));
LEX *lex = Lex;
lex->sql_command = SQLCOM_CHANGE_MASTER;
bzero((char*) &lex->mi, sizeof(lex->mi));
}
master_defs
{}
;
master_defs:
master_def
| master_defs ',' master_def;
master_def:
MASTER_HOST_SYM EQ TEXT_STRING_sys
{
Lex->mi.host = $3.str;
}
|
MASTER_USER_SYM EQ TEXT_STRING_sys
{
Lex->mi.user = $3.str;
}
|
MASTER_PASSWORD_SYM EQ TEXT_STRING_sys
{
Lex->mi.password = $3.str;
}
|
MASTER_PORT_SYM EQ ULONG_NUM
{
Lex->mi.port = $3;
}
|
MASTER_CONNECT_RETRY_SYM EQ ULONG_NUM
{
Lex->mi.connect_retry = $3;
}
| MASTER_SSL_SYM EQ ULONG_NUM
{
Lex->mi.ssl= $3 ?
LEX_MASTER_INFO::SSL_ENABLE : LEX_MASTER_INFO::SSL_DISABLE;
}
| MASTER_SSL_CA_SYM EQ TEXT_STRING_sys
{
Lex->mi.ssl_ca= $3.str;
}
| MASTER_SSL_CAPATH_SYM EQ TEXT_STRING_sys
{
Lex->mi.ssl_capath= $3.str;
}
| MASTER_SSL_CERT_SYM EQ TEXT_STRING_sys
{
Lex->mi.ssl_cert= $3.str;
}
| MASTER_SSL_CIPHER_SYM EQ TEXT_STRING_sys
{
Lex->mi.ssl_cipher= $3.str;
}
| MASTER_SSL_KEY_SYM EQ TEXT_STRING_sys
{
Lex->mi.ssl_key= $3.str;
}
|
master_file_def
;
master_file_def:
MASTER_LOG_FILE_SYM EQ TEXT_STRING_sys
{
Lex->mi.log_file_name = $3.str;
}
| MASTER_LOG_POS_SYM EQ ulonglong_num
{
Lex->mi.pos = $3;
/*
If the user specified a value < BIN_LOG_HEADER_SIZE, adjust it
instead of causing subsequent errors.
We need to do it in this file, because only there we know that
MASTER_LOG_POS has been explicitely specified. On the contrary
in change_master() (sql_repl.cc) we cannot distinguish between 0
(MASTER_LOG_POS explicitely specified as 0) and 0 (unspecified),
whereas we want to distinguish (specified 0 means "read the binlog
from 0" (4 in fact), unspecified means "don't change the position
(keep the preceding value)").
*/
Lex->mi.pos = max(BIN_LOG_HEADER_SIZE, Lex->mi.pos);
}
| RELAY_LOG_FILE_SYM EQ TEXT_STRING_sys
{
Lex->mi.relay_log_name = $3.str;
}
| RELAY_LOG_POS_SYM EQ ULONG_NUM
{
Lex->mi.relay_log_pos = $3;
/* Adjust if < BIN_LOG_HEADER_SIZE (same comment as Lex->mi.pos) */
Lex->mi.relay_log_pos = max(BIN_LOG_HEADER_SIZE, Lex->mi.relay_log_pos);
}
;
/* create a table */
create:
CREATE opt_table_options TABLE_SYM opt_if_not_exists table_ident
{
THD *thd= YYTHD;
LEX *lex=Lex;
lex->sql_command= SQLCOM_CREATE_TABLE;
if (!lex->select_lex.add_table_to_list(thd, $5, NULL,
TL_OPTION_UPDATING,
(using_update_log ?
TL_READ_NO_INSERT:
TL_READ)))
YYABORT;
lex->create_list.empty();
lex->key_list.empty();
lex->col_list.empty();
lex->change=NullS;
bzero((char*) &lex->create_info,sizeof(lex->create_info));
lex->create_info.options=$2 | $4;
lex->create_info.db_type= (enum db_type) lex->thd->variables.table_type;
lex->create_info.default_table_charset= NULL;
lex->name=0;
}
create2
{ Lex->current_select= &Lex->select_lex; }
| CREATE opt_unique_or_fulltext INDEX_SYM ident key_alg ON table_ident
{
LEX *lex=Lex;
lex->sql_command= SQLCOM_CREATE_INDEX;
if (!lex->current_select->add_table_to_list(lex->thd, $7, NULL,
TL_OPTION_UPDATING))
YYABORT;
lex->create_list.empty();
lex->key_list.empty();
lex->col_list.empty();
lex->change=NullS;
}
'(' key_list ')'
{
LEX *lex=Lex;
lex->key_list.push_back(new Key($2,$4.str, $5, 0, lex->col_list));
lex->col_list.empty();
}
| CREATE DATABASE opt_if_not_exists ident
{
Lex->create_info.default_table_charset= NULL;
Lex->create_info.used_fields= 0;
}
opt_create_database_options
{
LEX *lex=Lex;
lex->sql_command=SQLCOM_CREATE_DB;
lex->name=$4.str;
lex->create_info.options=$3;
}
| CREATE udf_func_type FUNCTION_SYM sp_name
{
LEX *lex=Lex;
lex->spname= $4;
lex->udf.type= $2;
}
create_function_tail
{}
| CREATE PROCEDURE sp_name
{
LEX *lex= Lex;
sp_head *sp;
if (lex->sphead)
{
net_printf(YYTHD, ER_SP_NO_RECURSIVE_CREATE, "PROCEDURE");
YYABORT;
}
/* Order is important here: new - reset - init */
sp= new sp_head();
sp->reset_thd_mem_root(YYTHD);
sp->init(lex);
sp->m_type= TYPE_ENUM_PROCEDURE;
lex->sphead= sp;
/*
* We have to turn of CLIENT_MULTI_QUERIES while parsing a
* stored procedure, otherwise yylex will chop it into pieces
* at each ';'.
*/
sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES);
}
'('
{
LEX *lex= Lex;
lex->sphead->m_param_begin= lex->tok_start+1;
}
sp_pdparam_list
')'
{
LEX *lex= Lex;
lex->sphead->m_param_end= lex->tok_start;
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
}
sp_c_chistics
{
LEX *lex= Lex;
lex->sphead->m_chistics= &lex->sp_chistics;
lex->sphead->m_body_begin= lex->tok_start;
}
sp_proc_stmt
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
if (sp->check_backpatch(YYTHD))
YYABORT;
sp->init_strings(YYTHD, lex, $3);
lex->sql_command= SQLCOM_CREATE_PROCEDURE;
/* Restore flag if it was cleared above */
if (sp->m_old_cmq)
YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
sp->restore_thd_mem_root(YYTHD);
}
| CREATE or_replace algorithm VIEW_SYM table_ident
{
THD *thd= YYTHD;
LEX *lex= thd->lex;
lex->sql_command= SQLCOM_CREATE_VIEW;
lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE;
/* first table in list is target VIEW name */
if (!lex->select_lex.add_table_to_list(thd, $5, NULL, 0))
YYABORT;
}
opt_view_list AS select_init check_option
{}
| CREATE TRIGGER_SYM ident trg_action_time trg_event
ON table_ident FOR_SYM EACH_SYM ROW_SYM
{
LEX *lex= Lex;
sp_head *sp;
if (lex->sphead)
{
net_printf(YYTHD, ER_SP_NO_RECURSIVE_CREATE, "TRIGGER");
YYABORT;
}
sp= new sp_head();
sp->reset_thd_mem_root(YYTHD);
sp->init(lex);
sp->m_type= TYPE_ENUM_TRIGGER;
lex->sphead= sp;
/*
We have to turn of CLIENT_MULTI_QUERIES while parsing a
stored procedure, otherwise yylex will chop it into pieces
at each ';'.
*/
sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES;
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
lex->sphead->m_chistics= &lex->sp_chistics;
lex->sphead->m_body_begin= lex->tok_start;
}
sp_proc_stmt
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
lex->sql_command= SQLCOM_CREATE_TRIGGER;
sp->init_strings(YYTHD, lex, NULL);
/* Restore flag if it was cleared above */
if (sp->m_old_cmq)
YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
sp->restore_thd_mem_root(YYTHD);
lex->name_and_length= $3;
/*
We have to do it after parsing trigger body, because some of
sp_proc_stmt alternatives are not saving/restoring LEX, so
lex->query_tables can be wiped out.
QQ: What are other consequences of this?
QQ: Could we loosen lock type in certain cases ?
*/
if (!lex->select_lex.add_table_to_list(YYTHD, $7,
(LEX_STRING*) 0,
TL_OPTION_UPDATING,
TL_WRITE))
YYABORT;
}
;
sp_name:
IDENT_sys '.' IDENT_sys
{
$$= new sp_name($1, $3);
$$->init_qname(YYTHD);
}
| IDENT_sys
{
$$= sp_name_current_db_new(YYTHD, $1);
}
;
create_function_tail:
RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING_sys
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_CREATE_FUNCTION;
lex->udf.name = lex->spname->m_name;
lex->udf.returns=(Item_result) $2;
lex->udf.dl=$4.str;
}
| '('
{
LEX *lex= Lex;
sp_head *sp;
if (lex->sphead)
{
net_printf(YYTHD, ER_SP_NO_RECURSIVE_CREATE, "FUNCTION");
YYABORT;
}
/* Order is important here: new - reset - init */
sp= new sp_head();
sp->reset_thd_mem_root(YYTHD);
sp->init(lex);
sp->m_type= TYPE_ENUM_FUNCTION;
lex->sphead= sp;
/*
* We have to turn of CLIENT_MULTI_QUERIES while parsing a
* stored procedure, otherwise yylex will chop it into pieces
* at each ';'.
*/
sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES;
lex->sphead->m_param_begin= lex->tok_start+1;
}
sp_fdparam_list ')'
{
LEX *lex= Lex;
lex->sphead->m_param_end= lex->tok_start;
}
RETURNS_SYM
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
sp->m_returns_begin= lex->tok_start;
sp->m_returns_cs= lex->charset= NULL;
}
type
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
sp->m_returns_end= lex->tok_start;
sp->m_returns= (enum enum_field_types)$8;
sp->m_returns_cs= lex->charset;
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
}
sp_c_chistics
{
LEX *lex= Lex;
lex->sphead->m_chistics= &lex->sp_chistics;
lex->sphead->m_body_begin= lex->tok_start;
}
sp_proc_stmt
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
if (sp->check_backpatch(YYTHD))
YYABORT;
lex->sql_command= SQLCOM_CREATE_SPFUNCTION;
sp->init_strings(YYTHD, lex, lex->spname);
/* Restore flag if it was cleared above */
if (sp->m_old_cmq)
YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
sp->restore_thd_mem_root(YYTHD);
}
;
sp_a_chistics:
/* Empty */ {}
| sp_a_chistics sp_chistic {}
;
sp_c_chistics:
/* Empty */ {}
| sp_c_chistics sp_c_chistic {}
;
/* Characteristics for both create and alter */
sp_chistic:
COMMENT_SYM TEXT_STRING_sys
{ Lex->sp_chistics.comment= $2; }
| LANGUAGE_SYM SQL_SYM
{ /* Just parse it, we only have one language for now. */ }
| NO_SYM SQL_SYM
{ Lex->sp_chistics.daccess= SP_NO_SQL; }
| CONTAINS_SYM SQL_SYM
{ Lex->sp_chistics.daccess= SP_CONTAINS_SQL; }
| READS_SYM SQL_SYM DATA_SYM
{ Lex->sp_chistics.daccess= SP_READS_SQL_DATA; }
| MODIFIES_SYM SQL_SYM DATA_SYM
{ Lex->sp_chistics.daccess= SP_MODIFIES_SQL_DATA; }
| sp_suid
{ }
;
/* Create characteristics */
sp_c_chistic:
sp_chistic { }
| DETERMINISTIC_SYM { Lex->sp_chistics.detistic= TRUE; }
| NOT DETERMINISTIC_SYM { Lex->sp_chistics.detistic= FALSE; }
;
sp_suid:
SQL_SYM SECURITY_SYM DEFINER_SYM
{
Lex->sp_chistics.suid= SP_IS_SUID;
}
| SQL_SYM SECURITY_SYM INVOKER_SYM
{
Lex->sp_chistics.suid= SP_IS_NOT_SUID;
}
;
call:
CALL_SYM sp_name
{
LEX *lex = Lex;
lex->sql_command= SQLCOM_CALL;
lex->spname= $2;
lex->value_list.empty();
}
'(' sp_cparam_list ')' {}
;
/* CALL parameters */
sp_cparam_list:
/* Empty */
| sp_cparams
;
sp_cparams:
sp_cparams ',' expr
{
Lex->value_list.push_back($3);
}
| expr
{
Lex->value_list.push_back($1);
}
;
/* Stored FUNCTION parameter declaration list */
sp_fdparam_list:
/* Empty */
| sp_fdparams
;
sp_fdparams:
sp_fdparams ',' sp_fdparam
| sp_fdparam
;
sp_fdparam:
ident type
{
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
if (spc->find_pvar(&$1, TRUE))
{
net_printf(YYTHD, ER_SP_DUP_PARAM, $1.str);
YYABORT;
}
spc->push_pvar(&$1, (enum enum_field_types)$2, sp_param_in);
}
;
/* Stored PROCEDURE parameter declaration list */
sp_pdparam_list:
/* Empty */
| sp_pdparams
;
sp_pdparams:
sp_pdparams ',' sp_pdparam
| sp_pdparam
;
sp_pdparam:
sp_opt_inout ident type
{
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
if (spc->find_pvar(&$2, TRUE))
{
net_printf(YYTHD, ER_SP_DUP_PARAM, $2.str);
YYABORT;
}
spc->push_pvar(&$2, (enum enum_field_types)$3,
(sp_param_mode_t)$1);
}
;
sp_opt_inout:
/* Empty */ { $$= sp_param_in; }
| IN_SYM { $$= sp_param_in; }
| OUT_SYM { $$= sp_param_out; }
| INOUT_SYM { $$= sp_param_inout; }
;
sp_proc_stmts:
/* Empty */ {}
| sp_proc_stmts { Lex->query_tables= 0; } sp_proc_stmt ';'
;
sp_proc_stmts1:
sp_proc_stmt ';' {}
| sp_proc_stmts1 { Lex->query_tables= 0; } sp_proc_stmt ';'
;
sp_decls:
/* Empty */
{
$$.vars= $$.conds= $$.hndlrs= $$.curs= 0;
}
| sp_decls sp_decl ';'
{
/* We check for declarations out of (standard) order this way
because letting the grammar rules reflect it caused tricky
shift/reduce conflicts with the wrong result. (And we get
better error handling this way.) */
if (($2.vars || $2.conds) && ($1.curs || $1.hndlrs))
{ /* Variable or condition following cursor or handler */
send_error(YYTHD, ER_SP_VARCOND_AFTER_CURSHNDLR);
YYABORT;
}
if ($2.curs && $1.hndlrs)
{ /* Cursor following handler */
send_error(YYTHD, ER_SP_CURSOR_AFTER_HANDLER);
YYABORT;
}
$$.vars= $1.vars + $2.vars;
$$.conds= $1.conds + $2.conds;
$$.hndlrs= $1.hndlrs + $2.hndlrs;
$$.curs= $1.curs + $2.curs;
}
;
sp_decl:
DECLARE_SYM sp_decl_idents type sp_opt_default
{
LEX *lex= Lex;
sp_pcontext *ctx= lex->spcont;
uint max= ctx->context_pvars();
enum enum_field_types type= (enum enum_field_types)$3;
Item *it= $4;
for (uint i = max-$2 ; i < max ; i++)
{
ctx->set_type(i, type);
if (! it)
ctx->set_isset(i, FALSE);
else
{
sp_instr_set *in= new sp_instr_set(lex->sphead->instructions(),
ctx,
ctx->pvar_context2index(i),
it, type);
in->tables= lex->query_tables;
lex->query_tables= 0;
lex->sphead->add_instr(in);
ctx->set_isset(i, TRUE);
ctx->set_default(i, it);
}
}
$$.vars= $2;
$$.conds= $$.hndlrs= $$.curs= 0;
}
| DECLARE_SYM ident CONDITION_SYM FOR_SYM sp_cond
{
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
if (spc->find_cond(&$2, TRUE))
{
net_printf(YYTHD, ER_SP_DUP_COND, $2.str);
YYABORT;
}
YYTHD->lex->spcont->push_cond(&$2, $5);
$$.vars= $$.hndlrs= $$.curs= 0;
$$.conds= 1;
}
| DECLARE_SYM sp_handler_type HANDLER_SYM FOR_SYM
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
sp_pcontext *ctx= lex->spcont;
sp_instr_hpush_jump *i=
new sp_instr_hpush_jump(sp->instructions(), ctx, $2,
ctx->current_pvars());
sp->add_instr(i);
sp->push_backpatch(i, ctx->push_label((char *)"", 0));
ctx->add_handler();
sp->m_in_handler= TRUE;
}
sp_hcond_list sp_proc_stmt
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
sp_pcontext *ctx= lex->spcont;
sp_label_t *hlab= lex->spcont->pop_label(); /* After this hdlr */
sp_instr_hreturn *i;
if ($2 == SP_HANDLER_CONTINUE)
{
i= new sp_instr_hreturn(sp->instructions(), ctx,
ctx->current_pvars());
sp->add_instr(i);
}
else
{ /* EXIT or UNDO handler, just jump to the end of the block */
i= new sp_instr_hreturn(sp->instructions(), ctx, 0);
sp->add_instr(i);
sp->push_backpatch(i, lex->spcont->last_label()); /* Block end */
}
lex->sphead->backpatch(hlab);
sp->m_in_handler= FALSE;
$$.vars= $$.conds= $$.curs= 0;
$$.hndlrs= $6;
}
| DECLARE_SYM ident CURSOR_SYM FOR_SYM sp_cursor_stmt
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
sp_pcontext *ctx= lex->spcont;
uint offp;
sp_instr_cpush *i;
if (ctx->find_cursor(&$2, &offp, TRUE))
{
net_printf(YYTHD, ER_SP_DUP_CURS, $2.str);
delete $5;
YYABORT;
}
i= new sp_instr_cpush(sp->instructions(), ctx, $5);
sp->add_instr(i);
ctx->push_cursor(&$2);
$$.vars= $$.conds= $$.hndlrs= 0;
$$.curs= 1;
}
;
sp_cursor_stmt:
{
Lex->sphead->reset_lex(YYTHD);
/* We use statement here just be able to get a better
error message. Using 'select' works too, but will then
result in a generic "syntax error" if a non-select
statement is given. */
}
statement
{
LEX *lex= Lex;
if (lex->sql_command != SQLCOM_SELECT)
{
send_error(YYTHD, ER_SP_BAD_CURSOR_QUERY);
YYABORT;
}
if (lex->result)
{
send_error(YYTHD, ER_SP_BAD_CURSOR_SELECT);
YYABORT;
}
lex->sp_lex_in_use= TRUE;
$$= lex;
lex->sphead->restore_lex(YYTHD);
}
;
sp_handler_type:
EXIT_SYM { $$= SP_HANDLER_EXIT; }
| CONTINUE_SYM { $$= SP_HANDLER_CONTINUE; }
/* | UNDO_SYM { QQ No yet } */
;
sp_hcond_list:
sp_hcond
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
sp_instr_hpush_jump *i= (sp_instr_hpush_jump *)sp->last_instruction();
i->add_condition($1);
$$= 1;
}
| sp_hcond_list ',' sp_hcond
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
sp_instr_hpush_jump *i= (sp_instr_hpush_jump *)sp->last_instruction();
i->add_condition($3);
$$= $1 + 1;
}
;
sp_cond:
ULONG_NUM
{ /* mysql errno */
$$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
$$->type= sp_cond_type_t::number;
$$->mysqlerr= $1;
}
| SQLSTATE_SYM opt_value TEXT_STRING_literal
{ /* SQLSTATE */
uint len= ($3.length < sizeof($$->sqlstate)-1 ?
$3.length : sizeof($$->sqlstate)-1);
$$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
$$->type= sp_cond_type_t::state;
memcpy($$->sqlstate, $3.str, len);
$$->sqlstate[len]= '\0';
}
;
opt_value:
/* Empty */ {}
| VALUE_SYM {}
;
sp_hcond:
sp_cond
{
$$= $1;
}
| ident /* CONDITION name */
{
$$= Lex->spcont->find_cond(&$1);
if ($$ == NULL)
{
net_printf(YYTHD, ER_SP_COND_MISMATCH, $1.str);
YYABORT;
}
}
| SQLWARNING_SYM /* SQLSTATEs 01??? */
{
$$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
$$->type= sp_cond_type_t::warning;
}
| NOT FOUND_SYM /* SQLSTATEs 02??? */
{
$$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
$$->type= sp_cond_type_t::notfound;
}
| SQLEXCEPTION_SYM /* All other SQLSTATEs */
{
$$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
$$->type= sp_cond_type_t::exception;
}
;
sp_decl_idents:
ident
{
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
if (spc->find_pvar(&$1, TRUE))
{
net_printf(YYTHD, ER_SP_DUP_VAR, $1.str);
YYABORT;
}
spc->push_pvar(&$1, (enum_field_types)0, sp_param_in);
$$= 1;
}
| sp_decl_idents ',' ident
{
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
if (spc->find_pvar(&$3, TRUE))
{
net_printf(YYTHD, ER_SP_DUP_VAR, $3.str);
YYABORT;
}
spc->push_pvar(&$3, (enum_field_types)0, sp_param_in);
$$= $1 + 1;
}
;
sp_opt_default:
/* Empty */ { $$ = NULL; }
| DEFAULT expr { $$ = $2; }
;
sp_proc_stmt:
{
LEX *lex= Lex;
lex->sphead->reset_lex(YYTHD);
lex->sphead->m_tmp_query= lex->tok_start;
}
statement
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
if ((lex->sql_command == SQLCOM_SELECT && !lex->result) ||
sp_multi_results_command(lex->sql_command))
{
/* We maybe have one or more SELECT without INTO */
sp->m_multi_results= TRUE;
}
if (lex->sql_command == SQLCOM_CHANGE_DB)
{ /* "USE db" doesn't work in a procedure */
send_error(YYTHD, ER_SP_NO_USE);
YYABORT;
}
/* Don't add an instruction for empty SET statements.
** (This happens if the SET only contained local variables,
** which get their set instructions generated separately.)
*/
if (lex->sql_command != SQLCOM_SET_OPTION ||
! lex->var_list.is_empty())
{
/*
Currently we can't handle queries inside a FUNCTION or
TRIGGER, because of the way table locking works. This is
unfortunate, and limits the usefulness of functions and
especially triggers a tremendously, but it's nothing we
can do about this at the moment.
*/
if (sp->m_type != TYPE_ENUM_PROCEDURE)
{
send_error(YYTHD, ER_SP_BADSTATEMENT);
YYABORT;
}
else
{
sp_instr_stmt *i=new sp_instr_stmt(sp->instructions(),
lex->spcont);
/* Extract the query statement from the tokenizer:
The end is either lex->tok_end or tok->ptr. */
if (lex->ptr - lex->tok_end > 1)
i->m_query.length= lex->ptr - sp->m_tmp_query;
else
i->m_query.length= lex->tok_end - sp->m_tmp_query;
i->m_query.str= strmake_root(YYTHD->mem_root,
(char *)sp->m_tmp_query,
i->m_query.length);
i->set_lex(lex);
sp->add_instr(i);
lex->sp_lex_in_use= TRUE;
}
}
sp->restore_lex(YYTHD);
}
| RETURN_SYM expr
{
LEX *lex= Lex;
if (lex->sphead->m_type == TYPE_ENUM_PROCEDURE)
{
send_error(YYTHD, ER_SP_BADRETURN);
YYABORT;
}
else
{
sp_instr_freturn *i;
if ($2->type() == Item::SUBSELECT_ITEM)
{ /* QQ For now, just disallow subselects as values */
send_error(lex->thd, ER_SP_BADSTATEMENT);
YYABORT;
}
i= new sp_instr_freturn(lex->sphead->instructions(),
lex->spcont,
$2, lex->sphead->m_returns);
lex->sphead->add_instr(i);
lex->sphead->m_has_return= TRUE;
}
}
| IF sp_if END IF {}
| CASE_SYM WHEN_SYM
{
Lex->sphead->m_simple_case= FALSE;
}
sp_case END CASE_SYM {}
| CASE_SYM expr WHEN_SYM
{
/* We "fake" this by using an anonymous variable which we
set to the expression. Note that all WHENs are evaluate
at the same frame level, so we then know that it's the
top-most variable in the frame. */
LEX *lex= Lex;
uint offset= lex->spcont->current_pvars();
sp_instr_set *i = new sp_instr_set(lex->sphead->instructions(),
lex->spcont,
offset, $2, MYSQL_TYPE_STRING);
LEX_STRING dummy;
dummy.str= (char *)"";
dummy.length= 0;
lex->spcont->push_pvar(&dummy, MYSQL_TYPE_STRING, sp_param_in);
i->tables= lex->query_tables;
lex->query_tables= 0;
lex->sphead->add_instr(i);
lex->sphead->m_simple_case= TRUE;
}
sp_case END CASE_SYM
{
Lex->spcont->pop_pvar();
}
| sp_labeled_control
{}
| { /* Unlabeled controls get a secret label. */
LEX *lex= Lex;
lex->spcont->push_label((char *)"", lex->sphead->instructions());
}
sp_unlabeled_control
{
LEX *lex= Lex;
lex->sphead->backpatch(lex->spcont->pop_label());
}
| LEAVE_SYM IDENT
{
LEX *lex= Lex;
sp_head *sp = lex->sphead;
sp_pcontext *ctx= lex->spcont;
sp_label_t *lab= ctx->find_label($2.str);
if (! lab)
{
net_printf(YYTHD, ER_SP_LILABEL_MISMATCH, "LEAVE", $2.str);
YYABORT;
}
else
{
uint ip= sp->instructions();
sp_instr_jump *i;
sp_instr_hpop *ih;
sp_instr_cpop *ic;
ih= new sp_instr_hpop(ip++, ctx, 0);
sp->push_backpatch(ih, lab);
sp->add_instr(ih);
ic= new sp_instr_cpop(ip++, ctx, 0);
sp->push_backpatch(ic, lab);
sp->add_instr(ic);
i= new sp_instr_jump(ip, ctx);
sp->push_backpatch(i, lab); /* Jumping forward */
sp->add_instr(i);
}
}
| ITERATE_SYM IDENT
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
sp_pcontext *ctx= lex->spcont;
sp_label_t *lab= ctx->find_label($2.str);
if (! lab || lab->type != SP_LAB_ITER)
{
net_printf(YYTHD, ER_SP_LILABEL_MISMATCH, "ITERATE", $2.str);
YYABORT;
}
else
{
sp_instr_jump *i;
uint ip= sp->instructions();
uint n;
n= ctx->diff_handlers(lab->ctx);
if (n)
sp->add_instr(new sp_instr_hpop(ip++, ctx, n));
n= ctx->diff_cursors(lab->ctx);
if (n)
sp->add_instr(new sp_instr_cpop(ip++, ctx, n));
i= new sp_instr_jump(ip, ctx, lab->ip); /* Jump back */
sp->add_instr(i);
}
}
| LABEL_SYM IDENT
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
sp_pcontext *ctx= lex->spcont;
sp_label_t *lab= ctx->find_label($2.str);
if (lab)
{
net_printf(YYTHD, ER_SP_LABEL_REDEFINE, $2.str);
YYABORT;
}
else
{
lab= ctx->push_label($2.str, sp->instructions());
lab->type= SP_LAB_GOTO;
lab->ctx= ctx;
sp->backpatch(lab);
}
}
| GOTO_SYM IDENT
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
sp_pcontext *ctx= lex->spcont;
uint ip= lex->sphead->instructions();
sp_label_t *lab;
sp_instr_jump *i;
sp_instr_hpop *ih;
sp_instr_cpop *ic;
if (sp->m_in_handler)
{
send_error(lex->thd, ER_SP_GOTO_IN_HNDLR);
YYABORT;
}
lab= ctx->find_label($2.str);
if (! lab)
{
lab= (sp_label_t *)YYTHD->alloc(sizeof(sp_label_t));
lab->name= $2.str;
lab->ip= 0;
lab->type= SP_LAB_REF;
lab->ctx= ctx;
ih= new sp_instr_hpop(ip++, ctx, 0);
sp->push_backpatch(ih, lab);
sp->add_instr(ih);
ic= new sp_instr_cpop(ip++, ctx, 0);
sp->add_instr(ic);
sp->push_backpatch(ic, lab);
i= new sp_instr_jump(ip, ctx);
sp->push_backpatch(i, lab); /* Jumping forward */
sp->add_instr(i);
}
else
{
uint n;
n= ctx->diff_handlers(lab->ctx);
if (n)
{
ih= new sp_instr_hpop(ip++, ctx, n);
sp->add_instr(ih);
}
n= ctx->diff_cursors(lab->ctx);
if (n)
{
ic= new sp_instr_cpop(ip++, ctx, n);
sp->add_instr(ic);
}
i= new sp_instr_jump(ip, ctx, lab->ip); /* Jump back */
sp->add_instr(i);
}
}
| OPEN_SYM ident
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
uint offset;
sp_instr_copen *i;
if (! lex->spcont->find_cursor(&$2, &offset))
{
net_printf(YYTHD, ER_SP_CURSOR_MISMATCH, $2.str);
YYABORT;
}
i= new sp_instr_copen(sp->instructions(), lex->spcont, offset);
sp->add_instr(i);
}
| FETCH_SYM sp_opt_fetch_noise ident INTO
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
uint offset;
sp_instr_cfetch *i;
if (! lex->spcont->find_cursor(&$3, &offset))
{
net_printf(YYTHD, ER_SP_CURSOR_MISMATCH, $3.str);
YYABORT;
}
i= new sp_instr_cfetch(sp->instructions(), lex->spcont, offset);
sp->add_instr(i);
}
sp_fetch_list
{ }
| CLOSE_SYM ident
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
uint offset;
sp_instr_cclose *i;
if (! lex->spcont->find_cursor(&$2, &offset))
{
net_printf(YYTHD, ER_SP_CURSOR_MISMATCH, $2.str);
YYABORT;
}
i= new sp_instr_cclose(sp->instructions(), lex->spcont, offset);
sp->add_instr(i);
}
master_defs
{}
;
master_defs:
master_def
| master_defs ',' master_def;
sp_opt_fetch_noise:
/* Empty */
| NEXT_SYM FROM
| FROM
;
master_def:
MASTER_HOST_SYM EQ TEXT_STRING_sys
sp_fetch_list:
ident
{
Lex->mi.host = $3.str;
}
|
MASTER_USER_SYM EQ TEXT_STRING_sys
LEX *lex= Lex;
sp_head *sp= lex->sphead;
sp_pcontext *spc= lex->spcont;
sp_pvar_t *spv;
if (!spc || !(spv = spc->find_pvar(&$1)))
{
Lex->mi.user = $3.str;
net_printf(YYTHD, ER_SP_UNDECLARED_VAR, $1.str);
YYABORT;
}
|
MASTER_PASSWORD_SYM EQ TEXT_STRING_sys
else
{
Lex->mi.password = $3.str;
/* An SP local variable */
sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction();
i->add_to_varlist(spv);
spv->isset= TRUE;
}
|
MASTER_PORT_SYM EQ ULONG_NUM
{
Lex->mi.port = $3;
}
|
MASTER_CONNECT_RETRY_SYM EQ ULONG_NUM
sp_fetch_list ',' ident
{
Lex->mi.connect_retry = $3;
}
| MASTER_SSL_SYM EQ ULONG_NUM
LEX *lex= Lex;
sp_head *sp= lex->sphead;
sp_pcontext *spc= lex->spcont;
sp_pvar_t *spv;
if (!spc || !(spv = spc->find_pvar(&$3)))
{
Lex->mi.ssl= $3 ?
LEX_MASTER_INFO::SSL_ENABLE : LEX_MASTER_INFO::SSL_DISABLE;
net_printf(YYTHD, ER_SP_UNDECLARED_VAR, $3.str);
YYABORT;
}
| MASTER_SSL_CA_SYM EQ TEXT_STRING_sys
else
{
Lex->mi.ssl_ca= $3.str;
/* An SP local variable */
sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction();
i->add_to_varlist(spv);
spv->isset= TRUE;
}
| MASTER_SSL_CAPATH_SYM EQ TEXT_STRING_sys
{
Lex->mi.ssl_capath= $3.str;
}
| MASTER_SSL_CERT_SYM EQ TEXT_STRING_sys
;
sp_if:
expr THEN_SYM
{
Lex->mi.ssl_cert= $3.str;
LEX *lex= Lex;
sp_head *sp= lex->sphead;
sp_pcontext *ctx= lex->spcont;
uint ip= sp->instructions();
sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, ctx, $1);
i->tables= lex->query_tables;
lex->query_tables= 0;
sp->push_backpatch(i, ctx->push_label((char *)"", 0));
sp->add_instr(i);
}
| MASTER_SSL_CIPHER_SYM EQ TEXT_STRING_sys
sp_proc_stmts1
{
Lex->mi.ssl_cipher= $3.str;
sp_head *sp= Lex->sphead;
sp_pcontext *ctx= Lex->spcont;
uint ip= sp->instructions();
sp_instr_jump *i = new sp_instr_jump(ip, ctx);
sp->add_instr(i);
sp->backpatch(ctx->pop_label());
sp->push_backpatch(i, ctx->push_label((char *)"", 0));
}
| MASTER_SSL_KEY_SYM EQ TEXT_STRING_sys
sp_elseifs
{
Lex->mi.ssl_key= $3.str;
LEX *lex= Lex;
lex->sphead->backpatch(lex->spcont->pop_label());
}
|
master_file_def
;
master_file_def:
MASTER_LOG_FILE_SYM EQ TEXT_STRING_sys
sp_elseifs:
/* Empty */
| ELSEIF_SYM sp_if
| ELSE sp_proc_stmts1
;
sp_case:
expr THEN_SYM
{
Lex->mi.log_file_name = $3.str;
LEX *lex= Lex;
sp_head *sp= lex->sphead;
sp_pcontext *ctx= Lex->spcont;
uint ip= sp->instructions();
sp_instr_jump_if_not *i;
if (! sp->m_simple_case)
i= new sp_instr_jump_if_not(ip, ctx, $1);
else
{ /* Simple case: <caseval> = <whenval> */
LEX_STRING ivar;
ivar.str= (char *)"_tmp_";
ivar.length= 5;
Item *var= (Item*) new Item_splocal(ivar,
ctx->current_pvars()-1);
Item *expr= new Item_func_eq(var, $1);
i= new sp_instr_jump_if_not(ip, ctx, expr);
lex->variables_used= 1;
}
| MASTER_LOG_POS_SYM EQ ulonglong_num
sp->push_backpatch(i, ctx->push_label((char *)"", 0));
i->tables= lex->query_tables;
lex->query_tables= 0;
sp->add_instr(i);
}
sp_proc_stmts1
{
Lex->mi.pos = $3;
/*
If the user specified a value < BIN_LOG_HEADER_SIZE, adjust it
instead of causing subsequent errors.
We need to do it in this file, because only there we know that
MASTER_LOG_POS has been explicitely specified. On the contrary
in change_master() (sql_repl.cc) we cannot distinguish between 0
(MASTER_LOG_POS explicitely specified as 0) and 0 (unspecified),
whereas we want to distinguish (specified 0 means "read the binlog
from 0" (4 in fact), unspecified means "don't change the position
(keep the preceding value)").
*/
Lex->mi.pos = max(BIN_LOG_HEADER_SIZE, Lex->mi.pos);
sp_head *sp= Lex->sphead;
sp_pcontext *ctx= Lex->spcont;
uint ip= sp->instructions();
sp_instr_jump *i = new sp_instr_jump(ip, ctx);
sp->add_instr(i);
sp->backpatch(ctx->pop_label());
sp->push_backpatch(i, ctx->push_label((char *)"", 0));
}
| RELAY_LOG_FILE_SYM EQ TEXT_STRING_sys
sp_whens
{
Lex->mi.relay_log_name = $3.str;
LEX *lex= Lex;
lex->sphead->backpatch(lex->spcont->pop_label());
}
| RELAY_LOG_POS_SYM EQ ULONG_NUM
;
sp_whens:
/* Empty */
{
Lex->mi.relay_log_pos = $3;
/* Adjust if < BIN_LOG_HEADER_SIZE (same comment as Lex->mi.pos) */
Lex->mi.relay_log_pos = max(BIN_LOG_HEADER_SIZE, Lex->mi.relay_log_pos);
sp_head *sp= Lex->sphead;
uint ip= sp->instructions();
sp_instr_error *i= new sp_instr_error(ip, Lex->spcont,
ER_SP_CASE_NOT_FOUND);
sp->add_instr(i);
}
| ELSE sp_proc_stmts1 {}
| WHEN_SYM sp_case {}
;
/* create a table */
sp_labeled_control:
IDENT ':'
{
LEX *lex= Lex;
sp_pcontext *ctx= lex->spcont;
sp_label_t *lab= ctx->find_label($1.str);
create:
CREATE opt_table_options TABLE_SYM opt_if_not_exists table_ident
if (lab)
{
THD *thd= YYTHD;
LEX *lex=Lex;
lex->sql_command= SQLCOM_CREATE_TABLE;
if (!lex->select_lex.add_table_to_list(thd, $5, NULL,
TL_OPTION_UPDATING,
(using_update_log ?
TL_READ_NO_INSERT:
TL_READ)))
net_printf(YYTHD, ER_SP_LABEL_REDEFINE, $1.str);
YYABORT;
lex->create_list.empty();
lex->key_list.empty();
lex->col_list.empty();
lex->change=NullS;
bzero((char*) &lex->create_info,sizeof(lex->create_info));
lex->create_info.options=$2 | $4;
lex->create_info.db_type= (enum db_type) lex->thd->variables.table_type;
lex->create_info.default_table_charset= NULL;
lex->name=0;
}
create2
{ Lex->current_select= &Lex->select_lex; }
| CREATE opt_unique_or_fulltext INDEX_SYM ident key_alg ON table_ident
else
{
LEX *lex=Lex;
lex->sql_command= SQLCOM_CREATE_INDEX;
if (!lex->current_select->add_table_to_list(lex->thd, $7, NULL,
TL_OPTION_UPDATING))
lab= lex->spcont->push_label($1.str,
lex->sphead->instructions());
lab->type= SP_LAB_ITER;
}
}
sp_unlabeled_control sp_opt_label
{
LEX *lex= Lex;
if ($5.str)
{
sp_label_t *lab= lex->spcont->find_label($5.str);
if (!lab ||
my_strcasecmp(system_charset_info, $5.str, lab->name) != 0)
{
net_printf(YYTHD, ER_SP_LABEL_MISMATCH, $5.str);
YYABORT;
lex->create_list.empty();
lex->key_list.empty();
lex->col_list.empty();
lex->change=NullS;
}
'(' key_list ')'
}
lex->sphead->backpatch(lex->spcont->pop_label());
}
;
sp_opt_label:
/* Empty */
{ $$.str= NULL; $$.length= 0; }
| IDENT
{ $$= $1; }
;
sp_unlabeled_control:
BEGIN_SYM
{ /* QQ This is just a dummy for grouping declarations and statements
together. No [[NOT] ATOMIC] yet, and we need to figure out how
make it coexist with the existing BEGIN COMMIT/ROLLBACK. */
LEX *lex= Lex;
sp_label_t *lab= lex->spcont->last_label();
lab->type= SP_LAB_BEGIN;
lex->spcont= lex->spcont->push_context();
}
sp_decls
sp_proc_stmts
END
{
LEX *lex=Lex;
LEX *lex= Lex;
sp_head *sp= lex->sphead;
sp_pcontext *ctx= lex->spcont;
lex->key_list.push_back(new Key($2,$4.str, $5, 0, lex->col_list));
lex->col_list.empty();
sp->backpatch(ctx->last_label()); /* We always have a label */
if ($3.hndlrs)
sp->add_instr(new sp_instr_hpop(sp->instructions(), ctx,
$3.hndlrs));
if ($3.curs)
sp->add_instr(new sp_instr_cpop(sp->instructions(), ctx,
$3.curs));
lex->spcont= ctx->pop_context();
}
| CREATE DATABASE opt_if_not_exists ident
| LOOP_SYM
sp_proc_stmts1 END LOOP_SYM
{
Lex->create_info.default_table_charset= NULL;
Lex->create_info.used_fields= 0;
LEX *lex= Lex;
uint ip= lex->sphead->instructions();
sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip);
lex->sphead->add_instr(i);
}
opt_create_database_options
| WHILE_SYM expr DO_SYM
{
LEX *lex=Lex;
lex->sql_command=SQLCOM_CREATE_DB;
lex->name=$4.str;
lex->create_info.options=$3;
LEX *lex= Lex;
sp_head *sp= lex->sphead;
uint ip= sp->instructions();
sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont,
$2);
/* Jumping forward */
sp->push_backpatch(i, lex->spcont->last_label());
i->tables= lex->query_tables;
lex->query_tables= 0;
sp->add_instr(i);
}
| CREATE udf_func_type UDF_SYM IDENT_sys
sp_proc_stmts1 END WHILE_SYM
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_CREATE_FUNCTION;
lex->udf.name = $4;
lex->udf.type= $2;
LEX *lex= Lex;
uint ip= lex->sphead->instructions();
sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip);
lex->sphead->add_instr(i);
}
UDF_RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING_sys
| REPEAT_SYM sp_proc_stmts1 UNTIL_SYM expr END REPEAT_SYM
{
LEX *lex=Lex;
lex->udf.returns=(Item_result) $7;
lex->udf.dl=$9.str;
LEX *lex= Lex;
uint ip= lex->sphead->instructions();
sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont,
$4, lab->ip);
i->tables= lex->query_tables;
lex->query_tables= 0;
lex->sphead->add_instr(i);
}
;
trg_action_time:
BEFORE_SYM
{ Lex->trg_chistics.action_time= TRG_ACTION_BEFORE; }
| AFTER_SYM
{ Lex->trg_chistics.action_time= TRG_ACTION_AFTER; }
;
trg_event:
INSERT
{ Lex->trg_chistics.event= TRG_EVENT_INSERT; }
| UPDATE_SYM
{ Lex->trg_chistics.event= TRG_EVENT_UPDATE; }
| DELETE_SYM
{ Lex->trg_chistics.event= TRG_EVENT_DELETE; }
;
create2:
'(' create2a {}
| opt_create_table_options create3 {}
......@@ -1117,6 +2440,10 @@ create_select:
lex->sql_command= SQLCOM_INSERT_SELECT;
else if (lex->sql_command == SQLCOM_REPLACE)
lex->sql_command= SQLCOM_REPLACE_SELECT;
/*
The following work only with the local list, the global list
is created correctly in this case
*/
lex->current_select->table_list.save_and_clear(&lex->save_list);
mysql_init_select(lex);
lex->current_select->parsing_place= SELECT_LIST;
......@@ -1126,7 +2453,13 @@ create_select:
Select->parsing_place= NO_MATTER;
}
opt_select_from
{ Lex->current_select->table_list.push_front(&Lex->save_list); }
{
/*
The following work only with the local list, the global list
is created correctly in this case
*/
Lex->current_select->table_list.push_front(&Lex->save_list);
}
;
opt_as:
......@@ -1174,19 +2507,19 @@ create_table_options:
| create_table_option ',' create_table_options;
create_table_option:
ENGINE_SYM opt_equal storage_engines { Lex->create_info.db_type= $3; }
| TYPE_SYM opt_equal storage_engines { Lex->create_info.db_type= $3; WARN_DEPRECATED("TYPE=storage_engine","ENGINE=storage_engine"); }
ENGINE_SYM opt_equal storage_engines { Lex->create_info.db_type= $3; Lex->create_info.used_fields|= HA_CREATE_USED_ENGINE; }
| TYPE_SYM opt_equal storage_engines { Lex->create_info.db_type= $3; WARN_DEPRECATED("TYPE=storage_engine","ENGINE=storage_engine"); Lex->create_info.used_fields|= HA_CREATE_USED_ENGINE; }
| MAX_ROWS opt_equal ulonglong_num { Lex->create_info.max_rows= $3; Lex->create_info.used_fields|= HA_CREATE_USED_MAX_ROWS;}
| MIN_ROWS opt_equal ulonglong_num { Lex->create_info.min_rows= $3; Lex->create_info.used_fields|= HA_CREATE_USED_MIN_ROWS;}
| AVG_ROW_LENGTH opt_equal ULONG_NUM { Lex->create_info.avg_row_length=$3; Lex->create_info.used_fields|= HA_CREATE_USED_AVG_ROW_LENGTH;}
| PASSWORD opt_equal TEXT_STRING_sys { Lex->create_info.password=$3.str; }
| COMMENT_SYM opt_equal TEXT_STRING_sys { Lex->create_info.comment=$3.str; }
| PASSWORD opt_equal TEXT_STRING_sys { Lex->create_info.password=$3.str; Lex->create_info.used_fields|= HA_CREATE_USED_PASSWORD; }
| COMMENT_SYM opt_equal TEXT_STRING_sys { Lex->create_info.comment=$3.str; Lex->create_info.used_fields|= HA_CREATE_USED_COMMENT; }
| AUTO_INC opt_equal ulonglong_num { Lex->create_info.auto_increment_value=$3; Lex->create_info.used_fields|= HA_CREATE_USED_AUTO;}
| PACK_KEYS_SYM opt_equal ULONG_NUM { Lex->create_info.table_options|= $3 ? HA_OPTION_PACK_KEYS : HA_OPTION_NO_PACK_KEYS; Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS;}
| PACK_KEYS_SYM opt_equal DEFAULT { Lex->create_info.table_options&= ~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS); Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS;}
| CHECKSUM_SYM opt_equal ULONG_NUM { Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM; }
| DELAY_KEY_WRITE_SYM opt_equal ULONG_NUM { Lex->create_info.table_options|= $3 ? HA_OPTION_DELAY_KEY_WRITE : HA_OPTION_NO_DELAY_KEY_WRITE; }
| ROW_FORMAT_SYM opt_equal row_types { Lex->create_info.row_type= $3; }
| CHECKSUM_SYM opt_equal ULONG_NUM { Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM; Lex->create_info.used_fields|= HA_CREATE_USED_CHECKSUM; }
| DELAY_KEY_WRITE_SYM opt_equal ULONG_NUM { Lex->create_info.table_options|= $3 ? HA_OPTION_DELAY_KEY_WRITE : HA_OPTION_NO_DELAY_KEY_WRITE; Lex->create_info.used_fields|= HA_CREATE_USED_DELAY_KEY_WRITE; }
| ROW_FORMAT_SYM opt_equal row_types { Lex->create_info.row_type= $3; Lex->create_info.used_fields|= HA_CREATE_USED_ROW_FORMAT; }
| RAID_TYPE opt_equal raid_types { Lex->create_info.raid_type= $3; Lex->create_info.used_fields|= HA_CREATE_USED_RAID;}
| RAID_CHUNKS opt_equal ULONG_NUM { Lex->create_info.raid_chunks= $3; Lex->create_info.used_fields|= HA_CREATE_USED_RAID;}
| RAID_CHUNKSIZE opt_equal ULONG_NUM { Lex->create_info.raid_chunksize= $3*RAID_BLOCK_SIZE; Lex->create_info.used_fields|= HA_CREATE_USED_RAID;}
......@@ -1197,18 +2530,20 @@ create_table_option:
TABLE_LIST *table_list= lex->select_lex.get_table_list();
lex->create_info.merge_list= lex->select_lex.table_list;
lex->create_info.merge_list.elements--;
lex->create_info.merge_list.first= (byte*) (table_list->next);
lex->create_info.merge_list.first=
(byte*) (table_list->next_local);
lex->select_lex.table_list.elements=1;
lex->select_lex.table_list.next= (byte**) &(table_list->next);
table_list->next=0;
lex->select_lex.table_list.next=
(byte**) &(table_list->next_local);
table_list->next_local= 0;
lex->create_info.used_fields|= HA_CREATE_USED_UNION;
}
| default_charset
| default_collation
| INSERT_METHOD opt_equal merge_insert_types { Lex->create_info.merge_insert_method= $3; Lex->create_info.used_fields|= HA_CREATE_USED_INSERT_METHOD;}
| DATA_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys
{ Lex->create_info.data_file_name= $4.str; }
| INDEX_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys { Lex->create_info.index_file_name= $4.str; };
| DATA_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys { Lex->create_info.data_file_name= $4.str; Lex->create_info.used_fields|= HA_CREATE_USED_DATADIR; }
| INDEX_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys { Lex->create_info.index_file_name= $4.str; Lex->create_info.used_fields|= HA_CREATE_USED_INDEXDIR; }
;
default_charset:
opt_default charset opt_equal charset_name_or_default
......@@ -1823,14 +3158,12 @@ key_part:
| ident '(' NUM ')'
{
int key_part_len= atoi($3.str);
#if MYSQL_VERSION_ID < 50000
if (!key_part_len)
{
my_printf_error(ER_UNKNOWN_ERROR,
"Key part '%s' length cannot be 0",
MYF(0), $1.str);
}
#endif
$$=new key_part_spec($1.str,(uint) key_part_len);
};
......@@ -1870,7 +3203,6 @@ alter:
lex->create_info.default_table_charset= NULL;
lex->create_info.row_type= ROW_TYPE_NOT_USED;
lex->alter_info.reset();
lex->alter_info.is_simple= 1;
lex->alter_info.flags= 0;
}
alter_list
......@@ -1885,8 +3217,48 @@ alter:
LEX *lex=Lex;
lex->sql_command=SQLCOM_ALTER_DB;
lex->name=$3.str;
};
}
| ALTER PROCEDURE sp_name
{
LEX *lex= Lex;
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
}
sp_a_chistics
{
THD *thd= YYTHD;
LEX *lex=Lex;
lex->sql_command= SQLCOM_ALTER_PROCEDURE;
lex->spname= $3;
}
| ALTER FUNCTION_SYM sp_name
{
LEX *lex= Lex;
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
}
sp_a_chistics
{
THD *thd= YYTHD;
LEX *lex=Lex;
lex->sql_command= SQLCOM_ALTER_FUNCTION;
lex->spname= $3;
}
| ALTER algorithm VIEW_SYM table_ident
{
THD *thd= YYTHD;
LEX *lex= thd->lex;
lex->sql_command= SQLCOM_CREATE_VIEW;
lex->create_view_mode= VIEW_ALTER;
lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE;
/* first table in list is target VIEW name */
lex->select_lex.add_table_to_list(thd, $4, NULL, 0);
}
opt_view_list AS select_init check_option
{}
;
alter_list:
| DISCARD TABLESPACE { Lex->alter_info.tablespace_op= DISCARD_TABLESPACE; }
......@@ -1903,19 +3275,19 @@ add_column:
};
alter_list_item:
add_column column_def opt_place { Lex->alter_info.is_simple= 0; }
add_column column_def opt_place { }
| ADD key_def
{
LEX *lex=Lex;
lex->alter_info.is_simple= 0;
lex->alter_info.flags|= ALTER_ADD_INDEX;
Lex->alter_info.flags|= ALTER_ADD_INDEX;
}
| add_column '(' field_list ')'
{
Lex->alter_info.flags|= ALTER_ADD_COLUMN | ALTER_ADD_INDEX;
}
| add_column '(' field_list ')' { Lex->alter_info.is_simple= 0; }
| CHANGE opt_column field_ident
{
LEX *lex=Lex;
lex->change= $3.str;
lex->alter_info.is_simple= 0;
lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
}
field_spec opt_place
......@@ -1926,7 +3298,6 @@ alter_list_item:
lex->default_value= lex->on_update_value= 0;
lex->comment=0;
lex->charset= NULL;
lex->alter_info.is_simple= 0;
lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
}
type opt_attribute
......@@ -1947,16 +3318,17 @@ alter_list_item:
LEX *lex=Lex;
lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::COLUMN,
$3.str));
lex->alter_info.is_simple= 0;
lex->alter_info.flags|= ALTER_DROP_COLUMN;
}
| DROP FOREIGN KEY_SYM opt_ident { Lex->alter_info.is_simple= 0; }
| DROP FOREIGN KEY_SYM opt_ident
{
Lex->alter_info.flags|= ALTER_DROP_INDEX;
}
| DROP PRIMARY_SYM KEY_SYM
{
LEX *lex=Lex;
lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::KEY,
primary_key_name));
lex->alter_info.is_simple= 0;
lex->alter_info.flags|= ALTER_DROP_INDEX;
}
| DROP key_or_index field_ident
......@@ -1964,25 +3336,32 @@ alter_list_item:
LEX *lex=Lex;
lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::KEY,
$3.str));
lex->alter_info.is_simple= 0;
lex->alter_info.flags|= ALTER_DROP_INDEX;
}
| DISABLE_SYM KEYS { Lex->alter_info.keys_onoff= DISABLE; }
| ENABLE_SYM KEYS { Lex->alter_info.keys_onoff= ENABLE; }
| DISABLE_SYM KEYS
{
LEX *lex=Lex;
lex->alter_info.keys_onoff= DISABLE;
lex->alter_info.flags|= ALTER_KEYS_ONOFF;
}
| ENABLE_SYM KEYS
{
LEX *lex=Lex;
lex->alter_info.keys_onoff= ENABLE;
lex->alter_info.flags|= ALTER_KEYS_ONOFF;
}
| ALTER opt_column field_ident SET DEFAULT signed_literal
{
LEX *lex=Lex;
lex->alter_info.alter_list.push_back(new Alter_column($3.str,$6));
lex->alter_info.is_simple= 0;
lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
lex->alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT;
}
| ALTER opt_column field_ident DROP DEFAULT
{
LEX *lex=Lex;
lex->alter_info.alter_list.push_back(new Alter_column($3.str,
(Item*) 0));
lex->alter_info.is_simple= 0;
lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
lex->alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT;
}
| RENAME opt_to table_ident
{
......@@ -2016,18 +3395,16 @@ alter_list_item:
lex->create_info.default_table_charset= $5;
lex->create_info.used_fields|= (HA_CREATE_USED_CHARSET |
HA_CREATE_USED_DEFAULT_CHARSET);
lex->alter_info.is_simple= 0;
lex->alter_info.flags|= ALTER_CONVERT;
}
| create_table_options_space_separated
{
LEX *lex=Lex;
lex->alter_info.is_simple= 0;
lex->alter_info.flags|= ALTER_OPTIONS;
}
| order_clause
{
LEX *lex=Lex;
lex->alter_info.is_simple= 0;
lex->alter_info.flags|= ALTER_ORDER;
};
......@@ -2040,9 +3417,10 @@ opt_ignore:
| IGNORE_SYM { Lex->duplicates=DUP_IGNORE; };
opt_restrict:
/* empty */ {}
| RESTRICT {}
| CASCADE {};
/* empty */ { Lex->drop_mode= DROP_DEFAULT; }
| RESTRICT { Lex->drop_mode= DROP_RESTRICT; }
| CASCADE { Lex->drop_mode= DROP_CASCADE; }
;
opt_place:
/* empty */ {}
......@@ -2542,8 +3920,12 @@ select_item:
YYABORT;
if ($4.str)
$2->set_name($4.str,$4.length,system_charset_info);
else if (!$2->name)
$2->set_name($1,(uint) ($3 - $1), YYTHD->charset());
else if (!$2->name) {
char *str = $1;
if (str[-1] == '`')
str--;
$2->set_name(str,(uint) ($3 - str), YYTHD->charset());
}
};
remember_name:
......@@ -2767,12 +4149,16 @@ simple_expr:
| '@' ident_or_text SET_VAR expr
{
$$= new Item_func_set_user_var($2,$4);
Lex->uncacheable(UNCACHEABLE_RAND);
LEX *lex= Lex;
lex->uncacheable(UNCACHEABLE_RAND);
lex->variables_used= 1;
}
| '@' ident_or_text
{
$$= new Item_func_get_user_var($2);
Lex->uncacheable(UNCACHEABLE_RAND);
LEX *lex= Lex;
lex->uncacheable(UNCACHEABLE_RAND);
lex->variables_used= 1;
}
| '@' '@' opt_var_ident_type ident_or_text opt_component
{
......@@ -2784,6 +4170,7 @@ simple_expr:
}
if (!($$= get_system_var(YYTHD, (enum_var_type) $3, $4, $5)))
YYABORT;
Lex->variables_used= 1;
}
| sum_expr
| '+' expr %prec NEG { $$= $2; }
......@@ -2888,6 +4275,8 @@ simple_expr:
{ $$= new Item_date_add_interval($3, $5, INTERVAL_DAY, 0);}
| ADDDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')'
{ $$= new Item_date_add_interval($3, $6, $7, 0); }
| REPEAT_SYM '(' expr ',' expr ')'
{ $$= new Item_func_repeat($3,$5); }
| ATAN '(' expr ')'
{ $$= new Item_func_atan($3); }
| ATAN '(' expr ',' expr ')'
......@@ -2904,6 +4293,8 @@ simple_expr:
{ $$= new Item_func_concat(* $3); }
| CONCAT_WS '(' expr ',' expr_list ')'
{ $5->push_front($3); $$= new Item_func_concat_ws(*$5); }
| CONTAINS_SYM '(' expr ',' expr ')'
{ $$= create_func_contains($3, $5); }
| CONVERT_TZ_SYM '(' expr ',' expr ',' expr ')'
{
Lex->time_zone_tables_used= &fake_time_zone_tables_list;
......@@ -3059,6 +4450,8 @@ simple_expr:
{ $$= new Item_func_old_password($3); }
| POSITION_SYM '(' no_in_expr IN_SYM expr ')'
{ $$ = new Item_func_locate($5,$3); }
| QUARTER_SYM '(' expr ')'
{ $$ = new Item_func_quarter($3); }
| RAND '(' expr ')'
{ $$= new Item_func_rand($3); Lex->uncacheable(UNCACHEABLE_RAND);}
| RAND '(' ')'
......@@ -3070,6 +4463,11 @@ simple_expr:
| ROUND '(' expr ')'
{ $$= new Item_func_round($3, new Item_int((char*)"0",0,1),0); }
| ROUND '(' expr ',' expr ')' { $$= new Item_func_round($3,$5,0); }
| ROW_COUNT_SYM '(' ')'
{
$$= new Item_func_row_count();
Lex->safe_to_cache_query= 0;
}
| SUBDATE_SYM '(' expr ',' expr ')'
{ $$= new Item_date_add_interval($3, $5, INTERVAL_DAY, 1);}
| SUBDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')'
......@@ -3092,6 +4490,10 @@ simple_expr:
{ $$= new Item_datetime_typecast($3); }
| TIMESTAMP '(' expr ',' expr ')'
{ $$= new Item_func_add_time($3, $5, 1, 0); }
| TIMESTAMP_ADD '(' interval_time_st ',' expr ',' expr ')'
{ $$= new Item_date_add_interval($7,$5,$3,0); }
| TIMESTAMP_DIFF '(' interval_time_st ',' expr ',' expr ')'
{ $$= new Item_func_timestamp_diff($5,$7,$3); }
| TRIM '(' expr ')'
{ $$= new Item_func_trim($3); }
| TRIM '(' LEADING expr FROM expr ')'
......@@ -3112,47 +4514,89 @@ simple_expr:
{ $$= new Item_func_round($3,$5,1); }
| TRUE_SYM
{ $$= new Item_int((char*) "TRUE",1,1); }
| UDA_CHAR_SUM '(' udf_expr_list ')'
| ident '.' ident '(' udf_expr_list ')'
{
LEX *lex= Lex;
sp_name *name= new sp_name($1, $3);
name->init_qname(YYTHD);
sp_add_fun_to_lex(Lex, name);
if ($5)
$$= new Item_func_sp(name, *$5);
else
$$= new Item_func_sp(name);
}
| IDENT_sys '(' udf_expr_list ')'
{
#ifdef HAVE_DLOPEN
udf_func *udf;
if (using_udf_functions && (udf=find_udf($1.str, $1.length)))
{
switch (udf->returns) {
case STRING_RESULT:
if (udf->type == UDFTYPE_FUNCTION)
{
if ($3 != NULL)
$$ = new Item_func_udf_str(udf, *$3);
else
$$ = new Item_func_udf_str(udf);
}
else
{
if ($3 != NULL)
$$ = new Item_sum_udf_str($1, *$3);
$$ = new Item_sum_udf_str(udf, *$3);
else
$$ = new Item_sum_udf_str($1);
$$ = new Item_sum_udf_str(udf);
}
| UDA_FLOAT_SUM '(' udf_expr_list ')'
break;
case REAL_RESULT:
if (udf->type == UDFTYPE_FUNCTION)
{
if ($3 != NULL)
$$ = new Item_sum_udf_float($1, *$3);
$$ = new Item_func_udf_float(udf, *$3);
else
$$ = new Item_sum_udf_float($1);
$$ = new Item_func_udf_float(udf);
}
| UDA_INT_SUM '(' udf_expr_list ')'
else
{
if ($3 != NULL)
$$ = new Item_sum_udf_int($1, *$3);
$$ = new Item_sum_udf_float(udf, *$3);
else
$$ = new Item_sum_udf_int($1);
$$ = new Item_sum_udf_float(udf);
}
| UDF_CHAR_FUNC '(' udf_expr_list ')'
break;
case INT_RESULT:
if (udf->type == UDFTYPE_FUNCTION)
{
if ($3 != NULL)
$$ = new Item_func_udf_str($1, *$3);
$$ = new Item_func_udf_int(udf, *$3);
else
$$ = new Item_func_udf_str($1);
$$ = new Item_func_udf_int(udf);
}
| UDF_FLOAT_FUNC '(' udf_expr_list ')'
else
{
if ($3 != NULL)
$$ = new Item_func_udf_float($1, *$3);
$$ = new Item_sum_udf_int(udf, *$3);
else
$$ = new Item_func_udf_float($1);
$$ = new Item_sum_udf_int(udf);
}
break;
default:
YYABORT;
}
}
| UDF_INT_FUNC '(' udf_expr_list ')'
else
#endif /* HAVE_DLOPEN */
{
if ($3 != NULL)
$$ = new Item_func_udf_int($1, *$3);
sp_name *name= sp_name_current_db_new(YYTHD, $1);
sp_add_fun_to_lex(Lex, name);
if ($3)
$$= new Item_func_sp(name, *$3);
else
$$ = new Item_func_udf_int($1);
$$= new Item_func_sp(name);
}
}
| UNIQUE_USERS '(' text_literal ',' NUM ',' NUM ',' expr_list ')'
{
......@@ -3262,7 +4706,36 @@ fulltext_options:
udf_expr_list:
/* empty */ { $$= NULL; }
| expr_list { $$= $1;};
| udf_expr_list2 { $$= $1;}
;
udf_expr_list2:
{ Select->expr_list.push_front(new List<Item>); }
udf_expr_list3
{ $$= Select->expr_list.pop(); }
;
udf_expr_list3:
udf_expr
{
Select->expr_list.head()->push_back($1);
}
| udf_expr_list3 ',' udf_expr
{
Select->expr_list.head()->push_back($3);
}
;
udf_expr:
remember_name expr remember_end select_alias
{
if ($4.str)
$2->set_name($4.str,$4.length,system_charset_info);
else
$2->set_name($1,(uint) ($3 - $1), YYTHD->charset());
$$= $2;
}
;
sum_expr:
AVG_SYM '(' in_sum_expr ')'
......@@ -3287,14 +4760,25 @@ sum_expr:
{ $$= new Item_sum_unique_users($3,atoi($5.str),atoi($7.str),$9); }
| MIN_SYM '(' in_sum_expr ')'
{ $$=new Item_sum_min($3); }
/*
According to ANSI SQL, DISTINCT is allowed and has
no sence inside MIN and MAX grouping functions; so MIN|MAX(DISTINCT ...)
is processed like an ordinary MIN | MAX()
*/
| MIN_SYM '(' DISTINCT in_sum_expr ')'
{ $$=new Item_sum_min($4); }
| MAX_SYM '(' in_sum_expr ')'
{ $$=new Item_sum_max($3); }
| MAX_SYM '(' DISTINCT in_sum_expr ')'
{ $$=new Item_sum_max($4); }
| STD_SYM '(' in_sum_expr ')'
{ $$=new Item_sum_std($3); }
| VARIANCE_SYM '(' in_sum_expr ')'
{ $$=new Item_sum_variance($3); }
| SUM_SYM '(' in_sum_expr ')'
{ $$=new Item_sum_sum($3); }
| SUM_SYM '(' DISTINCT in_sum_expr ')'
{ $$=new Item_sum_sum_distinct($4); }
| GROUP_CONCAT_SYM '(' opt_distinct
{ Select->in_sum_expr++; }
expr_list opt_gorder_clause
......@@ -3408,59 +4892,80 @@ when_list2:
sel->when_list.head()->push_back($5);
};
join_table_list:
'(' join_table_list ')' { $$=$2; }
table_ref:
table_factor { $$=$1; }
| join_table { $$=$1; }
| join_table_list ',' join_table_list { $$=$3; }
| join_table_list normal_join join_table_list { $$=$3; }
| join_table_list STRAIGHT_JOIN join_table_list
{ $$=$3 ; $1->next->straight=1; }
| join_table_list normal_join join_table_list ON expr
{
LEX *lex= Lex;
if (!($$= lex->current_select->nest_last_join(lex->thd)))
YYABORT;
}
;
join_table_list:
table_ref { $$=$1; }
| join_table_list ',' table_ref { $$=$3; }
;
join_table:
table_ref normal_join table_ref { $$=$3; }
| table_ref STRAIGHT_JOIN table_factor
{ $3->straight=1; $$=$3 ; }
| table_ref normal_join table_ref ON expr
{ add_join_on($3,$5); $$=$3; }
| join_table_list normal_join join_table_list
| table_ref normal_join table_ref
USING
{
SELECT_LEX *sel= Select;
sel->db1=$1->db; sel->table1=$1->alias;
sel->db2=$3->db; sel->table2=$3->alias;
sel->save_names_for_using_list($1, $3);
}
'(' using_list ')'
{ add_join_on($3,$7); $$=$3; }
| join_table_list LEFT opt_outer JOIN_SYM join_table_list ON expr
| table_ref LEFT opt_outer JOIN_SYM table_ref ON expr
{ add_join_on($5,$7); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; }
| join_table_list LEFT opt_outer JOIN_SYM join_table_list
| table_ref LEFT opt_outer JOIN_SYM table_factor
{
SELECT_LEX *sel= Select;
sel->db1=$1->db; sel->table1=$1->alias;
sel->db2=$5->db; sel->table2=$5->alias;
sel->save_names_for_using_list($1, $5);
}
USING '(' using_list ')'
{ add_join_on($5,$9); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; }
| join_table_list NATURAL LEFT opt_outer JOIN_SYM join_table_list
| table_ref NATURAL LEFT opt_outer JOIN_SYM table_factor
{
add_join_natural($1,$1->next);
$1->next->outer_join|=JOIN_TYPE_LEFT;
add_join_natural($1,$6);
$6->outer_join|=JOIN_TYPE_LEFT;
$$=$6;
}
| join_table_list RIGHT opt_outer JOIN_SYM join_table_list ON expr
{ add_join_on($1,$7); $1->outer_join|=JOIN_TYPE_RIGHT; $$=$5; }
| join_table_list RIGHT opt_outer JOIN_SYM join_table_list
| table_ref RIGHT opt_outer JOIN_SYM table_ref ON expr
{
LEX *lex= Lex;
if (!($$= lex->current_select->convert_right_join()))
YYABORT;
add_join_on($$, $7);
}
| table_ref RIGHT opt_outer JOIN_SYM table_factor
{
SELECT_LEX *sel= Select;
sel->db1=$1->db; sel->table1=$1->alias;
sel->db2=$5->db; sel->table2=$5->alias;
sel->save_names_for_using_list($1, $5);
}
USING '(' using_list ')'
{ add_join_on($1,$9); $1->outer_join|=JOIN_TYPE_RIGHT; $$=$5; }
| join_table_list NATURAL RIGHT opt_outer JOIN_SYM join_table_list
{
add_join_natural($1->next,$1);
$1->outer_join|=JOIN_TYPE_RIGHT;
$$=$6;
LEX *lex= Lex;
if (!($$= lex->current_select->convert_right_join()))
YYABORT;
add_join_on($$, $9);
}
| table_ref NATURAL RIGHT opt_outer JOIN_SYM table_factor
{
add_join_natural($6,$1);
LEX *lex= Lex;
if (!($$= lex->current_select->convert_right_join()))
YYABORT;
}
| join_table_list NATURAL JOIN_SYM join_table_list
{ add_join_natural($1,$1->next); $$=$4; };
| table_ref NATURAL JOIN_SYM table_factor
{ add_join_natural($1,$4); $$=$4; };
normal_join:
JOIN_SYM {}
......@@ -3468,7 +4973,7 @@ normal_join:
| CROSS JOIN_SYM {}
;
join_table:
table_factor:
{
SELECT_LEX *sel= Select;
sel->use_index_ptr=sel->ignore_index_ptr=0;
......@@ -3484,8 +4989,21 @@ join_table:
sel->get_use_index(),
sel->get_ignore_index())))
YYABORT;
sel->add_joined_table($$);
}
| '('
{
LEX *lex= Lex;
if (lex->current_select->init_nested_join(lex->thd))
YYABORT;
}
join_table_list ')'
{
LEX *lex= Lex;
if (!($$= lex->current_select->end_nested_join(lex->thd)))
YYABORT;
}
| '{' ident join_table LEFT OUTER JOIN_SYM join_table ON expr '}'
| '{' ident table_ref LEFT OUTER JOIN_SYM table_ref ON expr '}'
{ add_join_on($7,$9); $7->outer_join|=JOIN_TYPE_LEFT; $$=$7; }
| '(' SELECT_SYM select_derived ')' opt_table_alias
{
......@@ -3498,12 +5016,13 @@ join_table:
(List<String> *)0)))
YYABORT;
lex->current_select->add_joined_table($$);
};
select_derived:
{
LEX *lex= Lex;
lex->derived_tables= 1;
lex->derived_tables|= DERIVED_SUBQUERY;
if (((int)lex->sql_command >= (int)SQLCOM_HA_OPEN &&
lex->sql_command <= (int)SQLCOM_HA_READ) ||
lex->sql_command == (int)SQLCOM_KILL)
......@@ -3594,23 +5113,29 @@ using_list:
};
interval:
DAY_HOUR_SYM { $$=INTERVAL_DAY_HOUR; }
interval_time_st {}
| DAY_HOUR_SYM { $$=INTERVAL_DAY_HOUR; }
| DAY_MICROSECOND_SYM { $$=INTERVAL_DAY_MICROSECOND; }
| DAY_MINUTE_SYM { $$=INTERVAL_DAY_MINUTE; }
| DAY_SECOND_SYM { $$=INTERVAL_DAY_SECOND; }
| DAY_SYM { $$=INTERVAL_DAY; }
| HOUR_MICROSECOND_SYM { $$=INTERVAL_HOUR_MICROSECOND; }
| HOUR_MINUTE_SYM { $$=INTERVAL_HOUR_MINUTE; }
| HOUR_SECOND_SYM { $$=INTERVAL_HOUR_SECOND; }
| HOUR_SYM { $$=INTERVAL_HOUR; }
| MICROSECOND_SYM { $$=INTERVAL_MICROSECOND; }
| MINUTE_MICROSECOND_SYM { $$=INTERVAL_MINUTE_MICROSECOND; }
| MINUTE_SECOND_SYM { $$=INTERVAL_MINUTE_SECOND; }
| SECOND_MICROSECOND_SYM { $$=INTERVAL_SECOND_MICROSECOND; }
| YEAR_MONTH_SYM { $$=INTERVAL_YEAR_MONTH; };
interval_time_st:
DAY_SYM { $$=INTERVAL_DAY; }
| WEEK_SYM { $$=INTERVAL_WEEK; }
| HOUR_SYM { $$=INTERVAL_HOUR; }
| FRAC_SECOND_SYM { $$=INTERVAL_MICROSECOND; }
| MINUTE_SYM { $$=INTERVAL_MINUTE; }
| MONTH_SYM { $$=INTERVAL_MONTH; }
| SECOND_MICROSECOND_SYM { $$=INTERVAL_SECOND_MICROSECOND; }
| QUARTER_SYM { $$=INTERVAL_QUARTER; }
| SECOND_SYM { $$=INTERVAL_SECOND; }
| YEAR_MONTH_SYM { $$=INTERVAL_YEAR_MONTH; }
| YEAR_SYM { $$=INTERVAL_YEAR; }
;
......@@ -3672,7 +5197,10 @@ opt_escape:
ESCAPE_SYM simple_expr { $$= $2; }
| /* empty */
{
$$= new Item_string("\\", 1, &my_charset_latin1);
$$= ((YYTHD->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) ?
new Item_string("", 0, &my_charset_latin1) :
new Item_string("\\", 1, &my_charset_latin1));
}
;
......@@ -3885,11 +5413,32 @@ select_var_list:
| select_var_ident {}
;
select_var_ident: '@' ident_or_text
select_var_ident:
'@' ident_or_text
{
LEX *lex=Lex;
if (lex->result && ((select_dumpvar *)lex->result)->var_list.push_back((LEX_STRING*) sql_memdup(&$2,sizeof(LEX_STRING))))
if (lex->result)
((select_dumpvar *)lex->result)->var_list.push_back( new my_var($2,0,0,(enum_field_types)0));
else
YYABORT;
}
| ident_or_text
{
LEX *lex=Lex;
sp_pvar_t *t;
if (!lex->spcont || !(t=lex->spcont->find_pvar(&$1)))
{
net_printf(YYTHD, ER_SP_UNDECLARED_VAR, $1.str);
YYABORT;
}
if (! lex->result)
YYABORT;
else
{
((select_dumpvar *)lex->result)->var_list.push_back( new my_var($1,1,t->offset,t->type));
t->isset= TRUE;
}
}
;
......@@ -3966,11 +5515,29 @@ drop:
lex->drop_if_exists=$3;
lex->name=$4.str;
}
| DROP UDF_SYM IDENT_sys
| DROP FUNCTION_SYM if_exists sp_name
{
LEX *lex=Lex;
if (lex->sphead)
{
net_printf(YYTHD, ER_SP_NO_DROP_SP, "FUNCTION");
YYABORT;
}
lex->sql_command = SQLCOM_DROP_FUNCTION;
lex->udf.name = $3;
lex->drop_if_exists= $3;
lex->spname= $4;
}
| DROP PROCEDURE if_exists sp_name
{
LEX *lex=Lex;
if (lex->sphead)
{
net_printf(YYTHD, ER_SP_NO_DROP_SP, "PROCEDURE");
YYABORT;
}
lex->sql_command = SQLCOM_DROP_PROCEDURE;
lex->drop_if_exists= $3;
lex->spname= $4;
}
| DROP USER
{
......@@ -3980,8 +5547,28 @@ drop:
}
user_list
{}
;
| DROP VIEW_SYM if_exists table_list opt_restrict
{
THD *thd= YYTHD;
LEX *lex= thd->lex;
lex->sql_command= SQLCOM_DROP_VIEW;
lex->drop_if_exists= $3;
}
| DROP TRIGGER_SYM ident '.' ident
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_DROP_TRIGGER;
/* QQ: Could we loosen lock type in certain cases ? */
if (!lex->select_lex.add_table_to_list(YYTHD,
new Table_ident($3),
(LEX_STRING*) 0,
TL_OPTION_UPDATING,
TL_WRITE))
YYABORT;
lex->name_and_length= $5;
}
;
table_list:
table_name
......@@ -4041,7 +5628,6 @@ replace:
}
insert_field_spec
{}
{}
;
insert_lock_option:
......@@ -4107,7 +5693,7 @@ ident_eq_list:
ident_eq_value;
ident_eq_value:
simple_ident equal expr_or_default
simple_ident_nospvar equal expr_or_default
{
LEX *lex=Lex;
if (lex->field_list.push_back($1) ||
......@@ -4173,7 +5759,6 @@ opt_insert_update:
yyerror(ER(ER_SYNTAX_ERROR));
YYABORT;
}
lex->duplicates= DUP_UPDATE;
}
KEY_SYM UPDATE_SYM update_list
;
......@@ -4211,12 +5796,12 @@ update:
;
update_list:
update_list ',' simple_ident equal expr_or_default
update_list ',' simple_ident_nospvar equal expr_or_default
{
if (add_item_to_list(YYTHD, $3) || add_value_to_list(YYTHD, $5))
YYABORT;
}
| simple_ident equal expr_or_default
| simple_ident_nospvar equal expr_or_default
{
if (add_item_to_list(YYTHD, $1) || add_value_to_list(YYTHD, $3))
YYABORT;
......@@ -4321,11 +5906,11 @@ show: SHOW
show_param:
DATABASES wild
{ Lex->sql_command= SQLCOM_SHOW_DATABASES; }
| TABLES opt_db wild
| opt_full TABLES opt_db wild
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_TABLES;
lex->select_lex.db= $2;
lex->select_lex.db= $3;
}
| TABLE_SYM STATUS_SYM opt_db wild
{
......@@ -4411,8 +5996,12 @@ show_param:
{ Lex->sql_command = SQLCOM_SHOW_WARNS;}
| ERRORS opt_limit_clause_init
{ Lex->sql_command = SQLCOM_SHOW_ERRORS;}
| STATUS_SYM wild
{ Lex->sql_command= SQLCOM_SHOW_STATUS; }
| opt_var_type STATUS_SYM wild
{
THD *thd= YYTHD;
thd->lex->sql_command= SQLCOM_SHOW_STATUS;
thd->lex->option_type= (enum_var_type) $1;
}
| INNOBASE_SYM STATUS_SYM
{ Lex->sql_command = SQLCOM_SHOW_INNODB_STATUS; WARN_DEPRECATED("SHOW INNODB STATUS", "SHOW ENGINE INNODB STATUS"); }
| opt_full PROCESSLIST_SYM
......@@ -4469,9 +6058,19 @@ show_param:
}
| CREATE TABLE_SYM table_ident
{
Lex->sql_command = SQLCOM_SHOW_CREATE;
if (!Select->add_table_to_list(YYTHD, $3, NULL,0))
LEX *lex= Lex;
lex->sql_command = SQLCOM_SHOW_CREATE;
if (!lex->select_lex.add_table_to_list(YYTHD, $3, NULL,0))
YYABORT;
lex->only_view= 0;
}
| CREATE VIEW_SYM table_ident
{
LEX *lex= Lex;
lex->sql_command = SQLCOM_SHOW_CREATE;
if (!lex->select_lex.add_table_to_list(YYTHD, $3, NULL, 0))
YYABORT;
lex->only_view= 1;
}
| MASTER_SYM STATUS_SYM
{
......@@ -4480,6 +6079,28 @@ show_param:
| SLAVE STATUS_SYM
{
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
}
| CREATE PROCEDURE sp_name
{
LEX *lex= Lex;
lex->sql_command = SQLCOM_SHOW_CREATE_PROC;
lex->spname= $3;
}
| CREATE FUNCTION_SYM sp_name
{
LEX *lex= Lex;
lex->sql_command = SQLCOM_SHOW_CREATE_FUNC;
lex->spname= $3;
}
| PROCEDURE STATUS_SYM wild
{
Lex->sql_command = SQLCOM_SHOW_STATUS_PROC;
}
| FUNCTION_SYM STATUS_SYM wild
{
Lex->sql_command = SQLCOM_SHOW_STATUS_FUNC;
};
show_engine_param:
......@@ -4670,18 +6291,23 @@ purge_option:
/* kill threads */
kill:
KILL_SYM expr
KILL_SYM kill_option expr
{
LEX *lex=Lex;
if ($2->fix_fields(lex->thd, 0, &$2) || $2->check_cols(1))
if ($3->fix_fields(lex->thd, 0, &$3) || $3->check_cols(1))
{
send_error(lex->thd, ER_SET_CONSTANTS_ONLY);
YYABORT;
}
lex->sql_command=SQLCOM_KILL;
lex->thread_id= (ulong) $2->val_int();
lex->thread_id= (ulong) $3->val_int();
};
kill_option:
/* empty */ { Lex->type= 0; }
| CONNECTION_SYM { Lex->type= 0; }
| QUERY_SYM { Lex->type= ONLY_KILL_QUERY; };
/* change database */
use: USE_SYM ident
......@@ -4892,8 +6518,24 @@ NUM_literal:
NUM { int error; $$ = new Item_int($1.str, (longlong) my_strtoll10($1.str, NULL, &error), $1.length); }
| LONG_NUM { int error; $$ = new Item_int($1.str, (longlong) my_strtoll10($1.str, NULL, &error), $1.length); }
| ULONGLONG_NUM { $$ = new Item_uint($1.str, $1.length); }
| REAL_NUM { $$ = new Item_real($1.str, $1.length); }
| FLOAT_NUM { $$ = new Item_float($1.str, $1.length); }
| REAL_NUM
{
$$= new Item_real($1.str, $1.length);
if (YYTHD->net.report_error)
{
send_error(YYTHD, 0, NullS);
YYABORT;
}
}
| FLOAT_NUM
{
$$ = new Item_float($1.str, $1.length);
if (YYTHD->net.report_error)
{
send_error(YYTHD, 0, NullS);
YYABORT;
}
}
;
/**********************************************************************
......@@ -4901,7 +6543,7 @@ NUM_literal:
**********************************************************************/
insert_ident:
simple_ident { $$=$1; }
simple_ident_nospvar { $$=$1; }
| table_wild { $$=$1; };
table_wild:
......@@ -4923,18 +6565,97 @@ order_ident:
expr { $$=$1; };
simple_ident:
ident
{
sp_pvar_t *spv;
LEX *lex = Lex;
sp_pcontext *spc = lex->spcont;
if (spc && (spv = spc->find_pvar(&$1)))
{ /* We're compiling a stored procedure and found a variable */
$$ = (Item*) new Item_splocal($1, spv->offset);
lex->variables_used= 1;
lex->safe_to_cache_query=0;
}
else
{
SELECT_LEX *sel=Select;
$$= (sel->parsing_place != IN_HAVING ||
sel->get_in_sum_expr() > 0) ?
(Item*) new Item_field(NullS,NullS,$1.str) :
(Item*) new Item_ref(NullS,NullS,$1.str);
}
}
| simple_ident_q { $$= $1; }
;
simple_ident_nospvar:
ident
{
SELECT_LEX *sel=Select;
$$= (sel->parsing_place != IN_HAVING ||
sel->get_in_sum_expr() > 0) ?
(Item*) new Item_field(NullS,NullS,$1.str) :
(Item*) new Item_ref(NullS, NullS, $1.str);
(Item*) new Item_ref(NullS,NullS,$1.str);
}
| ident '.' ident
| simple_ident_q { $$= $1; }
;
simple_ident_q:
ident '.' ident
{
THD *thd= YYTHD;
LEX *lex= thd->lex;
/*
FIXME This will work ok in simple_ident_nospvar case because
we can't meet simple_ident_nospvar in trigger now. But it
should be changed in future.
*/
if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER &&
(!my_strcasecmp(system_charset_info, $1.str, "NEW") ||
!my_strcasecmp(system_charset_info, $1.str, "OLD")))
{
bool new_row= ($1.str[0]=='N' || $1.str[0]=='n');
if (lex->trg_chistics.event == TRG_EVENT_INSERT &&
!new_row)
{
net_printf(YYTHD, ER_TRG_NO_SUCH_ROW_IN_TRG, "OLD",
"on INSERT");
YYABORT;
}
if (lex->trg_chistics.event == TRG_EVENT_DELETE &&
new_row)
{
net_printf(YYTHD, ER_TRG_NO_SUCH_ROW_IN_TRG, "NEW",
"on DELETE");
YYABORT;
}
Item_trigger_field *trg_fld=
new Item_trigger_field(new_row ? Item_trigger_field::NEW_ROW :
Item_trigger_field::OLD_ROW,
$3.str);
if (lex->trg_table &&
trg_fld->setup_field(thd, lex->trg_table,
lex->trg_chistics.event))
{
/*
FIXME. Far from perfect solution. See comment for
"SET NEW.field_name:=..." for more info.
*/
net_printf(YYTHD, ER_BAD_FIELD_ERROR, $3.str,
new_row ? "NEW": "OLD");
YYABORT;
}
$$= (Item *)trg_fld;
}
else
{
SELECT_LEX *sel= lex->current_select;
if (sel->no_table_names_allowed)
{
......@@ -4945,7 +6666,8 @@ simple_ident:
$$= (sel->parsing_place != IN_HAVING ||
sel->get_in_sum_expr() > 0) ?
(Item*) new Item_field(NullS,$1.str,$3.str) :
(Item*) new Item_ref(NullS, $1.str, $3.str);
(Item*) new Item_ref(NullS,$1.str,$3.str);
}
}
| '.' ident '.' ident
{
......@@ -5109,6 +6831,7 @@ keyword:
| AFTER_SYM {}
| AGAINST {}
| AGGREGATE_SYM {}
| ALGORITHM_SYM {}
| ANY_SYM {}
| ASCII_SYM {}
| AUTO_INC {}
......@@ -5124,6 +6847,7 @@ keyword:
| BYTE_SYM {}
| BTREE_SYM {}
| CACHE_SYM {}
| CASCADED {}
| CHANGED {}
| CHARSET {}
| CHECKSUM_SYM {}
......@@ -5137,12 +6861,14 @@ keyword:
| COMPRESSED_SYM {}
| CONCURRENT {}
| CONSISTENT_SYM {}
| CONTAINS_SYM {}
| CUBE_SYM {}
| DATA_SYM {}
| DATETIME {}
| DATE_SYM {}
| DAY_SYM {}
| DEALLOCATE_SYM {}
| DEFINER_SYM {}
| DELAY_KEY_WRITE_SYM {}
| DES_KEY_FILE {}
| DIRECTORY_SYM {}
......@@ -5162,6 +6888,7 @@ keyword:
| EXPANSION_SYM {}
| EXTENDED_SYM {}
| FAST_SYM {}
| FOUND_SYM {}
| DISABLE_SYM {}
| ENABLE_SYM {}
| FULL {}
......@@ -5169,6 +6896,7 @@ keyword:
| FIRST_SYM {}
| FIXED_SYM {}
| FLUSH_SYM {}
| FRAC_SECOND_SYM {}
| GEOMETRY_SYM {}
| GEOMETRYCOLLECTION {}
| GET_FORMAT {}
......@@ -5180,6 +6908,7 @@ keyword:
| HOSTS_SYM {}
| HOUR_SYM {}
| IDENTIFIED_SYM {}
| INVOKER_SYM {}
| IMPORT {}
| INDEXES {}
| ISOLATION {}
......@@ -5187,6 +6916,8 @@ keyword:
| INNOBASE_SYM {}
| INSERT_METHOD {}
| RELAY_THREAD {}
| LABEL_SYM {}
| LANGUAGE_SYM {}
| LAST_SYM {}
| LEAVES {}
| LEVEL_SYM {}
......@@ -5214,6 +6945,7 @@ keyword:
| MAX_QUERIES_PER_HOUR {}
| MAX_UPDATES_PER_HOUR {}
| MEDIUM_SYM {}
| MERGE_SYM {}
| MICROSECOND_SYM {}
| MINUTE_SYM {}
| MIN_ROWS {}
......@@ -5223,6 +6955,7 @@ keyword:
| MULTILINESTRING {}
| MULTIPOINT {}
| MULTIPOLYGON {}
| NAME_SYM {}
| NAMES_SYM {}
| NATIONAL_SYM {}
| NCHAR_SYM {}
......@@ -5245,6 +6978,7 @@ keyword:
| PREV_SYM {}
| PROCESS {}
| PROCESSLIST_SYM {}
| QUARTER_SYM {}
| QUERY_SYM {}
| QUICK {}
| RAID_0_SYM {}
......@@ -5261,6 +6995,7 @@ keyword:
| RESET_SYM {}
| RESOURCES {}
| RESTORE_SYM {}
| RETURNS_SYM {}
| ROLLBACK_SYM {}
| ROLLUP_SYM {}
| ROWS_SYM {}
......@@ -5269,6 +7004,7 @@ keyword:
| RTREE_SYM {}
| SAVEPOINT_SYM {}
| SECOND_SYM {}
| SECURITY_SYM {}
| SERIAL_SYM {}
| SERIALIZABLE_SYM {}
| SESSION_SYM {}
......@@ -5293,23 +7029,29 @@ keyword:
| SUPER_SYM {}
| TABLESPACE {}
| TEMPORARY {}
| TEMPTABLE_SYM {}
| TEXT_SYM {}
| TRANSACTION_SYM {}
| TRUNCATE_SYM {}
| TIMESTAMP {}
| TIMESTAMP_ADD {}
| TIMESTAMP_DIFF {}
| TIME_SYM {}
| TYPE_SYM {}
| TYPES_SYM {}
| UDF_RETURNS_SYM {}
| UDF_SYM {}
| FUNCTION_SYM {}
| UNCOMMITTED_SYM {}
| UNDEFINED_SYM {}
| UNICODE_SYM {}
| UNTIL_SYM {}
| USER {}
| USE_FRM {}
| VARIABLES {}
| VIEW_SYM {}
| VALUE_SYM {}
| WARNINGS {}
| WEEK_SYM {}
| WORK_SYM {}
| X509_SYM {}
| YEAR_SYM {}
......@@ -5363,14 +7105,106 @@ opt_var_ident_type:
option_value:
'@' ident_or_text equal expr
{
Lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4)));
LEX *lex= Lex;
if (lex->sphead && lex->sphead->m_type != TYPE_ENUM_PROCEDURE)
{
/*
We have to use special instruction in functions and triggers
because sp_instr_stmt will close all tables and thus ruin
execution of statement invoking function or trigger.
We also do not want to allow expression with subselects in
this case.
*/
if (lex->query_tables)
{
send_error(YYTHD, ER_SP_SUBSELECT_NYI);
YYABORT;
}
sp_instr_set_user_var *i=
new sp_instr_set_user_var(lex->sphead->instructions(),
lex->spcont, $2, $4);
lex->sphead->add_instr(i);
}
else
lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4)));
}
| internal_variable_name equal set_expr_or_default
{
LEX *lex=Lex;
if ($1.var == &trg_new_row_fake_var)
{
/* We are in trigger and assigning value to field of new row */
Item *it;
sp_instr_set_trigger_field *i;
if (lex->query_tables)
{
send_error(YYTHD, ER_SP_SUBSELECT_NYI);
YYABORT;
}
if ($3)
it= $3;
else
{
/* QQ: Shouldn't this be field's default value ? */
it= new Item_null();
}
i= new sp_instr_set_trigger_field(lex->sphead->instructions(),
lex->spcont, $1.base_name, it);
if (lex->trg_table && i->setup_field(YYTHD, lex->trg_table,
lex->trg_chistics.event))
{
/*
FIXME. Now we are catching this kind of errors only
during opening tables. But this doesn't save us from most
common user error - misspelling field name, because we
will bark too late in this case... Moreover it is easy to
make table unusable with such kind of error...
So in future we either have to parse trigger definition
second time during create trigger or gather all trigger
fields in one list and perform setup_field() for them as
separate stage.
Error message also should be improved.
*/
net_printf(YYTHD, ER_BAD_FIELD_ERROR, $1.base_name, "NEW");
YYABORT;
}
lex->sphead->add_instr(i);
}
else if ($1.var)
{ /* System variable */
lex->var_list.push_back(new set_var(lex->option_type, $1.var,
&$1.base_name, $3));
}
else
{
/* An SP local variable */
sp_pcontext *ctx= lex->spcont;
sp_pvar_t *spv;
sp_instr_set *i;
Item *it;
spv= ctx->find_pvar(&$1.base_name);
if ($3)
it= $3;
else if (spv->dflt)
it= spv->dflt;
else
it= new Item_null();
i= new sp_instr_set(lex->sphead->instructions(), ctx,
spv->offset, it, spv->type);
i->tables= lex->query_tables;
lex->query_tables= 0;
lex->sphead->add_instr(i);
spv->isset= TRUE;
}
}
| '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default
{
LEX *lex=Lex;
......@@ -5427,6 +7261,14 @@ option_value:
internal_variable_name:
ident
{
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
sp_pvar_t *spv;
/* We have to lookup here since local vars can shadow sysvars */
if (!spc || !(spv = spc->find_pvar(&$1)))
{
/* Not an SP local variable */
sys_var *tmp=find_sys_var($1.str, $1.length);
if (!tmp)
YYABORT;
......@@ -5440,13 +7282,47 @@ internal_variable_name:
if (tmp == &sys_time_zone)
Lex->time_zone_tables_used= &fake_time_zone_tables_list;
}
else
{
/* An SP local variable */
$$.var= NULL;
$$.base_name= $1;
}
}
| ident '.' ident
{
LEX *lex= Lex;
if (check_reserved_words(&$1))
{
yyerror(ER(ER_SYNTAX_ERROR));
YYABORT;
}
if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER &&
(!my_strcasecmp(system_charset_info, $1.str, "NEW") ||
!my_strcasecmp(system_charset_info, $1.str, "OLD")))
{
if ($1.str[0]=='O' || $1.str[0]=='o')
{
net_printf(YYTHD, ER_TRG_CANT_CHANGE_ROW, "OLD", "");
YYABORT;
}
if (lex->trg_chistics.event == TRG_EVENT_DELETE)
{
net_printf(YYTHD, ER_TRG_NO_SUCH_ROW_IN_TRG, "NEW",
"on DELETE");
YYABORT;
}
if (lex->trg_chistics.action_time == TRG_ACTION_AFTER)
{
net_printf(YYTHD, ER_TRG_CANT_CHANGE_ROW, "NEW", "after ");
YYABORT;
}
/* This special combination will denote field of NEW row */
$$.var= &trg_new_row_fake_var;
$$.base_name= $3;
}
else
{
sys_var *tmp=find_sys_var($3.str, $3.length);
if (!tmp)
YYABORT;
......@@ -5455,6 +7331,7 @@ internal_variable_name:
$$.var= tmp;
$$.base_name= $1;
}
}
| DEFAULT '.' ident
{
sys_var *tmp=find_sys_var($3.str, $3.length);
......@@ -5687,8 +7564,10 @@ grant_privilege:
| SUPER_SYM { Lex->grant |= SUPER_ACL;}
| CREATE TEMPORARY TABLES { Lex->grant |= CREATE_TMP_ACL;}
| LOCK_SYM TABLES { Lex->grant |= LOCK_TABLES_ACL; }
| REPLICATION SLAVE { Lex->grant |= REPL_SLAVE_ACL;}
| REPLICATION CLIENT_SYM { Lex->grant |= REPL_CLIENT_ACL;}
| REPLICATION SLAVE { Lex->grant |= REPL_SLAVE_ACL; }
| REPLICATION CLIENT_SYM { Lex->grant |= REPL_CLIENT_ACL; }
| CREATE VIEW_SYM { Lex->grant |= CREATE_VIEW_ACL; }
| SHOW VIEW_SYM { Lex->grant |= SHOW_VIEW_ACL; }
;
......@@ -6077,3 +7956,47 @@ subselect_end:
lex->current_select = lex->current_select->return_after_parsing();
};
opt_view_list:
/* empty */ {}
| '(' view_list ')'
;
view_list:
ident
{
Lex->view_list.push_back((LEX_STRING*)
sql_memdup(&$1, sizeof(LEX_STRING)));
}
| view_list ',' ident
{
Lex->view_list.push_back((LEX_STRING*)
sql_memdup(&$3, sizeof(LEX_STRING)));
}
;
or_replace:
/* empty */ { Lex->create_view_mode= VIEW_CREATE_NEW; }
| OR_SYM REPLACE { Lex->create_view_mode= VIEW_CREATE_OR_REPLACE; }
;
algorithm:
/* empty */
{ Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; }
| ALGORITHM_SYM EQ UNDEFINED_SYM
{ Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; }
| ALGORITHM_SYM EQ MERGE_SYM
{ Lex->create_view_algorithm= VIEW_ALGORITHM_MERGE; }
| ALGORITHM_SYM EQ TEMPTABLE_SYM
{ Lex->create_view_algorithm= VIEW_ALGORITHM_TMPTABLE; }
;
check_option:
/* empty */
{ Lex->create_view_check= VIEW_CHECK_NONE; }
| WITH CHECK_SYM OPTION
{ Lex->create_view_check= VIEW_CHECK_CASCADED; }
| WITH CASCADED CHECK_SYM OPTION
{ Lex->create_view_check= VIEW_CHECK_CASCADED; }
| WITH LOCAL_SYM CHECK_SYM OPTION
{ Lex->create_view_check= VIEW_CHECK_LOCAL; }
;
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