summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client/mysqlbinlog.cc330
-rw-r--r--client/mysqldump.c224
-rw-r--r--sql/handler.cc195
-rw-r--r--sql/lex.h71
-rw-r--r--sql/log.cc161
-rw-r--r--sql/log_event.cc1634
-rw-r--r--sql/sql_parse.cc1731
-rw-r--r--sql/sql_yacc.yy2373
8 files changed, 5382 insertions, 1337 deletions
diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc
index 8015871428e..ff90c77a2e8 100644
--- a/client/mysqlbinlog.cc
+++ b/client/mysqlbinlog.cc
@@ -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,30 +341,49 @@ 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
-1 ok and terminate
-
+
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))
{
- /*
- 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).
- */
- start_datetime= 0;
- offset= 0; // print everything and protect against cycling rec_count
+ 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).
+ */
+ 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;
@@ -873,64 +956,135 @@ static int dump_remote_log_entries(const char* logname)
const char *old_fname= le->fname;
uint old_len= le->fname_len;
File file;
-
+
if ((file= load_processor.prepare_new_file_for_old_format(le,fname)) < 0)
{
error= 1;
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));
+ 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)
{
- my_close(file,MYF(MY_WME));
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(;;)
{
- if (buf[4] == START_EVENT)
+ tmp_pos= my_b_tell(file); /* should be 4 the first time */
+ if (my_b_read(file, buf, sizeof(buf)))
{
- uint event_len;
- event_len = uint4korr(buf + EVENT_LEN_OFFSET);
- old_format = (event_len < (LOG_EVENT_HEADER_LEN + START_HEADER_LEN));
+ 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 (uint4korr(buf + EVENT_LEN_OFFSET) <
+ (LOG_EVENT_MINIMAL_HEADER_LEN + START_V3_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 */
@@ -972,46 +1123,44 @@ static int dump_local_log_entries(const char* logname)
{
tmp=min(length,sizeof(buff));
if (my_b_read(file, buff, (uint) tmp))
- {
- error= 1;
- goto end;
- }
+ {
+ error= 1;
+ goto end;
+ }
}
}
file->pos_in_file= start_position_mot;
file->seek_not_done=0;
}
- if (!start_position)
+ 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))
{
- // Skip header
- if (my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE))
- {
- error= 1;
- goto end;
- }
+ 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)
{
fprintf(stderr,
- "Could not read entry at offset %s:"
- "Error in log format or read error\n",
- llstr(old_off,llbuff));
- error= 1;
+ "Could not read entry at offset %s:"
+ "Error in log format or read error\n",
+ llstr(old_off,llbuff));
+ error= 1;
}
// 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 @@ end:
if (fd >= 0)
my_close(fd, MYF(MY_WME));
end_io_cache(file);
+ delete description_event;
return error;
}
diff --git a/client/mysqldump.c b/client/mysqldump.c
index dbc11f1ab90..76211c4244e 100644
--- a/client/mysqldump.c
+++ b/client/mysqldump.c
@@ -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_names))
+ if (dump_all_tables_in_db(*db))
result=1;
}
+ if (!result && was_views)
+ {
+ for (db= db_names ; *db ; db++)
+ {
+ 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 (strcmp(row[1], (result= "MRG_MyISAM")) &&
- strcmp(row[1], (result= "MRG_ISAM")))
- result= 0;
+ 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;
diff --git a/sql/handler.cc b/sql/handler.cc
index 5dae7950390..b2acb262763 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -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);
diff --git a/sql/lex.h b/sql/lex.h
index 325d052de90..db31b3af994 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -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)},
diff --git a/sql/log.cc b/sql/log.cc
index bcd99326501..86b76ed883e 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -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 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.
+ */
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;
- }
+ 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 @@ err:
/*
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 @@ err:
/*
- 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 */
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 091566c5d90..7fba19cb000 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -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 @@ end:
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) ||
- my_b_safe_write(file, (db) ? (byte*) db : (byte*)"", db_len + 1) ||
- my_b_safe_write(file, (byte*) query, q_len)) ? -1 : 0;
+ /*
+ 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;
}
@@ -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;
-
- 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;
+
+ /* A 2nd variable part; this is common to all versions */
+
+ 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);
@@ -1032,20 +1444,20 @@ START SLAVE; . Query: '%s'", expected_error, thd->query);
}
goto end;
}
-
+
/*
If we expected a non-zero error code, and we don't get the same error
code, and none of them should be ignored.
*/
DBUG_PRINT("info",("expected_error: %d last_errno: %d",
- expected_error, thd->net.last_errno));
+ expected_error, thd->net.last_errno));
if ((expected_error != (actual_error= thd->net.last_errno)) &&
- expected_error &&
- !ignored_error_code(actual_error) &&
- !ignored_error_code(expected_error))
+ expected_error &&
+ !ignored_error_code(actual_error) &&
+ !ignored_error_code(expected_error))
{
slave_print_error(rli, 0,
- "\
+ "\
Query caused different errors on master and slave. \
Error on master: '%s' (%d), Error on slave: '%s' (%d). \
Default database: '%s'. Query: '%s'",
@@ -1053,14 +1465,14 @@ 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;
}
/*
If we get the same error code as expected, or they should be ignored.
*/
else if (expected_error == actual_error ||
- ignored_error_code(actual_error))
+ ignored_error_code(actual_error))
{
DBUG_PRINT("info",("error ignored"));
clear_all_errors(thd, rli);
@@ -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 @@ end:
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,57 +1574,69 @@ 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;
-
- print_header(file);
- fprintf(file, "\tStart: binlog v %d, server v %s created ", binlog_version,
- server_version);
- print_timestamp(file);
- if (created)
- fprintf(file," at startup");
- fputc('\n', file);
+ if (!short_form)
+ {
+ print_header(file);
+ fprintf(file, "\tStart: binlog v %d, server v %s created ", binlog_version,
+ server_version);
+ print_timestamp(file);
+ 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:
- /*
- 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.
+ switch (rli->relay_log.description_event_for_exec->binlog_version)
+ {
+ case 3:
+ case 4:
+ /*
+ 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').
*/
- close_temporary_tables(thd);
- cleanup_load_tmpdir();
+ 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:
- /*
- 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.
- */
- break;
- case BINLOG_FORMAT_323_GEQ_57:
+ case 1:
+ if (strncmp(rli->relay_log.description_event_for_exec->server_version,
+ "3.23.57",7) >= 0 && created)
+ {
+ /*
+ Can distinguish, based on the value of 'created': this event was
+ generated at master startup.
+ */
+ 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) */
-/**************************************************************************
- Load_log_event methods
-**************************************************************************/
+/***************************************************************************
+ 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,21 +2250,17 @@ 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,
- buf_end,
- buf[EVENT_TYPE_OFFSET] != LOAD_EVENT)))
+ if (!(field_lens= (uchar*)sql_ex.init((char*)buf + body_offset,
+ buf_end,
+ buf[EVENT_TYPE_OFFSET] != LOAD_EVENT)))
DBUG_RETURN(1);
-
+
data_len = event_len - body_offset;
if (num_fields > data_len) // simple sanity check against corruption
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,15 +2443,12 @@ 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));
}
-
- /*
+
+ /*
We test replicate_*_db rules. Note that we have already prepared the file
to load, even if we are going to ignore and delete it now. So it is
possible that we did a lot of disk writes for nothing. In other words, a
@@ -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,17 +2655,17 @@ 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)
my_fwrite(file, (byte*) new_log_ident, (uint)ident_len,
- MYF(MY_NABP | MY_WME));
+ MYF(MY_NABP | MY_WME));
fprintf(file, " pos: %s", llstr(pos, buf));
fputc('\n', file);
fflush(file);
@@ -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,29 +2705,31 @@ 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) ||
- my_b_safe_write(file, (byte*)new_log_ident, (uint) ident_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));
}
/*
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);
- flush_relay_log_info(rli);
+ 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,13 +3493,13 @@ 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));
+ 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;
@@ -3250,8 +4032,8 @@ int Execute_load_log_event::exec_event(struct st_relay_log_info* rli)
goto err;
}
if (!(lev = (Load_log_event*)Log_event::read_log_event(&file,
- (pthread_mutex_t*)0,
- (bool)0)) ||
+ (pthread_mutex_t*)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
+ rli->future_group_master_log_pos= log_pos;
if (lev->exec_event(0,rli,1))
{
/*
@@ -3332,15 +4106,15 @@ err:
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++;
}
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 9d755731cb7..5e9162b52b2 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -26,6 +26,9 @@
#include "ha_innodb.h"
#endif
+#include "sp_head.h"
+#include "sp.h"
+
#ifdef HAVE_OPENSSL
/*
Without SSL the handshake consists of one packet. This packet
@@ -44,6 +47,15 @@
#define MIN_HANDSHAKE_SIZE 6
#endif /* HAVE_OPENSSL */
+/* Used in error handling only */
+#define SP_TYPE_STRING(LP) \
+ ((LP)->sphead->m_type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE")
+#define SP_COM_STRING(LP) \
+ ((LP)->sql_command == SQLCOM_CREATE_SPFUNCTION || \
+ (LP)->sql_command == SQLCOM_ALTER_FUNCTION || \
+ (LP)->sql_command == SQLCOM_DROP_FUNCTION ? \
+ "FUNCTION" : "PROCEDURE")
+
#ifdef SOLARIS
extern "C" int gethostname(char *name, int namelen);
#endif
@@ -57,6 +69,7 @@ static void remove_escape(char *name);
static void refresh_status(void);
static bool append_file_to_dir(THD *thd, const char **filename_ptr,
const char *table_name);
+static bool check_sp_definer_access(THD *thd, sp_head *sp);
const char *any_db="*any*"; // Special symbol for check_access
@@ -66,7 +79,7 @@ const char *command_name[]={
"Connect","Kill","Debug","Ping","Time","Delayed insert","Change user",
"Binlog Dump","Table Dump", "Connect Out", "Register Slave",
"Prepare", "Prepare Execute", "Long Data", "Close stmt",
- "Reset stmt", "Set option",
+ "Reset stmt", "Set option", "Fetch",
"Error" // Last command number
};
@@ -96,7 +109,7 @@ static void unlock_locked_tables(THD *thd)
if (thd->locked_tables)
{
thd->lock=thd->locked_tables;
- thd->locked_tables=0; // Will be automaticly closed
+ thd->locked_tables=0; // Will be automatically closed
close_thread_tables(thd); // Free tables
}
}
@@ -193,13 +206,13 @@ end:
command originator of the check: now check_user is called
during connect and change user procedures; used for
logging.
- passwd scrambled password recieved from client
+ passwd scrambled password received from client
passwd_len length of scrambled password
db database name to connect to, may be NULL
check_count dont know exactly
Note, that host, user and passwd may point to communication buffer.
- Current implementation does not depened on that, but future changes
+ Current implementation does not depend on that, but future changes
should be done with this in mind; 'thd' is INOUT, all other params
are 'IN'.
@@ -257,7 +270,7 @@ int check_user(THD *thd, enum enum_server_command command,
/*
Clear thd->db as it points to something, that will be freed when
- connection is closed. We don't want to accidently free a wrong pointer
+ connection is closed. We don't want to accidentally free a wrong pointer
if connect failed. Also in case of 'CHANGE USER' failure, current
database will be switched to 'no database selected'.
*/
@@ -284,9 +297,10 @@ int check_user(THD *thd, enum enum_server_command command,
thd->user, thd->host_or_ip);
DBUG_RETURN(-1);
}
+ /* We have to read very specific packet size */
if (send_old_password_request(thd) ||
- my_net_read(net) != SCRAMBLE_LENGTH_323 + 1) // We have to read very
- { // specific packet size
+ my_net_read(net) != SCRAMBLE_LENGTH_323 + 1)
+ {
inc_host_errors(&thd->remote.sin_addr);
DBUG_RETURN(ER_HANDSHAKE_ERROR);
}
@@ -298,7 +312,7 @@ int check_user(THD *thd, enum enum_server_command command,
/* here res is always >= 0 */
if (res == 0)
{
- if (!(thd->master_access & NO_ACCESS)) // authentification is OK
+ if (!(thd->master_access & NO_ACCESS)) // authentication is OK
{
DBUG_PRINT("info",
("Capabilities: %d packet_length: %ld Host: '%s' "
@@ -533,6 +547,8 @@ void init_update_queries(void)
uc_update_queries[SQLCOM_DELETE_MULTI]=1;
uc_update_queries[SQLCOM_DROP_INDEX]=1;
uc_update_queries[SQLCOM_UPDATE_MULTI]=1;
+ uc_update_queries[SQLCOM_CREATE_VIEW]=1;
+ uc_update_queries[SQLCOM_DROP_VIEW]=1;
}
bool is_update_query(enum enum_sql_command command)
@@ -621,8 +637,9 @@ static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0)
uc->conn_per_hour=0;
}
}
- else // for FLUSH PRIVILEGES and FLUSH USER_RESOURCES
+ else
{
+ /* for FLUSH PRIVILEGES and FLUSH USER_RESOURCES */
for (uint idx=0;idx < hash_user_connections.records; idx++)
{
USER_CONN *uc=(struct user_conn *) hash_element(&hash_user_connections,
@@ -721,7 +738,7 @@ static int check_connection(THD *thd)
#endif /* HAVE_COMPRESS */
#ifdef HAVE_OPENSSL
if (ssl_acceptor_fd)
- client_flags |= CLIENT_SSL; /* Wow, SSL is avalaible! */
+ client_flags |= CLIENT_SSL; /* Wow, SSL is available! */
#endif /* HAVE_OPENSSL */
end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1;
@@ -948,7 +965,7 @@ pthread_handler_decl(handle_one_connection,arg)
pthread_detach_this_thread();
#if !defined( __WIN__) && !defined(OS2) // Win32 calls this in pthread_create
- // The following calls needs to be done before we call DBUG_ macros
+ /* The following calls needs to be done before we call DBUG_ macros */
if (!(test_flags & TEST_NO_THREADS) & my_thread_init())
{
close_connection(thd, ER_OUT_OF_RESOURCES, 1);
@@ -967,7 +984,7 @@ pthread_handler_decl(handle_one_connection,arg)
*/
DBUG_PRINT("info", ("handle_one_connection called by thread %d\n",
thd->thread_id));
- // now that we've called my_thread_init(), it is safe to call DBUG_*
+ /* now that we've called my_thread_init(), it is safe to call DBUG_* */
#if defined(__WIN__)
init_signals(); // IRENA; testing ?
@@ -1013,24 +1030,28 @@ pthread_handler_decl(handle_one_connection,arg)
thd->proc_info= 0;
thd->set_time();
thd->init_for_queries();
+
if (sys_init_connect.value_length && !(thd->master_access & SUPER_ACL))
{
execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect);
if (thd->query_error)
- thd->killed= 1;
+ thd->killed= THD::KILL_CONNECTION;
}
- while (!net->error && net->vio != 0 && !thd->killed)
+
+ thd->proc_info=0;
+ thd->set_time();
+ thd->init_for_queries();
+ while (!net->error && net->vio != 0 && !(thd->killed == THD::KILL_CONNECTION))
{
if (do_command(thd))
break;
}
if (thd->user_connect)
decrease_user_connections(thd->user_connect);
- free_root(thd->mem_root,MYF(0));
if (net->error && net->vio != 0 && net->report_error)
{
if (!thd->killed && thd->variables.log_warnings > 1)
- sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
+ sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
thd->thread_id,(thd->db ? thd->db : "unconnected"),
thd->user ? thd->user : "unauthenticated",
thd->host_or_ip,
@@ -1116,6 +1137,10 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg)
thd->query_length=length;
thd->query= thd->memdup_w_gap(buff, length+1, thd->db_length+1);
thd->query[length] = '\0';
+ /*
+ We don't need to obtain LOCK_thread_count here because in bootstrap
+ mode we have only one thread.
+ */
thd->query_id=query_id++;
if (mqh_used && thd->user_connect && check_mqh(thd, SQLCOM_END))
{
@@ -1149,8 +1174,10 @@ end:
void free_items(Item *item)
{
+ DBUG_ENTER("free_items");
for (; item ; item=item->next)
item->delete_self();
+ DBUG_VOID_RETURN;
}
/* This works because items are allocated with sql_alloc() */
@@ -1170,10 +1197,10 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd)
db = (db && db[0]) ? db : thd->db;
if (!(table_list = (TABLE_LIST*) thd->calloc(sizeof(TABLE_LIST))))
DBUG_RETURN(1); // out of memory
- table_list->db = db;
- table_list->real_name = table_list->alias = tbl_name;
- table_list->lock_type = TL_READ_NO_INSERT;
- table_list->next = 0;
+ table_list->db= db;
+ table_list->real_name= table_list->alias= tbl_name;
+ table_list->lock_type= TL_READ_NO_INSERT;
+ table_list->prev_global= &table_list; // can be removed after merge with 4.1
if (!db || check_db_name(db))
{
@@ -1237,7 +1264,7 @@ bool do_command(THD *thd)
packet=0;
old_timeout=net->read_timeout;
- // Wait max for 8 hours
+ /* Wait max for 8 hours */
net->read_timeout=(uint) thd->variables.net_wait_timeout;
thd->clear_error(); // Clear error message
@@ -1259,6 +1286,9 @@ bool do_command(THD *thd)
}
else
{
+ if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA)
+ thd->killed= THD::NOT_KILLED;
+
packet=(char*) net->read_pos;
command = (enum enum_server_command) (uchar) packet[0];
if (command >= COM_END)
@@ -1310,6 +1340,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
this so that they will not get logged to the slow query log
*/
thd->slow_command=FALSE;
+ thd->lex->sql_command= SQLCOM_END; /* to avoid confusing VIEW detectors */
thd->set_time();
VOID(pthread_mutex_lock(&LOCK_thread_count));
thd->query_id=query_id;
@@ -1325,7 +1356,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
case COM_INIT_DB:
{
LEX_STRING tmp;
- statistic_increment(com_stat[SQLCOM_CHANGE_DB],&LOCK_status);
+ statistic_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB],
+ &LOCK_status);
thd->convert_string(&tmp, system_charset_info,
packet, strlen(packet), thd->charset());
if (!mysql_change_db(thd, tmp.str))
@@ -1346,7 +1378,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
uint db_len= *(uchar*) packet;
uint tbl_len= *(uchar*) (packet + db_len + 1);
- statistic_increment(com_other, &LOCK_status);
+ statistic_increment(thd->status_var.com_other, &LOCK_status);
thd->slow_command= TRUE;
db= thd->alloc(db_len + tbl_len + 2);
tbl_name= strmake(db, packet + 1, db_len)+1;
@@ -1360,7 +1392,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->change_user();
thd->clear_error(); // if errors from rollback
- statistic_increment(com_other, &LOCK_status);
+ statistic_increment(thd->status_var.com_other, &LOCK_status);
char *user= (char*) packet;
char *passwd= strend(user)+1;
/*
@@ -1374,7 +1406,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
*passwd++ : strlen(passwd);
db+= passwd_len + 1;
#ifndef EMBEDDED_LIBRARY
- /* Small check for incomming packet */
+ /* Small check for incoming packet */
if ((uint) ((uchar*) db - net->read_pos) > packet_length)
{
send_error(thd, ER_UNKNOWN_COM_ERROR);
@@ -1410,7 +1442,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (res)
{
- /* authentification failure, we shall restore old user */
+ /* authentication failure, we shall restore old user */
if (res > 0)
send_error(thd, ER_UNKNOWN_COM_ERROR);
x_free(thd->user);
@@ -1437,6 +1469,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
mysql_stmt_execute(thd, packet, packet_length);
break;
}
+ case COM_FETCH:
+ {
+ mysql_stmt_fetch(thd, packet, packet_length);
+ break;
+ }
case COM_LONG_DATA:
{
mysql_stmt_get_longdata(thd, packet, packet_length);
@@ -1523,20 +1560,20 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
TABLE_LIST table_list;
LEX_STRING conv_name;
- statistic_increment(com_stat[SQLCOM_SHOW_FIELDS],&LOCK_status);
+ statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS],
+ &LOCK_status);
bzero((char*) &table_list,sizeof(table_list));
if (!(table_list.db=thd->db))
{
send_error(thd,ER_NO_DB_ERROR);
break;
}
- thd->free_list=0;
pend= strend(packet);
thd->convert_string(&conv_name, system_charset_info,
packet, (uint) (pend-packet), thd->charset());
table_list.alias= table_list.real_name= conv_name.str;
packet= pend+1;
- // command not cachable => no gap for data base name
+ /* command not cachable => no gap for data base name */
if (!(thd->query=fields=thd->memdup(packet,thd->query_length+1)))
break;
mysql_log.write(thd,command,"%s %s",table_list.real_name,fields);
@@ -1550,9 +1587,18 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (grant_option &&
check_grant(thd, SELECT_ACL, &table_list, 2, UINT_MAX, 0))
break;
+ /* init structures for VIEW processing */
+ table_list.select_lex= &(thd->lex->select_lex);
+ mysql_init_query(thd, (uchar*)"", 0);
+ thd->lex->
+ select_lex.table_list.link_in_list((byte*) &table_list,
+ (byte**) &table_list.next_local);
+
+ /* switch on VIEW optimisation: do not fill temporary tables */
+ thd->lex->sql_command= SQLCOM_SHOW_FIELDS;
mysqld_list_fields(thd,&table_list,fields);
- free_items(thd->free_list);
- thd->free_list= 0;
+ thd->lex->unit.cleanup();
+ thd->cleanup_after_query();
break;
}
#endif
@@ -1568,7 +1614,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
char *db=thd->strdup(packet), *alias;
HA_CREATE_INFO create_info;
- statistic_increment(com_stat[SQLCOM_CREATE_DB],&LOCK_status);
+ statistic_increment(thd->status_var.com_stat[SQLCOM_CREATE_DB],
+ &LOCK_status);
// null test to handle EOM
if (!db || !(alias= thd->strdup(db)) || check_db_name(db))
{
@@ -1586,9 +1633,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
}
case COM_DROP_DB: // QQ: To be removed
{
- statistic_increment(com_stat[SQLCOM_DROP_DB],&LOCK_status);
+ statistic_increment(thd->status_var.com_stat[SQLCOM_DROP_DB],
+ &LOCK_status);
char *db=thd->strdup(packet), *alias;
- // null test to handle EOM
+ /* null test to handle EOM */
if (!db || !(alias= thd->strdup(db)) || check_db_name(db))
{
net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL");
@@ -1614,7 +1662,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
ushort flags;
uint32 slave_server_id;
- statistic_increment(com_other,&LOCK_status);
+ statistic_increment(thd->status_var.com_other,&LOCK_status);
thd->slow_command = TRUE;
if (check_global_access(thd, REPL_SLAVE_ACL))
break;
@@ -1631,7 +1679,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
(long) pos);
mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags);
unregister_slave(thd,1,1);
- // fake COM_QUIT -- if we get here, the thread needs to terminate
+ /* fake COM_QUIT -- if we get here, the thread needs to terminate */
error = TRUE;
net->error = 0;
break;
@@ -1639,7 +1687,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#endif
case COM_REFRESH:
{
- statistic_increment(com_stat[SQLCOM_FLUSH],&LOCK_status);
+ statistic_increment(thd->status_var.com_stat[SQLCOM_FLUSH],
+ &LOCK_status);
ulong options= (ulong) (uchar) packet[0];
if (check_global_access(thd,RELOAD_ACL))
break;
@@ -1653,7 +1702,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#ifndef EMBEDDED_LIBRARY
case COM_SHUTDOWN:
{
- statistic_increment(com_other,&LOCK_status);
+ statistic_increment(thd->status_var.com_other, &LOCK_status);
if (check_global_access(thd,SHUTDOWN_ACL))
break; /* purecov: inspected */
/*
@@ -1684,8 +1733,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#endif
close_connection(thd, 0, 1);
close_thread_tables(thd); // Free before kill
- free_root(thd->mem_root,MYF(0));
- free_root(&thd->transaction.mem_root,MYF(0));
kill_mysql();
error=TRUE;
break;
@@ -1694,7 +1741,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
case COM_STATISTICS:
{
mysql_log.write(thd,command,NullS);
- statistic_increment(com_stat[SQLCOM_SHOW_STATUS],&LOCK_status);
+ statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_STATUS],
+ &LOCK_status);
#ifndef EMBEDDED_LIBRARY
char buff[200];
#else
@@ -1704,8 +1752,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
sprintf((char*) buff,
"Uptime: %ld Threads: %d Questions: %lu Slow queries: %ld Opens: %ld Flush tables: %ld Open tables: %u Queries per second avg: %.3f",
uptime,
- (int) thread_count,thd->query_id,long_query_count,
- opened_tables,refresh_version, cached_tables(),
+ (int) thread_count,thd->query_id,thd->status_var.long_query_count,
+ thd->status_var.opened_tables,refresh_version, cached_tables(),
uptime ? (float)thd->query_id/(float)uptime : 0);
#ifdef SAFEMALLOC
if (sf_malloc_cur_memory) // Using SAFEMALLOC
@@ -1720,11 +1768,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break;
}
case COM_PING:
- statistic_increment(com_other,&LOCK_status);
+ statistic_increment(thd->status_var.com_other, &LOCK_status);
send_ok(thd); // Tell client we are alive
break;
case COM_PROCESS_INFO:
- statistic_increment(com_stat[SQLCOM_SHOW_PROCESSLIST],&LOCK_status);
+ statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_PROCESSLIST],
+ &LOCK_status);
if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL))
break;
mysql_log.write(thd,command,NullS);
@@ -1734,14 +1783,15 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break;
case COM_PROCESS_KILL:
{
- statistic_increment(com_stat[SQLCOM_KILL],&LOCK_status);
+ statistic_increment(thd->status_var.com_stat[SQLCOM_KILL], &LOCK_status);
ulong id=(ulong) uint4korr(packet);
- kill_one_thread(thd,id);
+ kill_one_thread(thd,id,false);
break;
}
case COM_SET_OPTION:
{
- statistic_increment(com_stat[SQLCOM_SET_OPTION], &LOCK_status);
+ statistic_increment(thd->status_var.com_stat[SQLCOM_SET_OPTION],
+ &LOCK_status);
enum_mysql_set_option command= (enum_mysql_set_option) uint2korr(packet);
switch (command) {
case MYSQL_OPTION_MULTI_STATEMENTS_ON:
@@ -1759,7 +1809,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break;
}
case COM_DEBUG:
- statistic_increment(com_other,&LOCK_status);
+ statistic_increment(thd->status_var.com_other, &LOCK_status);
if (check_global_access(thd, SUPER_ACL))
break; /* purecov: inspected */
mysql_print_status(thd);
@@ -1798,7 +1848,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) &&
(specialflag & SPECIAL_LOG_QUERIES_NOT_USING_INDEXES)))
{
- long_query_count++;
+ thd->status_var.long_query_count++;
mysql_slow_log.write(thd, thd->query, thd->query_length, start_of_query);
}
}
@@ -1811,6 +1861,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thread_running--;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
+
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
DBUG_RETURN(error);
}
@@ -1870,23 +1921,54 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length)
** Execute command saved in thd and current_lex->sql_command
****************************************************************************/
-void
+int
mysql_execute_command(THD *thd)
{
int res= 0;
LEX *lex= thd->lex;
+ /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
SELECT_LEX *select_lex= &lex->select_lex;
- TABLE_LIST *tables= (TABLE_LIST*) select_lex->table_list.first;
+ /* first table of first SELECT_LEX */
+ TABLE_LIST *first_table= (TABLE_LIST*) select_lex->table_list.first;
+ /* list of all tables in query */
+ TABLE_LIST *all_tables;
+ /* most outer SELECT_LEX_UNIT of query */
SELECT_LEX_UNIT *unit= &lex->unit;
DBUG_ENTER("mysql_execute_command");
/*
+ In many cases first table of main SELECT_LEX have special meaning =>
+ check that it is first table in global list and relink it first in
+ queries_tables list if it is necessary (we need such relinking only
+ for queries with subqueries in select list, in this case tables of
+ subqueries will go to global list first)
+
+ all_tables will differ from first_table only if most upper SELECT_LEX
+ do not contain tables.
+
+ Because of above in place where should be at least one table in most
+ outer SELECT_LEX we have following check:
+ DBUG_ASSERT(first_table == all_tables);
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ */
+ lex->first_lists_tables_same();
+ /* should be assigned after making first tables same */
+ all_tables= lex->query_tables;
+
+ if (lex->sql_command != SQLCOM_CREATE_PROCEDURE &&
+ lex->sql_command != SQLCOM_CREATE_SPFUNCTION)
+ {
+ if (sp_cache_functions(thd, lex))
+ DBUG_RETURN(-1);
+ }
+
+ /*
Reset warning count for each query that uses tables
A better approach would be to reset this for any commands
that is not a SHOW command or a select that only access local
variables, but for now this is probably good enough.
*/
- if (tables || &lex->select_lex != lex->all_selects_list)
+ if (all_tables || &lex->select_lex != lex->all_selects_list)
mysql_reset_errors(thd);
#ifdef HAVE_REPLICATION
@@ -1896,11 +1978,11 @@ mysql_execute_command(THD *thd)
Skip if we are in the slave thread, some table rules have been
given and the table list says the query should not be replicated
*/
- if (all_tables_not_ok(thd,tables))
+ if (all_tables_not_ok(thd, all_tables))
{
/* we warn the slave SQL thread */
my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
- DBUG_VOID_RETURN;
+ DBUG_RETURN(0);
}
#ifndef TO_BE_DELETED
/*
@@ -1916,10 +1998,20 @@ mysql_execute_command(THD *thd)
#endif
}
#endif /* !HAVE_REPLICATION */
- if ((&lex->select_lex != lex->all_selects_list ||
- lex->time_zone_tables_used) &&
- lex->unit.create_total_list(thd, lex, &tables))
- DBUG_VOID_RETURN;
+
+ if (lex->time_zone_tables_used)
+ {
+ TABLE_LIST *tmp;
+ if ((tmp= my_tz_get_table_list(thd, &lex->query_tables_last)) ==
+ &fake_time_zone_tables_list)
+ {
+ send_error(thd, 0);
+ DBUG_RETURN(-1);
+ }
+ lex->time_zone_tables_used= tmp;
+ if (!all_tables)
+ all_tables= tmp;
+ }
/*
When option readonly is set deny operations which change tables.
@@ -1930,10 +2022,11 @@ mysql_execute_command(THD *thd)
(uc_update_queries[lex->sql_command] > 0))
{
net_printf(thd, ER_OPTION_PREVENTS_STATEMENT, "--read-only");
- DBUG_VOID_RETURN;
+ DBUG_RETURN(-1);
}
- statistic_increment(com_stat[lex->sql_command],&LOCK_status);
+ statistic_increment(thd->status_var.com_stat[lex->sql_command],
+ &LOCK_status);
switch (lex->sql_command) {
case SQLCOM_SELECT:
{
@@ -1945,42 +2038,31 @@ mysql_execute_command(THD *thd)
}
select_result *result=lex->result;
- if (tables)
+ if (all_tables)
{
- res=check_table_access(thd,
- lex->exchange ? SELECT_ACL | FILE_ACL :
- SELECT_ACL,
- tables,0);
+ res= check_table_access(thd,
+ lex->exchange ? SELECT_ACL | FILE_ACL :
+ SELECT_ACL,
+ all_tables, 0);
}
else
- res=check_access(thd, lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
- any_db,0,0,0);
+ res= check_access(thd,
+ lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
+ any_db, 0, 0, 0);
if (res)
{
res=0;
break; // Error message is given
}
- /*
- In case of single SELECT unit->global_parameters points on first SELECT
- TODO: move counters to SELECT_LEX
- */
- unit->offset_limit_cnt= (ha_rows) unit->global_parameters->offset_limit;
- unit->select_limit_cnt= (ha_rows) (unit->global_parameters->select_limit+
- unit->global_parameters->offset_limit);
- if (unit->select_limit_cnt <
- (ha_rows) unit->global_parameters->select_limit)
- unit->select_limit_cnt= HA_POS_ERROR; // no limit
- if (unit->select_limit_cnt == HA_POS_ERROR && !select_lex->next_select())
- select_lex->options&= ~OPTION_FOUND_ROWS;
- if (!(res=open_and_lock_tables(thd,tables)))
+ if (!(res= open_and_lock_tables(thd, all_tables)))
{
if (lex->describe)
{
if (!(result= new select_send()))
{
send_error(thd, ER_OUT_OF_RESOURCES);
- DBUG_VOID_RETURN;
+ goto error;
}
else
thd->send_explain_fields(result);
@@ -2005,7 +2087,7 @@ mysql_execute_command(THD *thd)
res= -1;
break;
}
- query_cache_store_query(thd, tables);
+ query_cache_store_query(thd, all_tables);
res= handle_select(thd, lex, result);
if (result != lex->result)
delete result;
@@ -2127,8 +2209,9 @@ mysql_execute_command(THD *thd)
break;
}
case SQLCOM_DO:
- if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
- (res= open_and_lock_tables(thd,tables))))
+ if (all_tables &&
+ ((res= check_table_access(thd, SELECT_ACL, all_tables, 0)) ||
+ (res= open_and_lock_tables(thd, all_tables))))
break;
res= mysql_do(thd, *lex->insert_list);
@@ -2149,7 +2232,7 @@ mysql_execute_command(THD *thd)
{
if (check_global_access(thd, SUPER_ACL))
goto error;
- // PURGE MASTER LOGS TO 'file'
+ /* PURGE MASTER LOGS TO 'file' */
res = purge_master_logs(thd, lex->to_log);
break;
}
@@ -2157,7 +2240,7 @@ mysql_execute_command(THD *thd)
{
if (check_global_access(thd, SUPER_ACL))
goto error;
- // PURGE MASTER LOGS BEFORE 'data'
+ /* PURGE MASTER LOGS BEFORE 'data' */
res = purge_master_logs_before_date(thd, lex->purge_time);
break;
}
@@ -2210,41 +2293,45 @@ mysql_execute_command(THD *thd)
case SQLCOM_BACKUP_TABLE:
{
- if (check_db_used(thd,tables) ||
- check_table_access(thd,SELECT_ACL, tables,0) ||
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, SELECT_ACL, all_tables, 0) ||
check_global_access(thd, FILE_ACL))
goto error; /* purecov: inspected */
thd->slow_command=TRUE;
- res = mysql_backup_table(thd, tables);
+ res = mysql_backup_table(thd, first_table);
break;
}
case SQLCOM_RESTORE_TABLE:
{
- if (check_db_used(thd,tables) ||
- check_table_access(thd, INSERT_ACL, tables,0) ||
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, INSERT_ACL, all_tables, 0) ||
check_global_access(thd, FILE_ACL))
goto error; /* purecov: inspected */
thd->slow_command=TRUE;
- res = mysql_restore_table(thd, tables);
+ res = mysql_restore_table(thd, first_table);
break;
}
case SQLCOM_ASSIGN_TO_KEYCACHE:
{
- if (check_db_used(thd, tables) ||
- check_access(thd, INDEX_ACL, tables->db,
- &tables->grant.privilege, 0, 0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_access(thd, INDEX_ACL, first_table->db,
+ &first_table->grant.privilege, 0, 0))
goto error;
- res= mysql_assign_to_keycache(thd, tables, &lex->name_and_length);
+ res= mysql_assign_to_keycache(thd, first_table, &lex->name_and_length);
break;
}
case SQLCOM_PRELOAD_KEYS:
{
- if (check_db_used(thd, tables) ||
- check_access(thd, INDEX_ACL, tables->db,
- &tables->grant.privilege, 0, 0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_access(thd, INDEX_ACL, first_table->db,
+ &first_table->grant.privilege, 0, 0))
goto error;
- res = mysql_preload_keys(thd, tables);
+ res = mysql_preload_keys(thd, first_table);
break;
}
#ifdef HAVE_REPLICATION
@@ -2297,19 +2384,21 @@ mysql_execute_command(THD *thd)
#ifdef HAVE_REPLICATION
case SQLCOM_LOAD_MASTER_TABLE:
{
- if (!tables->db)
- tables->db=thd->db;
- if (check_access(thd,CREATE_ACL,tables->db,&tables->grant.privilege,0,0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (!first_table->db)
+ first_table->db= thd->db;
+ if (check_access(thd, CREATE_ACL, first_table->db,
+ &first_table->grant.privilege, 0, 0))
goto error; /* purecov: inspected */
if (grant_option)
{
/* Check that the first table has CREATE privilege */
- if (check_grant(thd, CREATE_ACL, tables, 0, 1, 0))
+ if (check_grant(thd, CREATE_ACL, all_tables, 0, 1, 0))
goto error;
}
- if (strlen(tables->real_name) > NAME_LEN)
+ if (strlen(first_table->real_name) > NAME_LEN)
{
- net_printf(thd,ER_WRONG_TABLE_NAME, tables->real_name);
+ net_printf(thd, ER_WRONG_TABLE_NAME, first_table->real_name);
break;
}
pthread_mutex_lock(&LOCK_active_mi);
@@ -2317,7 +2406,7 @@ mysql_execute_command(THD *thd)
fetch_master_table will send the error to the client on failure.
Give error if the table already exists.
*/
- if (!fetch_master_table(thd, tables->db, tables->real_name,
+ if (!fetch_master_table(thd, first_table->db, first_table->real_name,
active_mi, 0, 0))
{
send_ok(thd);
@@ -2329,12 +2418,13 @@ mysql_execute_command(THD *thd)
case SQLCOM_CREATE_TABLE:
{
- /* Skip first table, which is the table we are creating */
- TABLE_LIST *create_table, *create_table_local;
- tables= lex->unlink_first_table(tables, &create_table,
- &create_table_local);
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ bool link_to_local;
+ // Skip first table, which is the table we are creating
+ TABLE_LIST *create_table= lex->unlink_first_table(&link_to_local);
+ TABLE_LIST *select_tables= lex->query_tables;
- if ((res= create_table_precheck(thd, tables, create_table)))
+ if ((res= create_table_precheck(thd, select_tables, create_table)))
goto unsent_create_error;
#ifndef HAVE_READLINK
@@ -2343,7 +2433,7 @@ mysql_execute_command(THD *thd)
/* Fix names if symlinked tables */
if (append_file_to_dir(thd, &lex->create_info.data_file_name,
create_table->real_name) ||
- append_file_to_dir(thd,&lex->create_info.index_file_name,
+ append_file_to_dir(thd, &lex->create_info.index_file_name,
create_table->real_name))
{
res=-1;
@@ -2351,7 +2441,7 @@ mysql_execute_command(THD *thd)
}
#endif
/*
- If we are using SET CHARSET without DEFAULT, add an implicite
+ If we are using SET CHARSET without DEFAULT, add an implicit
DEFAULT to not confuse old users. (This may change).
*/
if ((lex->create_info.used_fields &
@@ -2368,21 +2458,42 @@ mysql_execute_command(THD *thd)
select_result *result;
select_lex->options|= SELECT_NO_UNLOCK;
- unit->offset_limit_cnt= select_lex->offset_limit;
- unit->select_limit_cnt= select_lex->select_limit+
- select_lex->offset_limit;
- if (unit->select_limit_cnt < select_lex->select_limit)
- unit->select_limit_cnt= HA_POS_ERROR; // No limit
+ unit->set_limit(select_lex, select_lex);
- if (!(res=open_and_lock_tables(thd,tables)))
+ if (!(res= open_and_lock_tables(thd, select_tables)))
{
- res= -1; // If error
- if ((result=new select_create(create_table->db,
- create_table->real_name,
- &lex->create_info,
- lex->create_list,
- lex->key_list,
- select_lex->item_list,lex->duplicates)))
+ /*
+ Is table which we are changing used somewhere in other parts
+ of query
+ */
+ if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&
+ unique_table(create_table, select_tables))
+ {
+ net_printf(thd, ER_UPDATE_TABLE_USED, create_table->real_name);
+ goto create_error;
+ }
+ /* If we create merge table, we have to test tables in merge, too */
+ if (lex->create_info.used_fields & HA_CREATE_USED_UNION)
+ {
+ TABLE_LIST *tab;
+ for (tab= (TABLE_LIST*) lex->create_info.merge_list.first;
+ tab;
+ tab= tab->next_local)
+ {
+ if (unique_table(tab, select_tables))
+ {
+ net_printf(thd, ER_UPDATE_TABLE_USED, tab->real_name);
+ goto create_error;
+ }
+ }
+ }
+
+ if ((result= new select_create(create_table,
+ &lex->create_info,
+ lex->create_list,
+ lex->key_list,
+ select_lex->item_list,
+ lex->duplicates)))
{
/*
CREATE from SELECT give its SELECT_LEX for SELECT,
@@ -2391,49 +2502,48 @@ mysql_execute_command(THD *thd)
select_lex->resolve_mode= SELECT_LEX::SELECT_MODE;
res=handle_select(thd, lex, result);
select_lex->resolve_mode= SELECT_LEX::NOMATTER_MODE;
+ delete result;
}
- //reset for PS
+ /* reset for PS */
lex->create_list.empty();
lex->key_list.empty();
}
}
- else // regular create
+ else
{
+ /* regular create */
if (lex->name)
res= mysql_create_like_table(thd, create_table, &lex->create_info,
(Table_ident *)lex->name);
else
{
- res= mysql_create_table(thd,create_table->db,
- create_table->real_name, &lex->create_info,
- lex->create_list,
- lex->key_list,0,0);
+ res= mysql_create_table(thd, create_table->db,
+ create_table->real_name, &lex->create_info,
+ lex->create_list,
+ lex->key_list, 0, 0);
}
if (!res)
send_ok(thd);
}
-
- // put tables back for PS rexecuting
- tables= lex->link_first_table_back(tables, create_table,
- create_table_local);
+ lex->link_first_table_back(create_table, link_to_local);
break;
create_error:
- res= 1; //error reported
+ res= 1; //error reported
unsent_create_error:
- // put tables back for PS rexecuting
- tables= lex->link_first_table_back(tables, create_table,
- create_table_local);
+ /* put tables back for PS rexecuting */
+ lex->link_first_table_back(create_table, link_to_local);
break;
}
case SQLCOM_CREATE_INDEX:
- if (check_one_table_access(thd, INDEX_ACL, tables))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_one_table_access(thd, INDEX_ACL, all_tables))
goto error; /* purecov: inspected */
thd->slow_command=TRUE;
if (end_active_trans(thd))
res= -1;
else
- res = mysql_create_index(thd, tables, lex->key_list);
+ res = mysql_create_index(thd, first_table, lex->key_list);
break;
#ifdef HAVE_REPLICATION
@@ -2461,7 +2571,7 @@ unsent_create_error:
if (thd->locked_tables || thd->active_transaction())
{
send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
- break;
+ goto error;
}
{
pthread_mutex_lock(&LOCK_active_mi);
@@ -2472,9 +2582,10 @@ unsent_create_error:
#endif /* HAVE_REPLICATION */
case SQLCOM_ALTER_TABLE:
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
#if defined(DONT_ALLOW_SHOW_COMMANDS)
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- break;
+ goto error;
#else
{
ulong priv=0;
@@ -2485,16 +2596,17 @@ unsent_create_error:
break;
}
if (!select_lex->db)
- select_lex->db=tables->db;
- if (check_access(thd,ALTER_ACL,tables->db,&tables->grant.privilege,0,0) ||
+ select_lex->db= first_table->db;
+ if (check_access(thd, ALTER_ACL, first_table->db,
+ &first_table->grant.privilege, 0, 0) ||
check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv,0,0)||
- check_merge_table_access(thd, tables->db,
+ check_merge_table_access(thd, first_table->db,
(TABLE_LIST *)
lex->create_info.merge_list.first))
goto error; /* purecov: inspected */
if (grant_option)
{
- if (check_grant(thd, ALTER_ACL, tables, 0, UINT_MAX, 0))
+ if (check_grant(thd, ALTER_ACL, all_tables, 0, UINT_MAX, 0))
goto error;
if (lex->name && !test_all_bits(priv,INSERT_ACL | CREATE_ACL))
{ // Rename of table
@@ -2518,7 +2630,7 @@ unsent_create_error:
thd->slow_command=TRUE;
res= mysql_alter_table(thd, select_lex->db, lex->name,
&lex->create_info,
- tables, lex->create_list,
+ first_table, lex->create_list,
lex->key_list,
select_lex->order_list.elements,
(ORDER *) select_lex->order_list.first,
@@ -2529,38 +2641,37 @@ unsent_create_error:
#endif /*DONT_ALLOW_SHOW_COMMANDS*/
case SQLCOM_RENAME_TABLE:
{
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
TABLE_LIST *table;
- if (check_db_used(thd,tables))
+ if (check_db_used(thd, all_tables))
goto error;
- for (table=tables ; table ; table=table->next->next)
+ for (table= first_table; table; table= table->next_local->next_local)
{
if (check_access(thd, ALTER_ACL | DROP_ACL, table->db,
&table->grant.privilege,0,0) ||
- check_access(thd, INSERT_ACL | CREATE_ACL, table->next->db,
- &table->next->grant.privilege,0,0))
+ check_access(thd, INSERT_ACL | CREATE_ACL, table->next_local->db,
+ &table->next_local->grant.privilege, 0, 0))
goto error;
if (grant_option)
{
- TABLE_LIST old_list,new_list;
+ TABLE_LIST old_list, new_list;
/*
we do not need initialize old_list and new_list because we will
come table[0] and table->next[0] there
*/
- old_list=table[0];
- new_list=table->next[0];
- old_list.next=new_list.next=0;
- if (check_grant(thd, ALTER_ACL, &old_list, 0, UINT_MAX, 0) ||
- (!test_all_bits(table->next->grant.privilege,
+ old_list= table[0];
+ new_list= table->next_local[0];
+ if (check_grant(thd, ALTER_ACL, &old_list, 0, 1, 0) ||
+ (!test_all_bits(table->next_local->grant.privilege,
INSERT_ACL | CREATE_ACL) &&
- check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0,
- UINT_MAX, 0)))
+ check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0, 1, 0)))
goto error;
}
}
- query_cache_invalidate3(thd, tables, 0);
+ query_cache_invalidate3(thd, first_table, 0);
if (end_active_trans(thd))
res= -1;
- else if (mysql_rename_tables(thd,tables))
+ else if (mysql_rename_tables(thd, first_table))
res= -1;
break;
}
@@ -2568,7 +2679,7 @@ unsent_create_error:
case SQLCOM_SHOW_BINLOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
if (check_global_access(thd, SUPER_ACL))
@@ -2579,38 +2690,40 @@ unsent_create_error:
#endif
#endif /* EMBEDDED_LIBRARY */
case SQLCOM_SHOW_CREATE:
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
- if (check_db_used(thd, tables) ||
- check_access(thd, SELECT_ACL | EXTRA_ACL, tables->db,
- &tables->grant.privilege,0,0))
+ if (check_db_used(thd, all_tables) ||
+ check_access(thd, SELECT_ACL | EXTRA_ACL, first_table->db,
+ &first_table->grant.privilege, 0, 0))
goto error;
- res = mysqld_show_create(thd, tables);
+ res = mysqld_show_create(thd, first_table);
break;
}
#endif
case SQLCOM_CHECKSUM:
{
- if (check_db_used(thd,tables) ||
- check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables,0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, SELECT_ACL | EXTRA_ACL, all_tables, 0))
goto error; /* purecov: inspected */
- res = mysql_checksum_table(thd, tables, &lex->check_opt);
+ res = mysql_checksum_table(thd, first_table, &lex->check_opt);
break;
}
case SQLCOM_REPAIR:
{
- if (check_db_used(thd,tables) ||
- check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
goto error; /* purecov: inspected */
thd->slow_command=TRUE;
- res = mysql_repair_table(thd, tables, &lex->check_opt);
+ res= mysql_repair_table(thd, first_table, &lex->check_opt);
/* ! we write after unlocking the table */
if (!res && !lex->no_write_to_binlog)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
thd->clear_error(); // No binlog error generated
@@ -2622,24 +2735,25 @@ unsent_create_error:
}
case SQLCOM_CHECK:
{
- if (check_db_used(thd,tables) ||
- check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables,0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, SELECT_ACL | EXTRA_ACL , all_tables, 0))
goto error; /* purecov: inspected */
thd->slow_command=TRUE;
- res = mysql_check_table(thd, tables, &lex->check_opt);
+ res = mysql_check_table(thd, first_table, &lex->check_opt);
break;
}
case SQLCOM_ANALYZE:
{
- if (check_db_used(thd,tables) ||
- check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
goto error; /* purecov: inspected */
thd->slow_command=TRUE;
- res = mysql_analyze_table(thd, tables, &lex->check_opt);
+ res = mysql_analyze_table(thd, first_table, &lex->check_opt);
/* ! we write after unlocking the table */
if (!res && !lex->no_write_to_binlog)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
thd->clear_error(); // No binlog error generated
@@ -2652,17 +2766,17 @@ unsent_create_error:
case SQLCOM_OPTIMIZE:
{
- if (check_db_used(thd,tables) ||
- check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
goto error; /* purecov: inspected */
thd->slow_command=TRUE;
res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
- mysql_recreate_table(thd, tables, 1) :
- mysql_optimize_table(thd, tables, &lex->check_opt);
+ mysql_recreate_table(thd, first_table, 1) :
+ mysql_optimize_table(thd, first_table, &lex->check_opt);
/* ! we write after unlocking the table */
if (!res && !lex->no_write_to_binlog)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
thd->clear_error(); // No binlog error generated
@@ -2673,9 +2787,10 @@ unsent_create_error:
break;
}
case SQLCOM_UPDATE:
- if (update_precheck(thd, tables))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (update_precheck(thd, all_tables))
break;
- res= mysql_update(thd,tables,
+ res= mysql_update(thd, all_tables,
select_lex->item_list,
lex->value_list,
select_lex->where,
@@ -2688,9 +2803,10 @@ unsent_create_error:
break;
case SQLCOM_UPDATE_MULTI:
{
- if ((res= multi_update_precheck(thd, tables)))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if ((res= multi_update_precheck(thd, all_tables)))
break;
- res= mysql_multi_update(thd,tables,
+ res= mysql_multi_update(thd, all_tables,
&select_lex->item_list,
&lex->value_list,
select_lex->where,
@@ -2701,67 +2817,81 @@ unsent_create_error:
case SQLCOM_REPLACE:
case SQLCOM_INSERT:
{
- if ((res= insert_precheck(thd, tables)))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if ((res= insert_precheck(thd, all_tables)))
break;
- res = mysql_insert(thd,tables,lex->field_list,lex->many_values,
- select_lex->item_list, lex->value_list,
- lex->duplicates);
+ res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
+ select_lex->item_list, lex->value_list,
+ (lex->value_list.elements ?
+ DUP_UPDATE : lex->duplicates));
if (thd->net.report_error)
res= -1;
+ if (first_table->view && !first_table->contain_auto_increment)
+ thd->last_insert_id= 0; // do not show last insert ID if VIEW have not it
break;
}
case SQLCOM_REPLACE_SELECT:
case SQLCOM_INSERT_SELECT:
{
- TABLE_LIST *first_local_table= (TABLE_LIST *) select_lex->table_list.first;
- if ((res= insert_select_precheck(thd, tables)))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if ((res= insert_select_precheck(thd, all_tables)))
break;
/* Fix lock for first table */
- if (tables->lock_type == TL_WRITE_DELAYED)
- tables->lock_type= TL_WRITE;
+ if (first_table->lock_type == TL_WRITE_DELAYED)
+ first_table->lock_type= TL_WRITE;
/* Don't unlock tables until command is written to binary log */
select_lex->options|= SELECT_NO_UNLOCK;
select_result *result;
- unit->offset_limit_cnt= select_lex->offset_limit;
- unit->select_limit_cnt= select_lex->select_limit+select_lex->offset_limit;
- if (unit->select_limit_cnt < select_lex->select_limit)
- unit->select_limit_cnt= HA_POS_ERROR; // No limit
-
- if (find_real_table_in_list(tables->next, tables->db, tables->real_name))
- {
- /* Using same table for INSERT and SELECT */
- select_lex->options |= OPTION_BUFFER_RESULT;
- }
-
+ unit->set_limit(select_lex, select_lex);
- if (!(res= open_and_lock_tables(thd, tables)) &&
- (result= new select_insert(tables->table, &lex->field_list,
- lex->duplicates)))
+ if (!(res= open_and_lock_tables(thd, all_tables)))
{
- /* Skip first table, which is the table we are inserting in */
- lex->select_lex.table_list.first= (byte*) first_local_table->next;
/*
- insert/replace from SELECT give its SELECT_LEX for SELECT,
- and item_list belong to SELECT
+ Is table which we are changing used somewhere in other parts of
+ query
*/
- lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE;
- res= handle_select(thd, lex, result);
- /* revert changes for SP */
- lex->select_lex.table_list.first= (byte*) first_local_table;
- lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE;
- delete result;
+ if (unique_table(first_table, all_tables->next_global))
+ {
+ /* Using same table for INSERT and SELECT */
+ select_lex->options |= OPTION_BUFFER_RESULT;
+ }
+
+ if ((res= mysql_insert_select_prepare(thd)))
+ break;
+ if ((result= new select_insert(first_table, first_table->table,
+ &lex->field_list, lex->duplicates,
+ lex->duplicates == DUP_IGNORE)))
+ {
+ /* Skip first table, which is the table we are inserting in */
+ lex->select_lex.table_list.first= (byte*) first_table->next_local;
+ /*
+ insert/replace from SELECT give its SELECT_LEX for SELECT,
+ and item_list belong to SELECT
+ */
+ lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE;
+ res= handle_select(thd, lex, result);
+ /* revert changes for SP */
+ lex->select_lex.table_list.first= (byte*) first_table;
+ lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE;
+ delete result;
+ }
if (thd->net.report_error)
res= -1;
}
else
res= -1;
+
+ if (first_table->view && !first_table->contain_auto_increment)
+ thd->last_insert_id= 0; // do not show last insert ID if VIEW have not it
+
break;
}
case SQLCOM_TRUNCATE:
- if (check_one_table_access(thd, DELETE_ACL, tables))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_one_table_access(thd, DELETE_ACL, all_tables))
goto error;
/*
Don't allow this within a transaction because we want to use
@@ -2772,13 +2902,15 @@ unsent_create_error:
send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION,NullS);
goto error;
}
- res=mysql_truncate(thd, tables, 0);
+
+ res= mysql_truncate(thd, first_table, 0);
break;
case SQLCOM_DELETE:
{
- if ((res= delete_precheck(thd, tables)))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if ((res= delete_precheck(thd, all_tables)))
break;
- res = mysql_delete(thd,tables, select_lex->where,
+ res = mysql_delete(thd, all_tables, select_lex->where,
&select_lex->order_list,
select_lex->select_limit, select_lex->options);
if (thd->net.report_error)
@@ -2787,13 +2919,13 @@ unsent_create_error:
}
case SQLCOM_DELETE_MULTI:
{
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
TABLE_LIST *aux_tables=
(TABLE_LIST *)thd->lex->auxilliary_table_list.first;
- TABLE_LIST *target_tbl;
uint table_count;
multi_delete *result;
- if ((res= multi_delete_precheck(thd, tables, &table_count)))
+ if ((res= multi_delete_precheck(thd, all_tables, &table_count)))
break;
/* condition will be TRUE on SP re-excuting */
@@ -2806,28 +2938,9 @@ unsent_create_error:
}
thd->proc_info="init";
- if ((res=open_and_lock_tables(thd,tables)))
+ if ((res= open_and_lock_tables(thd, all_tables)) ||
+ (res= mysql_multi_delete_prepare(thd)))
break;
- /* Fix tables-to-be-deleted-from list to point at opened tables */
- for (target_tbl= (TABLE_LIST*) aux_tables;
- target_tbl;
- target_tbl= target_tbl->next)
- {
- TABLE_LIST *orig= target_tbl->table_list;
- target_tbl->table= orig->table;
- /*
- Multi-delete can't be constructed over-union => we always have
- single SELECT on top and have to check underlying SELECTs of it
- */
- if (lex->select_lex.check_updateable_in_subqueries(orig->db,
- orig->real_name))
- {
- my_error(ER_UPDATE_TABLE_USED, MYF(0),
- orig->real_name);
- res= -1;
- break;
- }
- }
if (!thd->is_fatal_error && (result= new multi_delete(thd,aux_tables,
table_count)))
@@ -2853,9 +2966,10 @@ unsent_create_error:
}
case SQLCOM_DROP_TABLE:
{
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
if (!lex->drop_temporary)
{
- if (check_table_access(thd,DROP_ACL,tables,0))
+ if (check_table_access(thd, DROP_ACL, all_tables, 0))
goto error; /* purecov: inspected */
if (end_active_trans(thd))
{
@@ -2876,21 +2990,23 @@ unsent_create_error:
if (thd->slave_thread)
lex->drop_if_exists= 1;
}
- res= mysql_rm_table(thd,tables,lex->drop_if_exists, lex->drop_temporary);
+ res= mysql_rm_table(thd, first_table, lex->drop_if_exists,
+ lex->drop_temporary);
}
break;
case SQLCOM_DROP_INDEX:
- if (check_one_table_access(thd, INDEX_ACL, tables))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_one_table_access(thd, INDEX_ACL, all_tables))
goto error; /* purecov: inspected */
if (end_active_trans(thd))
res= -1;
else
- res = mysql_drop_index(thd, tables, &lex->alter_info);
+ res = mysql_drop_index(thd, first_table, &lex->alter_info);
break;
case SQLCOM_SHOW_DATABASES:
#if defined(DONT_ALLOW_SHOW_COMMANDS)
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
if ((specialflag & SPECIAL_SKIP_SHOW_DB) &&
check_global_access(thd, SHOW_DB_ACL))
@@ -2915,18 +3031,28 @@ unsent_create_error:
res= mysqld_show_column_types(thd);
break;
case SQLCOM_SHOW_STATUS:
- res= mysqld_show(thd,(lex->wild ? lex->wild->ptr() : NullS),status_vars,
- OPT_GLOBAL, &LOCK_status);
+ STATUS_VAR tmp;
+ if (lex->option_type == OPT_GLOBAL)
+ {
+ pthread_mutex_lock(&LOCK_status);
+ calc_sum_of_all_status(&tmp);
+ }
+ res= mysqld_show(thd, (lex->wild ? lex->wild->ptr() : NullS),
+ status_vars, OPT_GLOBAL, &LOCK_status,
+ (lex->option_type == OPT_GLOBAL ?
+ &tmp: &thd->status_var));
+ if (lex->option_type == OPT_GLOBAL)
+ pthread_mutex_unlock(&LOCK_status);
break;
case SQLCOM_SHOW_VARIABLES:
res= mysqld_show(thd, (lex->wild ? lex->wild->ptr() : NullS),
init_vars, lex->option_type,
- &LOCK_global_system_variables);
+ &LOCK_global_system_variables, 0);
break;
case SQLCOM_SHOW_LOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
if (grant_option && check_access(thd, FILE_ACL, any_db,0,0,0))
@@ -2939,13 +3065,13 @@ unsent_create_error:
/* FALL THROUGH */
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
char *db=select_lex->db ? select_lex->db : thd->db;
if (!db)
{
- send_error(thd,ER_NO_DB_ERROR); /* purecov: inspected */
+ send_error(thd,ER_NO_DB_ERROR); /* purecov: inspected */
goto error; /* purecov: inspected */
}
remove_escape(db); // Fix escaped '_'
@@ -2969,8 +3095,9 @@ unsent_create_error:
res= mysqld_extend_show_tables(thd,db,
(lex->wild ? lex->wild->ptr() : NullS));
else
- res= mysqld_show_tables(thd,db,
- (lex->wild ? lex->wild->ptr() : NullS));
+ res= mysqld_show_tables(thd, db,
+ (lex->wild ? lex->wild->ptr() : NullS),
+ lex->verbose);
break;
}
#endif
@@ -2984,40 +3111,42 @@ unsent_create_error:
res= mysqld_show_collations(thd,(lex->wild ? lex->wild->ptr() : NullS));
break;
case SQLCOM_SHOW_FIELDS:
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
- char *db=tables->db;
+ char *db= first_table->db;
remove_escape(db); // Fix escaped '_'
- remove_escape(tables->real_name);
+ remove_escape(first_table->real_name);
if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,
- &tables->grant.privilege, 0, 0))
+ &first_table->grant.privilege, 0, 0))
goto error; /* purecov: inspected */
- if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0))
+ if (grant_option && check_grant(thd, SELECT_ACL, first_table, 2, UINT_MAX, 0))
goto error;
- res= mysqld_show_fields(thd,tables,
+ res= mysqld_show_fields(thd, first_table,
(lex->wild ? lex->wild->ptr() : NullS),
lex->verbose);
break;
}
#endif
case SQLCOM_SHOW_KEYS:
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
- char *db=tables->db;
+ char *db= first_table->db;
remove_escape(db); // Fix escaped '_'
- remove_escape(tables->real_name);
+ remove_escape(first_table->real_name);
if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,
- &tables->grant.privilege, 0, 0))
+ &first_table->grant.privilege, 0, 0))
goto error; /* purecov: inspected */
- if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0))
+ if (grant_option && check_grant(thd, SELECT_ACL, all_tables, 2, UINT_MAX, 0))
goto error;
- res= mysqld_show_keys(thd,tables);
+ res= mysqld_show_keys(thd, first_table);
break;
}
#endif
@@ -3027,12 +3156,13 @@ unsent_create_error:
case SQLCOM_LOAD:
{
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
uint privilege= (lex->duplicates == DUP_REPLACE ?
INSERT_ACL | DELETE_ACL : INSERT_ACL);
if (!lex->local_file)
{
- if (check_access(thd,privilege | FILE_ACL,tables->db,0,0,0))
+ if (check_access(thd, privilege | FILE_ACL, first_table->db, 0, 0, 0))
goto error;
}
else
@@ -3043,19 +3173,21 @@ unsent_create_error:
send_error(thd,ER_NOT_ALLOWED_COMMAND);
goto error;
}
- if (check_one_table_access(thd, privilege, tables))
+ if (check_one_table_access(thd, privilege, all_tables))
goto error;
}
- res=mysql_load(thd, lex->exchange, tables, lex->field_list,
- lex->duplicates, (bool) lex->local_file, lex->lock_option);
+ res= mysql_load(thd, lex->exchange, first_table, lex->field_list,
+ lex->duplicates, (bool) lex->local_file,
+ lex->lock_option, lex->duplicates == DUP_IGNORE);
break;
}
case SQLCOM_SET_OPTION:
{
List<set_var_base> *lex_var_list= &lex->var_list;
- if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
- (res= open_and_lock_tables(thd,tables))))
+ if (all_tables &&
+ ((res= check_table_access(thd, SELECT_ACL, all_tables, 0)) ||
+ (res= open_and_lock_tables(thd, all_tables))))
break;
if (lex->one_shot_set && not_all_support_one_shot(lex_var_list))
{
@@ -3097,17 +3229,18 @@ purposes internal to the MySQL server", MYF(0));
break;
case SQLCOM_LOCK_TABLES:
unlock_locked_tables(thd);
- if (check_db_used(thd,tables) || end_active_trans(thd))
+ if (check_db_used(thd, all_tables) || end_active_trans(thd))
goto error;
- if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, tables,0))
+ if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0))
goto error;
thd->in_lock_tables=1;
thd->options|= OPTION_TABLE_LOCK;
- if (!(res= open_and_lock_tables(thd, tables)))
+
+ if (!(res= open_and_lock_tables(thd, all_tables)))
{
#ifdef HAVE_QUERY_CACHE
if (thd->variables.query_cache_wlock_invalidate)
- query_cache.invalidate_locked_for_write(tables);
+ query_cache.invalidate_locked_for_write(first_table);
#endif /*HAVE_QUERY_CACHE*/
thd->locked_tables=thd->lock;
thd->lock=0;
@@ -3232,26 +3365,24 @@ purposes internal to the MySQL server", MYF(0));
res=mysqld_show_create_db(thd,lex->name,&lex->create_info);
break;
}
- case SQLCOM_CREATE_FUNCTION:
+ case SQLCOM_CREATE_FUNCTION: // UDF function
+ {
+ sp_head *sph;
if (check_access(thd,INSERT_ACL,"mysql",0,1,0))
break;
#ifdef HAVE_DLOPEN
+ if ((sph= sp_find_function(thd, lex->spname)))
+ {
+ net_printf(thd, ER_UDF_EXISTS, lex->spname->m_name.str);
+ goto error;
+ }
if (!(res = mysql_create_function(thd,&lex->udf)))
send_ok(thd);
#else
res= -1;
#endif
break;
- case SQLCOM_DROP_FUNCTION:
- if (check_access(thd,DELETE_ACL,"mysql",0,1,0))
- break;
-#ifdef HAVE_DLOPEN
- if (!(res = mysql_drop_function(thd,&lex->udf.name)))
- send_ok(thd);
-#else
- res= -1;
-#endif
- break;
+ }
#ifndef NO_EMBEDDED_ACCESS_CHECKS
case SQLCOM_DROP_USER:
{
@@ -3259,7 +3390,6 @@ purposes internal to the MySQL server", MYF(0));
break;
if (!(res= mysql_drop_user(thd, lex->users_list)))
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -3275,7 +3405,6 @@ purposes internal to the MySQL server", MYF(0));
break;
if (!(res = mysql_revoke_all(thd, lex->users_list)))
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -3289,34 +3418,12 @@ purposes internal to the MySQL server", MYF(0));
case SQLCOM_GRANT:
{
if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
- tables && tables->db ? tables->db : select_lex->db,
- tables ? &tables->grant.privilege : 0,
- tables ? 0 : 1,0))
+ ((first_table && first_table->db) ?
+ first_table->db : select_lex->db),
+ first_table ? &first_table->grant.privilege : 0,
+ first_table ? 0 : 1, 0))
goto error;
- /*
- Check that the user isn't trying to change a password for another
- user if he doesn't have UPDATE privilege to the MySQL database
- */
-
- if (thd->user) // If not replication
- {
- LEX_USER *user;
- List_iterator <LEX_USER> user_list(lex->users_list);
- while ((user=user_list++))
- {
- if (user->password.str &&
- (strcmp(thd->user,user->user.str) ||
- user->host.str &&
- my_strcasecmp(&my_charset_latin1,
- user->host.str, thd->host_or_ip)))
- {
- if (check_access(thd, UPDATE_ACL, "mysql",0,1,0))
- goto error;
- break; // We are allowed to do changes
- }
- }
- }
if (specialflag & SPECIAL_NO_RESOLVE)
{
LEX_USER *user;
@@ -3330,24 +3437,21 @@ purposes internal to the MySQL server", MYF(0));
user->host.str);
}
}
- if (tables)
+ if (first_table)
{
if (grant_option && check_grant(thd,
(lex->grant | lex->grant_tot_col |
GRANT_ACL),
- tables, 0, UINT_MAX, 0))
+ all_tables, 0, UINT_MAX, 0))
goto error;
- if (!(res = mysql_table_grant(thd,tables,lex->users_list, lex->columns,
- lex->grant,
- lex->sql_command == SQLCOM_REVOKE)))
+ if (!(res = mysql_table_grant(thd, all_tables, lex->users_list,
+ lex->columns, lex->grant,
+ lex->sql_command == SQLCOM_REVOKE)) &&
+ mysql_bin_log.is_open())
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
- mysql_bin_log.write(&qinfo);
- }
+ thd->clear_error();
+ Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
+ mysql_bin_log.write(&qinfo);
}
}
else
@@ -3362,7 +3466,6 @@ purposes internal to the MySQL server", MYF(0));
lex->sql_command == SQLCOM_REVOKE);
if (!res)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
thd->clear_error();
@@ -3389,14 +3492,14 @@ purposes internal to the MySQL server", MYF(0));
lex->no_write_to_binlog= 1;
case SQLCOM_FLUSH:
{
- if (check_global_access(thd,RELOAD_ACL) || check_db_used(thd, tables))
+ if (check_global_access(thd,RELOAD_ACL) || check_db_used(thd, all_tables))
goto error;
/*
reload_acl_and_cache() will tell us if we are allowed to write to the
binlog or not.
*/
bool write_to_binlog;
- if (reload_acl_and_cache(thd, lex->type, tables, &write_to_binlog))
+ if (reload_acl_and_cache(thd, lex->type, first_table, &write_to_binlog))
send_error(thd, 0);
else
{
@@ -3406,7 +3509,6 @@ purposes internal to the MySQL server", MYF(0));
*/
if (!lex->no_write_to_binlog && write_to_binlog)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -3418,7 +3520,7 @@ purposes internal to the MySQL server", MYF(0));
break;
}
case SQLCOM_KILL:
- kill_one_thread(thd,lex->thread_id);
+ kill_one_thread(thd,lex->thread_id, lex->type & ONLY_KILL_QUERY);
break;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
case SQLCOM_SHOW_GRANTS:
@@ -3432,34 +3534,37 @@ purposes internal to the MySQL server", MYF(0));
break;
#endif
case SQLCOM_HA_OPEN:
- if (check_db_used(thd,tables) ||
- check_table_access(thd,SELECT_ACL, tables,0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, SELECT_ACL, all_tables, 0))
goto error;
- res = mysql_ha_open(thd, tables);
+ res= mysql_ha_open(thd, first_table);
break;
case SQLCOM_HA_CLOSE:
- if (check_db_used(thd,tables))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables))
goto error;
- res = mysql_ha_close(thd, tables);
+ res= mysql_ha_close(thd, first_table);
break;
case SQLCOM_HA_READ:
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
/*
There is no need to check for table permissions here, because
if a user has no permissions to read a table, he won't be
able to open it (with SQLCOM_HA_OPEN) in the first place.
*/
- if (check_db_used(thd,tables))
+ if (check_db_used(thd, all_tables))
goto error;
- res = mysql_ha_read(thd, tables, lex->ha_read_mode, lex->backup_dir,
- lex->insert_list, lex->ha_rkey_mode, select_lex->where,
- select_lex->select_limit, select_lex->offset_limit);
+ res= mysql_ha_read(thd, first_table, lex->ha_read_mode, lex->backup_dir,
+ lex->insert_list, lex->ha_rkey_mode, select_lex->where,
+ select_lex->select_limit, select_lex->offset_limit);
break;
case SQLCOM_BEGIN:
if (thd->locked_tables)
{
thd->lock=thd->locked_tables;
- thd->locked_tables=0; // Will be automaticly closed
+ thd->locked_tables=0; // Will be automatically closed
close_thread_tables(thd); // Free tables
}
if (end_active_trans(thd))
@@ -3532,11 +3637,353 @@ purposes internal to the MySQL server", MYF(0));
else
res= -1;
break;
+ case SQLCOM_CREATE_PROCEDURE:
+ case SQLCOM_CREATE_SPFUNCTION:
+ {
+ uint namelen;
+ char *name;
+
+ if (!lex->sphead)
+ {
+ res= -1; // Shouldn't happen
+ break;
+ }
+
+ if (! lex->sphead->m_db.str)
+ {
+ send_error(thd,ER_NO_DB_ERROR);
+ delete lex->sphead;
+ lex->sphead= 0;
+ goto error;
+ }
+
+ name= lex->sphead->name(&namelen);
+#ifdef HAVE_DLOPEN
+ if (lex->sphead->m_type == TYPE_ENUM_FUNCTION)
+ {
+ udf_func *udf = find_udf(name, namelen);
+
+ if (udf)
+ {
+ net_printf(thd, ER_UDF_EXISTS, name);
+ delete lex->sphead;
+ lex->sphead= 0;
+ goto error;
+ }
+ }
+#endif
+ if (lex->sphead->m_type == TYPE_ENUM_FUNCTION &&
+ !lex->sphead->m_has_return)
+ {
+ net_printf(thd, ER_SP_NORETURN, name);
+ delete lex->sphead;
+ lex->sphead= 0;
+ goto error;
+ }
+
+ res= lex->sphead->create(thd);
+ switch (res) {
+ case SP_OK:
+ send_ok(thd);
+ lex->unit.cleanup();
+ delete lex->sphead;
+ lex->sphead= 0;
+ break;
+ case SP_WRITE_ROW_FAILED:
+ net_printf(thd, ER_SP_ALREADY_EXISTS, SP_TYPE_STRING(lex), name);
+ lex->unit.cleanup();
+ delete lex->sphead;
+ lex->sphead= 0;
+ goto error;
+ case SP_NO_DB_ERROR:
+ net_printf(thd, ER_BAD_DB_ERROR, lex->sphead->m_db.str);
+ lex->unit.cleanup();
+ delete lex->sphead;
+ lex->sphead= 0;
+ goto error;
+ default:
+ net_printf(thd, ER_SP_STORE_FAILED, SP_TYPE_STRING(lex), name);
+ lex->unit.cleanup();
+ delete lex->sphead;
+ lex->sphead= 0;
+ goto error;
+ }
+ break;
+ }
+ case SQLCOM_CALL:
+ {
+ sp_head *sp;
+
+ if (!(sp= sp_find_procedure(thd, lex->spname)))
+ {
+ net_printf(thd, ER_SP_DOES_NOT_EXIST, "PROCEDURE",
+ lex->spname->m_qname.str);
+ goto error;
+ }
+ else
+ {
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ st_sp_security_context save_ctx;
+#endif
+ ha_rows select_limit;
+ uint smrx;
+ LINT_INIT(smrx);
+
+ /* In case the arguments are subselects... */
+ if (all_tables &&
+ ((res= check_table_access(thd, SELECT_ACL, all_tables, 0)) ||
+ (res= open_and_lock_tables(thd, all_tables))))
+ {
+ break;
+ }
+
+#ifndef EMBEDDED_LIBRARY
+ /*
+ When executing substatements, they're assumed to send_error when
+ it happens, but not to send_ok.
+ */
+ my_bool nsok= thd->net.no_send_ok;
+ thd->net.no_send_ok= TRUE;
+#endif
+ if (sp->m_multi_results)
+ {
+ if (! (thd->client_capabilities & CLIENT_MULTI_RESULTS))
+ {
+ send_error(thd, ER_SP_BADSELECT);
+#ifndef EMBEDDED_LIBRARY
+ thd->net.no_send_ok= nsok;
+#endif
+ goto error;
+ }
+ smrx= thd->server_status & SERVER_MORE_RESULTS_EXISTS;
+ thd->server_status |= SERVER_MORE_RESULTS_EXISTS;
+ }
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ sp_change_security_context(thd, sp, &save_ctx);
+#endif
+ select_limit= thd->variables.select_limit;
+ thd->variables.select_limit= HA_POS_ERROR;
+
+ thd->row_count_func= 0;
+ res= sp->execute_procedure(thd, &lex->value_list);
+
+ thd->variables.select_limit= select_limit;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ sp_restore_security_context(thd, sp, &save_ctx);
+#endif
+
+#ifndef EMBEDDED_LIBRARY
+ thd->net.no_send_ok= nsok;
+#endif
+ if (sp->m_multi_results)
+ {
+ if (! smrx)
+ thd->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
+ }
+
+ if (res == 0)
+ send_ok(thd, (ulong) (thd->row_count_func < 0 ? 0 : thd->row_count_func));
+ else
+ goto error; // Substatement should already have sent error
+ }
+ break;
+ }
+ case SQLCOM_ALTER_PROCEDURE:
+ case SQLCOM_ALTER_FUNCTION:
+ {
+ sp_head *sp;
+ st_sp_chistics chistics;
+
+ memcpy(&chistics, &lex->sp_chistics, sizeof(chistics));
+ if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
+ sp= sp_find_procedure(thd, lex->spname);
+ else
+ sp= sp_find_function(thd, lex->spname);
+ mysql_reset_errors(thd);
+ if (! sp)
+ res= SP_KEY_NOT_FOUND;
+ else
+ {
+ if (check_sp_definer_access(thd, sp))
+ {
+ res= -1;
+ break;
+ }
+ memcpy(&lex->sp_chistics, &chistics, sizeof(lex->sp_chistics));
+ if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
+ res= sp_update_procedure(thd, lex->spname, &lex->sp_chistics);
+ else
+ res= sp_update_function(thd, lex->spname, &lex->sp_chistics);
+ }
+ switch (res)
+ {
+ case SP_OK:
+ send_ok(thd);
+ break;
+ case SP_KEY_NOT_FOUND:
+ net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex),
+ lex->spname->m_qname.str);
+ goto error;
+ default:
+ net_printf(thd, ER_SP_CANT_ALTER, SP_COM_STRING(lex),
+ lex->spname->m_qname.str);
+ goto error;
+ }
+ break;
+ }
+ case SQLCOM_DROP_PROCEDURE:
+ case SQLCOM_DROP_FUNCTION:
+ {
+ sp_head *sp;
+
+ if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
+ sp= sp_find_procedure(thd, lex->spname);
+ else
+ sp= sp_find_function(thd, lex->spname);
+ mysql_reset_errors(thd);
+ if (! sp)
+ res= SP_KEY_NOT_FOUND;
+ else
+ {
+ if (check_sp_definer_access(thd, sp))
+ {
+ res= -1;
+ break;
+ }
+ if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
+ res= sp_drop_procedure(thd, lex->spname);
+ else
+ {
+ res= sp_drop_function(thd, lex->spname);
+#ifdef HAVE_DLOPEN
+ if (res == SP_KEY_NOT_FOUND)
+ {
+ udf_func *udf = find_udf(lex->spname->m_name.str,
+ lex->spname->m_name.length);
+ if (udf)
+ {
+ if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 0))
+ goto error;
+ if (!(res = mysql_drop_function(thd,&lex->spname->m_name)))
+ {
+ send_ok(thd);
+ break;
+ }
+ }
+ }
+#endif
+ }
+ }
+ switch (res)
+ {
+ case SP_OK:
+ send_ok(thd);
+ break;
+ case SP_KEY_NOT_FOUND:
+ if (lex->drop_if_exists)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
+ SP_COM_STRING(lex), lex->spname->m_name.str);
+ res= 0;
+ send_ok(thd);
+ break;
+ }
+ net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex),
+ lex->spname->m_qname.str);
+ goto error;
+ default:
+ net_printf(thd, ER_SP_DROP_FAILED, SP_COM_STRING(lex),
+ lex->spname->m_qname.str);
+ goto error;
+ }
+ break;
+ }
+ case SQLCOM_SHOW_CREATE_PROC:
+ {
+ res= -1;
+ if (lex->spname->m_name.length > NAME_LEN)
+ {
+ net_printf(thd, ER_TOO_LONG_IDENT, lex->spname->m_name.str);
+ goto error;
+ }
+ res= sp_show_create_procedure(thd, lex->spname);
+ if (res != SP_OK)
+ { /* We don't distinguish between errors for now */
+ net_printf(thd, ER_SP_DOES_NOT_EXIST,
+ SP_COM_STRING(lex), lex->spname->m_name.str);
+ res= 0;
+ goto error;
+ }
+ break;
+ }
+ case SQLCOM_SHOW_CREATE_FUNC:
+ {
+ if (lex->spname->m_name.length > NAME_LEN)
+ {
+ net_printf(thd, ER_TOO_LONG_IDENT, lex->spname->m_name.str);
+ goto error;
+ }
+ res= sp_show_create_function(thd, lex->spname);
+ if (res != SP_OK)
+ { /* We don't distinguish between errors for now */
+ net_printf(thd, ER_SP_DOES_NOT_EXIST,
+ SP_COM_STRING(lex), lex->spname->m_name.str);
+ res= 0;
+ goto error;
+ }
+ res= 0;
+ break;
+ }
+ case SQLCOM_SHOW_STATUS_PROC:
+ {
+ res= sp_show_status_procedure(thd, (lex->wild ?
+ lex->wild->ptr() : NullS));
+ break;
+ }
+ case SQLCOM_SHOW_STATUS_FUNC:
+ {
+ res= sp_show_status_function(thd, (lex->wild ?
+ lex->wild->ptr() : NullS));
+ break;
+ }
+ case SQLCOM_CREATE_VIEW:
+ {
+ res= mysql_create_view(thd, thd->lex->create_view_mode);
+ break;
+ }
+ case SQLCOM_DROP_VIEW:
+ {
+ if (check_table_access(thd, DROP_ACL, all_tables, 0))
+ goto error;
+ if (end_active_trans(thd))
+ {
+ res= -1;
+ break;
+ }
+ res= mysql_drop_view(thd, first_table, thd->lex->drop_mode);
+ break;
+ }
+ case SQLCOM_CREATE_TRIGGER:
+ {
+ /* We don't care much about trigger body at that point */
+ delete lex->sphead;
+ lex->sphead= 0;
+
+ res= mysql_create_or_drop_trigger(thd, all_tables, 1);
+ break;
+ }
+ case SQLCOM_DROP_TRIGGER:
+ {
+ res= mysql_create_or_drop_trigger(thd, all_tables, 0);
+ break;
+ }
default: /* Impossible */
send_ok(thd);
break;
}
- thd->proc_info="query end"; // QQ
+ thd->proc_info="query end";
if (thd->one_shot_set)
{
/*
@@ -3561,11 +4008,38 @@ purposes internal to the MySQL server", MYF(0));
thd->one_shot_set= 0;
}
}
+
+ /*
+ The return value for ROW_COUNT() is "implementation dependent" if
+ the statement is not DELETE, INSERT or UPDATE (or a CALL executing
+ such a statement), but -1 is what JDBC and ODBC wants.
+ */
+ switch (lex->sql_command) {
+ case SQLCOM_UPDATE:
+ case SQLCOM_UPDATE_MULTI:
+ case SQLCOM_REPLACE:
+ case SQLCOM_INSERT:
+ case SQLCOM_REPLACE_SELECT:
+ case SQLCOM_INSERT_SELECT:
+ case SQLCOM_DELETE:
+ case SQLCOM_DELETE_MULTI:
+ case SQLCOM_CALL:
+ break;
+ default:
+ thd->row_count_func= -1;
+ }
+
+ /*
+ We end up here if res == 0 and send_ok() has been done,
+ or res != 0 and no send_error() has yet been done.
+ */
if (res < 0)
- send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
+ send_error(thd,thd->killed_errno());
+ DBUG_RETURN(res);
error:
- DBUG_VOID_RETURN;
+ /* We end up here if send_error() has already been done. */
+ DBUG_RETURN(-1);
}
@@ -3576,28 +4050,29 @@ error:
SYNOPSIS
check_one_table_access()
thd Thread handler
- privilege requested privelage
- tables table list of command
+ privilege requested privilege
+ all_tables global table list of query
RETURN
0 - OK
1 - access denied, error is sent to client
*/
-int check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables)
+bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables)
{
- if (check_access(thd, privilege, tables->db, &tables->grant.privilege,0,0))
+ if (check_access(thd, privilege, all_tables->db,
+ &all_tables->grant.privilege, 0, 0))
return 1;
/* Show only 1 table for check_grant */
- if (grant_option && check_grant(thd, privilege, tables, 0, 1, 0))
+ if (grant_option && check_grant(thd, privilege, all_tables, 0, 1, 0))
return 1;
/* Check rights on tables of subselect (if exists) */
TABLE_LIST *subselects_tables;
- if ((subselects_tables= tables->next))
+ if ((subselects_tables= all_tables->next_global))
{
- if ((check_table_access(thd, SELECT_ACL, subselects_tables,0)))
+ if ((check_table_access(thd, SELECT_ACL, subselects_tables, 0)))
return 1;
}
return 0;
@@ -3627,13 +4102,13 @@ bool
check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
bool dont_check_global_grants, bool no_errors)
{
- DBUG_ENTER("check_access");
- DBUG_PRINT("enter",("db: '%s' want_access: %lu master_access: %lu",
- db ? db : "", want_access, thd->master_access));
#ifndef NO_EMBEDDED_ACCESS_CHECKS
ulong db_access;
#endif
ulong dummy;
+ DBUG_ENTER("check_access");
+ DBUG_PRINT("enter",("db: %s want_access: %lu master_access: %lu",
+ db ? db : "", want_access, thd->master_access));
if (save_priv)
*save_priv=0;
else
@@ -3641,8 +4116,9 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
{
+ DBUG_PRINT("error",("No database"));
if (!no_errors)
- send_error(thd,ER_NO_DB_ERROR); /* purecov: tested */
+ send_error(thd,ER_NO_DB_ERROR); /* purecov: tested */
DBUG_RETURN(TRUE); /* purecov: tested */
}
@@ -3667,6 +4143,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
if (((want_access & ~thd->master_access) & ~(DB_ACLS | EXTRA_ACL)) ||
! db && dont_check_global_grants)
{ // We can never grant this
+ DBUG_PRINT("error",("No possible access"));
if (!no_errors)
net_printf(thd,ER_ACCESS_DENIED_ERROR,
thd->priv_user,
@@ -3686,13 +4163,17 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
DBUG_PRINT("info",("db_access: %lu", db_access));
/* Remove SHOW attribute and access rights we already have */
want_access &= ~(thd->master_access | EXTRA_ACL);
+ DBUG_PRINT("info",("db_access: %lu want_access: %lu",
+ db_access, want_access));
db_access= ((*save_priv=(db_access | thd->master_access)) & want_access);
/* grant_option is set if there exists a single table or column grant */
if (db_access == want_access ||
- ((grant_option && !dont_check_global_grants) &&
+ (grant_option && !dont_check_global_grants &&
!(want_access & ~(db_access | TABLE_ACLS))))
DBUG_RETURN(FALSE); /* Ok */
+
+ DBUG_PRINT("error",("Access denied"));
if (!no_errors)
net_printf(thd,ER_DBACCESS_DENIED_ERROR,
thd->priv_user,
@@ -3712,7 +4193,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
want_access Use should have any of these global rights
WARNING
- One gets access rigth if one has ANY of the rights in want_access
+ One gets access right if one has ANY of the rights in want_access
This is useful as one in most cases only need one global right,
but in some case we want to check if the user has SUPER or
REPL_CLIENT_ACL rights.
@@ -3750,7 +4231,7 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
uint found=0;
ulong found_access=0;
TABLE_LIST *org_tables=tables;
- for (; tables ; tables=tables->next)
+ for (; tables; tables= tables->next_global)
{
if (tables->derived ||
(tables->table && (int)tables->table->tmp_table) ||
@@ -3783,6 +4264,42 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
return FALSE;
}
+
+/*
+ Check if the given table has any of the asked privileges
+
+ SYNOPSIS
+ check_some_access()
+ thd Thread handler
+ want_access Bitmap of possible privileges to check for
+
+ RETURN
+ 0 ok
+ 1 error
+*/
+
+
+bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
+{
+ ulong access;
+ DBUG_ENTER("check_some_access");
+
+ /* This loop will work as long as we have less than 32 privileges */
+ for (access= 1; access < want_access ; access<<= 1)
+ {
+ if (access & want_access)
+ {
+ if (!check_access(thd, access, table->db,
+ &table->grant.privilege, 0, 1) &&
+ !grant_option || !check_grant(thd, access, table, 0, 1, 1))
+ DBUG_RETURN(0);
+ }
+ }
+ DBUG_PRINT("exit",("no matching access rights"));
+ DBUG_RETURN(1);
+}
+
+
bool check_merge_table_access(THD *thd, char *db,
TABLE_LIST *table_list)
{
@@ -3791,7 +4308,7 @@ bool check_merge_table_access(THD *thd, char *db,
{
/* Check that all tables use the current database */
TABLE_LIST *tmp;
- for (tmp=table_list; tmp ; tmp=tmp->next)
+ for (tmp= table_list; tmp; tmp= tmp->next_local)
{
if (!tmp->db || !tmp->db[0])
tmp->db=db;
@@ -3805,7 +4322,7 @@ bool check_merge_table_access(THD *thd, char *db,
static bool check_db_used(THD *thd,TABLE_LIST *tables)
{
- for (; tables ; tables=tables->next)
+ for (; tables; tables= tables->next_global)
{
if (!tables->db)
{
@@ -3819,6 +4336,41 @@ static bool check_db_used(THD *thd,TABLE_LIST *tables)
return FALSE;
}
+
+/*
+ Check if the given SP is owned by thd->priv_user/host, or priv_user is root.
+ QQ This is not quite complete, but it will do as a basic security check
+ for now. The question is exactly which rights should 'root' have?
+ Should root have access regardless of host for instance?
+
+ SYNOPSIS
+ check_sp_definer_access()
+ thd Thread handler
+ sp The SP pointer
+
+ RETURN
+ 0 ok
+ 1 error Error message has been sent
+*/
+
+static bool
+check_sp_definer_access(THD *thd, sp_head *sp)
+{
+ LEX_STRING *usr, *hst;
+
+ if (strcmp("root", thd->priv_user) == 0)
+ return FALSE; /* QQ Any root is ok now */
+ usr= &sp->m_definer_user;
+ hst= &sp->m_definer_host;
+ if (strncmp(thd->priv_user, usr->str, usr->length) == 0 &&
+ strncmp(thd->priv_host, hst->str, hst->length) == 0)
+ return FALSE; /* Both user and host must match */
+
+ my_error(ER_SP_ACCESS_DENIED_ERROR, MYF(0), sp->m_qname.str);
+ return TRUE; /* Not definer or root */
+}
+
+
/****************************************************************************
Check stack size; Send error if there isn't enough stack to continue
****************************************************************************/
@@ -3883,6 +4435,7 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
return 0;
}
+
/****************************************************************************
Initialize global thd variables needed for query
****************************************************************************/
@@ -3915,13 +4468,13 @@ void mysql_reset_thd_for_next_command(THD *thd)
DBUG_ENTER("mysql_reset_thd_for_next_command");
thd->free_list= 0;
thd->select_number= 1;
- thd->total_warn_count= 0; // Warnings for this query
+ thd->total_warn_count=0; // Warnings for this query
thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0;
thd->sent_row_count= thd->examined_row_count= 0;
thd->is_fatal_error= thd->rand_used= thd->time_zone_used= 0;
thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS |
- SERVER_QUERY_NO_INDEX_USED |
- SERVER_QUERY_NO_GOOD_INDEX_USED);
+ SERVER_QUERY_NO_INDEX_USED |
+ SERVER_QUERY_NO_GOOD_INDEX_USED);
thd->tmp_table_used= 0;
if (opt_bin_log)
reset_dynamic(&thd->user_var_events);
@@ -3940,7 +4493,6 @@ mysql_init_select(LEX *lex)
{
DBUG_ASSERT(lex->result == 0);
lex->exchange= 0;
- lex->proc_list.first= 0;
}
}
@@ -3954,6 +4506,7 @@ mysql_new_select(LEX *lex, bool move_down)
select_lex->select_number= ++lex->thd->select_number;
select_lex->init_query();
select_lex->init_select();
+ select_lex->parent_lex= lex;
if (move_down)
{
/* first select_lex of subselect or derived table */
@@ -3969,10 +4522,15 @@ mysql_new_select(LEX *lex, bool move_down)
unit->link_prev= 0;
unit->return_to= lex->current_select;
select_lex->include_down(unit);
- // TODO: assign resolve_mode for fake subquery after merging with new tree
+ /* TODO: assign resolve_mode for fake subquery after merging with new tree */
}
else
{
+ if (lex->current_select->order_list.first && !lex->current_select->braces)
+ {
+ net_printf(lex->thd, ER_WRONG_USAGE, "UNION", "ORDER BY");
+ return 1;
+ }
select_lex->include_neighbour(lex->current_select);
SELECT_LEX_UNIT *unit= select_lex->master_unit();
SELECT_LEX *fake= unit->fake_select_lex;
@@ -4040,6 +4598,8 @@ void mysql_init_multi_delete(LEX *lex)
lex->select_lex.select_limit= lex->unit.select_limit_cnt=
HA_POS_ERROR;
lex->select_lex.table_list.save_and_clear(&lex->auxilliary_table_list);
+ lex->query_tables= 0;
+ lex->query_tables_last= &lex->query_tables;
}
@@ -4068,22 +4628,42 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
#endif
{
if (thd->net.report_error)
+ {
send_error(thd, 0, NullS);
+ if (thd->lex->sphead)
+ {
+ if (lex != thd->lex)
+ thd->lex->sphead->restore_lex(thd);
+ delete thd->lex->sphead;
+ thd->lex->sphead= NULL;
+ }
+ }
else
{
mysql_execute_command(thd);
query_cache_end_of_result(thd);
}
}
+ lex->unit.cleanup();
}
else
{
DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
thd->is_fatal_error));
query_cache_abort(&thd->net);
+ lex->unit.cleanup();
+ if (thd->lex->sphead)
+ {
+ /* Clean up after failed stored procedure/function */
+ if (lex != thd->lex)
+ thd->lex->sphead->restore_lex(thd);
+ delete thd->lex->sphead;
+ thd->lex->sphead= NULL;
+ }
}
thd->proc_info="freeing items";
thd->end_statement();
+ thd->cleanup_after_query();
DBUG_ASSERT(thd->change_list.is_empty());
}
DBUG_VOID_RETURN;
@@ -4104,17 +4684,20 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length)
{
LEX *lex= thd->lex;
bool error= 0;
+ DBUG_ENTER("mysql_test_parse_for_slave");
mysql_init_query(thd, (uchar*) inBuf, length);
if (!yyparse((void*) thd) && ! thd->is_fatal_error &&
all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
- error= 1; /* Ignore question */
+ error= 1; /* Ignore question */
thd->end_statement();
- return error;
+ thd->cleanup_after_query();
+ DBUG_RETURN(error);
}
#endif
+
/*
Calculate interval lengths.
Strip trailing spaces from all strings.
@@ -4156,6 +4739,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
register create_field *new_field;
LEX *lex= thd->lex;
uint allowed_type_modifier=0;
+ uint sign_len;
char warn_buff[MYSQL_ERRMSG_SIZE];
DBUG_ENTER("add_field_to_list");
@@ -4247,9 +4831,19 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
new_field->comment.str= (char*) comment->str;
new_field->comment.length=comment->length;
}
+ /*
+ Set flag if this field doesn't have a default value
+ Enum values has always the first value as a default (set in
+ make_empty_rec().
+ */
+ if (!default_value && !(type_modifier & AUTO_INCREMENT_FLAG) &&
+ (type_modifier & NOT_NULL_FLAG) && type != FIELD_TYPE_TIMESTAMP &&
+ type != FIELD_TYPE_ENUM)
+ new_field->flags|= NO_DEFAULT_VALUE_FLAG;
+
if (length && !(new_field->length= (uint) atoi(length)))
length=0; /* purecov: inspected */
- uint sign_len=type_modifier & UNSIGNED_FLAG ? 0 : 1;
+ sign_len=type_modifier & UNSIGNED_FLAG ? 0 : 1;
if (new_field->length && new_field->decimals &&
new_field->length < new_field->decimals+1 &&
@@ -4302,7 +4896,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
new_field->sql_type= FIELD_TYPE_BLOB;
sprintf(warn_buff, ER(ER_AUTO_CONVERT), field_name, "CHAR",
(cs == &my_charset_bin) ? "BLOB" : "TEXT");
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_AUTO_CONVERT,
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_AUTO_CONVERT,
warn_buff);
/* fall through */
case FIELD_TYPE_BLOB:
@@ -4423,11 +5017,11 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
If we have TIMESTAMP NULL column without explicit DEFAULT value
we treat it as having DEFAULT NULL attribute.
*/
- new_field->unireg_check= on_update_value ?
- Field::TIMESTAMP_UN_FIELD :
- (new_field->flags & NOT_NULL_FLAG ?
- Field::TIMESTAMP_OLD_FIELD:
- Field::NONE);
+ new_field->unireg_check= (on_update_value ?
+ Field::TIMESTAMP_UN_FIELD :
+ (new_field->flags & NOT_NULL_FLAG ?
+ Field::TIMESTAMP_OLD_FIELD:
+ Field::NONE));
}
break;
case FIELD_TYPE_DATE: // Old date type
@@ -4601,6 +5195,7 @@ bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc)
order->asc = asc;
order->free_me=0;
order->used=0;
+ order->counter_used= 0;
list.link_in_list((byte*) order,(byte**) &order->next);
DBUG_RETURN(0);
}
@@ -4636,6 +5231,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
{
register TABLE_LIST *ptr;
char *alias_str;
+ LEX *lex= thd->lex;
DBUG_ENTER("add_table_to_list");
if (!table)
@@ -4689,6 +5285,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX);
ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES);
ptr->derived= table->sel;
+ ptr->select_lex= lex->current_select;
ptr->cacheable_table= 1;
if (use_index_arg)
ptr->use_index=(List<String> *) thd->memdup((gptr) use_index_arg,
@@ -4702,7 +5299,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
{
for (TABLE_LIST *tables=(TABLE_LIST*) table_list.first ;
tables ;
- tables=tables->next)
+ tables=tables->next_local)
{
if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) &&
!strcmp(ptr->db, tables->db))
@@ -4712,12 +5309,246 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
}
}
}
- table_list.link_in_list((byte*) ptr, (byte**) &ptr->next);
+ /* Link table in local list (list for current select) */
+ table_list.link_in_list((byte*) ptr, (byte**) &ptr->next_local);
+ /* Link table in global list (all used tables) */
+ lex->add_to_query_tables(ptr);
+ DBUG_RETURN(ptr);
+}
+
+
+/*
+ Initialize a new table list for a nested join
+
+ SYNOPSIS
+ init_table_list()
+ thd current thread
+
+ DESCRIPTION
+ The function initializes a structure of the TABLE_LIST type
+ for a nested join. It sets up its nested join list as empty.
+ The created structure is added to the front of the current
+ join list in the st_select_lex object. Then the function
+ changes the current nest level for joins to refer to the newly
+ created empty list after having saved the info on the old level
+ in the initialized structure.
+
+ RETURN VALUE
+ 0, if success
+ 1, otherwise
+*/
+
+bool st_select_lex::init_nested_join(THD *thd)
+{
+ TABLE_LIST *ptr;
+ NESTED_JOIN *nested_join;
+ DBUG_ENTER("init_nested_join");
+
+ if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))) ||
+ !(nested_join= ptr->nested_join=
+ (NESTED_JOIN *) thd->calloc(sizeof(NESTED_JOIN))))
+ DBUG_RETURN(1);
+ join_list->push_front(ptr);
+ ptr->embedding= embedding;
+ ptr->join_list= join_list;
+ embedding= ptr;
+ join_list= &nested_join->join_list;
+ join_list->empty();
+ DBUG_RETURN(0);
+}
+
+
+/*
+ End a nested join table list
+
+ SYNOPSIS
+ end_nested_join()
+ thd current thread
+
+ DESCRIPTION
+ The function returns to the previous join nest level.
+ If the current level contains only one member, the function
+ moves it one level up, eliminating the nest.
+
+ RETURN VALUE
+ Pointer to TABLE_LIST element added to the total table list, if success
+ 0, otherwise
+*/
+
+TABLE_LIST *st_select_lex::end_nested_join(THD *thd)
+{
+ TABLE_LIST *ptr;
+ DBUG_ENTER("end_nested_join");
+ ptr= embedding;
+ join_list= ptr->join_list;
+ embedding= ptr->embedding;
+ NESTED_JOIN *nested_join= ptr->nested_join;
+ if (nested_join->join_list.elements == 1)
+ {
+ TABLE_LIST *embedded= nested_join->join_list.head();
+ join_list->pop();
+ embedded->join_list= join_list;
+ embedded->embedding= embedding;
+ join_list->push_front(embedded);
+ ptr= embedded;
+ }
DBUG_RETURN(ptr);
}
/*
+ Nest last join operation
+
+ SYNOPSIS
+ nest_last_join()
+ thd current thread
+
+ DESCRIPTION
+ The function nest last join operation as if it was enclosed in braces.
+
+ RETURN VALUE
+ Pointer to TABLE_LIST element created for the new nested join, if success
+ 0, otherwise
+*/
+
+TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
+{
+ TABLE_LIST *ptr;
+ NESTED_JOIN *nested_join;
+ DBUG_ENTER("nest_last_join");
+
+ if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))) ||
+ !(nested_join= ptr->nested_join=
+ (NESTED_JOIN *) thd->calloc(sizeof(NESTED_JOIN))))
+ DBUG_RETURN(0);
+ ptr->embedding= embedding;
+ ptr->join_list= join_list;
+ List<TABLE_LIST> *embedded_list= &nested_join->join_list;
+ embedded_list->empty();
+ for (int i=0; i < 2; i++)
+ {
+ TABLE_LIST *table= join_list->pop();
+ table->join_list= embedded_list;
+ table->embedding= ptr;
+ embedded_list->push_back(table);
+ }
+ join_list->push_front(ptr);
+ nested_join->used_tables= nested_join->not_null_tables= (table_map) 0;
+ DBUG_RETURN(ptr);
+}
+
+
+/*
+ Save names for a join with using clause
+
+ SYNOPSIS
+ save_names_for_using_list
+ tab1 left table in join
+ tab2 right table in join
+
+ DESCRIPTION
+ The function saves the full names of the tables in st_select_lex
+ to be able to build later an on expression to replace the using clause.
+
+ RETURN VALUE
+ None
+*/
+
+void st_select_lex::save_names_for_using_list(TABLE_LIST *tab1,
+ TABLE_LIST *tab2)
+{
+ while (tab1->nested_join)
+ {
+ tab1= tab1->nested_join->join_list.head();
+ }
+ db1= tab1->db;
+ table1= tab1->alias;
+ while (tab2->nested_join)
+ {
+ TABLE_LIST *next;
+ List_iterator_fast<TABLE_LIST> it(tab2->nested_join->join_list);
+ tab2= it++;
+ while ((next= it++))
+ tab2= next;
+ }
+ db2= tab2->db;
+ table2= tab2->alias;
+}
+
+
+/*
+ Add a table to the current join list
+
+ SYNOPSIS
+ add_joined_table()
+ table the table to add
+
+ DESCRIPTION
+ The function puts a table in front of the current join list
+ of st_select_lex object.
+ Thus, joined tables are put into this list in the reverse order
+ (the most outer join operation follows first).
+
+ RETURN VALUE
+ None
+*/
+
+void st_select_lex::add_joined_table(TABLE_LIST *table)
+{
+ DBUG_ENTER("add_joined_table");
+ join_list->push_front(table);
+ table->join_list= join_list;
+ table->embedding= embedding;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Convert a right join into equivalent left join
+
+ SYNOPSIS
+ convert_right_join()
+ thd current thread
+
+ DESCRIPTION
+ The function takes the current join list t[0],t[1] ... and
+ effectively converts it into the list t[1],t[0] ...
+ Although the outer_join flag for the new nested table contains
+ JOIN_TYPE_RIGHT, it will be handled as the inner table of a left join
+ operation.
+
+ EXAMPLES
+ SELECT * FROM t1 RIGHT JOIN t2 ON on_expr =>
+ SELECT * FROM t2 LEFT JOIN t1 ON on_expr
+
+ SELECT * FROM t1,t2 RIGHT JOIN t3 ON on_expr =>
+ SELECT * FROM t1,t3 LEFT JOIN t2 ON on_expr
+
+ SELECT * FROM t1,t2 RIGHT JOIN (t3,t4) ON on_expr =>
+ SELECT * FROM t1,(t3,t4) LEFT JOIN t2 ON on_expr
+
+ SELECT * FROM t1 LEFT JOIN t2 ON on_expr1 RIGHT JOIN t3 ON on_expr2 =>
+ SELECT * FROM t3 LEFT JOIN (t1 LEFT JOIN t2 ON on_expr2) ON on_expr1
+
+ RETURN
+ Pointer to the table representing the inner table, if success
+ 0, otherwise
+*/
+
+TABLE_LIST *st_select_lex::convert_right_join()
+{
+ TABLE_LIST *tab2= join_list->pop();
+ TABLE_LIST *tab1= join_list->pop();
+ DBUG_ENTER("convert_right_join");
+
+ join_list->push_front(tab2);
+ join_list->push_front(tab1);
+ tab1->outer_join|= JOIN_TYPE_RIGHT;
+
+ DBUG_RETURN(tab1);
+}
+
+/*
Set lock for all tables in current select level
SYNOPSIS:
@@ -4737,9 +5568,9 @@ void st_select_lex::set_lock_for_tables(thr_lock_type lock_type)
DBUG_PRINT("enter", ("lock_type: %d for_update: %d", lock_type,
for_update));
- for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first ;
- tables ;
- tables=tables->next)
+ for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first;
+ tables;
+ tables= tables->next_local)
{
tables->lock_type= lock_type;
tables->updating= for_update;
@@ -4756,7 +5587,7 @@ void add_join_on(TABLE_LIST *b,Item *expr)
b->on_expr=expr;
else
{
- // This only happens if you have both a right and left join
+ /* This only happens if you have both a right and left join */
b->on_expr=new Item_cond_and(b->on_expr,expr);
}
b->on_expr->top_level_item();
@@ -4771,7 +5602,7 @@ void add_join_on(TABLE_LIST *b,Item *expr)
add_join_natural()
a Table to do normal join with
b Do normal join with this table
-
+
IMPLEMENTATION
This function just marks that table b should be joined with a.
The function setup_cond() will create in b->on_expr a list
@@ -4836,7 +5667,6 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
*/
tmp_write_to_binlog= 0;
mysql_log.new_file(1);
- mysql_update_log.new_file(1);
mysql_bin_log.new_file(1);
mysql_slow_log.new_file(1);
#ifdef HAVE_REPLICATION
@@ -4859,7 +5689,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
if (options & REFRESH_QUERY_CACHE_FREE)
{
query_cache.pack(); // FLUSH QUERY CACHE
- options &= ~REFRESH_QUERY_CACHE; //don't flush all cache, just free memory
+ options &= ~REFRESH_QUERY_CACHE; // Don't flush cache, just free memory
}
if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE))
{
@@ -4939,7 +5769,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
This is written such that we have a short lock on LOCK_thread_count
*/
-void kill_one_thread(THD *thd, ulong id)
+void kill_one_thread(THD *thd, ulong id, bool only_kill_query)
{
THD *tmp;
uint error=ER_NO_SUCH_THREAD;
@@ -4959,7 +5789,7 @@ void kill_one_thread(THD *thd, ulong id)
if ((thd->master_access & SUPER_ACL) ||
!strcmp(thd->user,tmp->user))
{
- tmp->awake(1 /*prepare to die*/);
+ tmp->awake(only_kill_query ? THD::KILL_QUERY : THD::KILL_CONNECTION);
error=0;
}
else
@@ -4993,6 +5823,13 @@ static void refresh_status(void)
(char*) &dflt_key_cache_var));
*(ulong*) value= 0;
}
+ else if (ptr->type == SHOW_LONG_STATUS)
+ {
+ THD *thd= current_thd;
+ /* We must update the global status before cleaning up the thread */
+ add_to_status(&global_status_var, &thd->status_var);
+ bzero((char*) &thd->status_var, sizeof(thd->status_var));
+ }
}
pthread_mutex_unlock(&LOCK_status);
}
@@ -5137,7 +5974,6 @@ int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
List<create_field> fields;
ALTER_INFO alter_info;
alter_info.flags= ALTER_ADD_INDEX;
- alter_info.is_simple= 0;
HA_CREATE_INFO create_info;
DBUG_ENTER("mysql_create_index");
bzero((char*) &create_info,sizeof(create_info));
@@ -5161,7 +5997,6 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info)
create_info.default_table_charset= thd->variables.collation_database;
alter_info->clear();
alter_info->flags= ALTER_DROP_INDEX;
- alter_info->is_simple= 0;
DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name,
&create_info, table_list,
fields, keys, 0, (ORDER*)0,
@@ -5175,7 +6010,7 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info)
SYNOPSIS
multi_update_precheck()
thd Thread handler
- tables Global table list
+ tables Global/local table list (have to be the same)
RETURN VALUE
0 OK
@@ -5185,12 +6020,11 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info)
int multi_update_precheck(THD *thd, TABLE_LIST *tables)
{
- DBUG_ENTER("multi_update_precheck");
const char *msg= 0;
TABLE_LIST *table;
LEX *lex= thd->lex;
SELECT_LEX *select_lex= &lex->select_lex;
- TABLE_LIST *update_list= (TABLE_LIST*)select_lex->table_list.first;
+ DBUG_ENTER("multi_update_precheck");
if (select_lex->item_list.elements != lex->value_list.elements)
{
@@ -5201,7 +6035,7 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables)
Ensure that we have UPDATE or SELECT privilege for each table
The exact privilege is checked in mysql_multi_update()
*/
- for (table= update_list; table; table= table->next)
+ for (table= tables; table; table= table->next_local)
{
if (table->derived)
table->grant.privilege= SELECT_ACL;
@@ -5209,17 +6043,12 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables)
&table->grant.privilege, 0, 1) ||
grant_option &&
check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) &&
- (check_access(thd, SELECT_ACL, table->db,
- &table->grant.privilege, 0, 0) ||
- grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0)))
+ (check_access(thd, SELECT_ACL, table->db,
+ &table->grant.privilege, 0, 0) ||
+ grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0)))
DBUG_RETURN(1);
- /*
- We assign following flag only to copy of table, because it will
- be checked only if query contains subqueries i.e. only if copy exists
- */
- if (table->table_list)
- table->table_list->table_in_update_from_clause= 1;
+ table->table_in_first_from_clause= 1;
}
/*
Is there tables of subqueries?
@@ -5227,19 +6056,9 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables)
if (&lex->select_lex != lex->all_selects_list)
{
DBUG_PRINT("info",("Checking sub query list"));
- for (table= tables; table; table= table->next)
+ for (table= tables; table; table= table->next_global)
{
- if (table->table_in_update_from_clause)
- {
- /*
- If we check table by local TABLE_LIST copy then we should copy
- grants to global table list, because it will be used for table
- opening.
- */
- if (table->table_list)
- table->grant= table->table_list->grant;
- }
- else if (!table->derived)
+ if (!table->table_in_first_from_clause && table->derived)
{
if (check_access(thd, SELECT_ACL, table->db,
&table->grant.privilege, 0, 0) ||
@@ -5268,7 +6087,7 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables)
SYNOPSIS
multi_delete_precheck()
thd Thread handler
- tables Global table list
+ tables Global/local table list
table_count Pointer to table counter
RETURN VALUE
@@ -5278,12 +6097,11 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables)
*/
int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count)
{
- DBUG_ENTER("multi_delete_precheck");
SELECT_LEX *select_lex= &thd->lex->select_lex;
TABLE_LIST *aux_tables=
(TABLE_LIST *)thd->lex->auxilliary_table_list.first;
- TABLE_LIST *delete_tables= (TABLE_LIST *)select_lex->table_list.first;
TABLE_LIST *target_tbl;
+ DBUG_ENTER("multi_delete_precheck");
*table_count= 0;
@@ -5298,12 +6116,12 @@ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count)
my_error(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE, MYF(0));
DBUG_RETURN(-1);
}
- for (target_tbl= aux_tables; target_tbl; target_tbl= target_tbl->next)
+ for (target_tbl= aux_tables; target_tbl; target_tbl= target_tbl->next_local)
{
(*table_count)++;
/* All tables in aux_tables must be found in FROM PART */
TABLE_LIST *walk;
- for (walk= delete_tables; walk; walk= walk->next)
+ for (walk= tables; walk; walk= walk->next_local)
{
if (!my_strcasecmp(table_alias_charset,
target_tbl->alias, walk->alias) &&
@@ -5316,14 +6134,8 @@ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count)
"MULTI DELETE");
DBUG_RETURN(-1);
}
- if (walk->derived)
- {
- my_error(ER_NON_UPDATABLE_TABLE, MYF(0), target_tbl->real_name,
- "DELETE");
- DBUG_RETURN(-1);
- }
walk->lock_type= target_tbl->lock_type;
- target_tbl->table_list= walk; // Remember corresponding table
+ target_tbl->correspondent_table= walk; // Remember corresponding table
}
DBUG_RETURN(0);
}
@@ -5427,9 +6239,9 @@ int insert_precheck(THD *thd, TABLE_LIST *tables)
LEX *lex= thd->lex;
DBUG_ENTER("insert_precheck");
- ulong privilege= INSERT_ACL |
- (lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) |
- (lex->duplicates == DUP_UPDATE ? UPDATE_ACL : 0);
+ ulong privilege= (INSERT_ACL |
+ (lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) |
+ (lex->value_list.elements ? UPDATE_ACL : 0));
if (check_one_table_access(thd, privilege, tables))
DBUG_RETURN(1);
@@ -5484,33 +6296,24 @@ int create_table_precheck(THD *thd, TABLE_LIST *tables,
/* Check permissions for used tables in CREATE TABLE ... SELECT */
/*
- For temporary tables or PREPARED STATEMETNS we don't have to check
- if the created table exists
+ Only do the check for PS, becasue we on execute we have to check that
+ against the opened tables to ensure we don't use a table that is part
+ of the view (which can only be done after the table has been opened).
*/
- if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&
- ! thd->current_arena->is_stmt_prepare() &&
- find_real_table_in_list(tables, create_table->db,
- create_table->real_name))
- {
- net_printf(thd,ER_UPDATE_TABLE_USED, create_table->real_name);
-
- goto err;
- }
- if (lex->create_info.used_fields & HA_CREATE_USED_UNION)
+ if (thd->current_arena->is_stmt_prepare())
{
- TABLE_LIST *tab;
- for (tab= tables; tab; tab= tab->next)
+ /*
+ For temporary tables we don't have to check if the created table exists
+ */
+ if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&
+ find_table_in_global_list(tables, create_table->db,
+ create_table->real_name))
{
- if (find_real_table_in_list((TABLE_LIST*) lex->create_info.
- merge_list.first,
- tables->db, tab->real_name))
- {
- net_printf(thd, ER_UPDATE_TABLE_USED, tab->real_name);
- goto err;
- }
- }
- }
+ net_printf(thd,ER_UPDATE_TABLE_USED, create_table->real_name);
+ goto err;
+ }
+ }
if (tables && check_table_access(thd, SELECT_ACL, tables,0))
goto err;
}
@@ -5526,7 +6329,7 @@ err:
SYNOPSIS
negate_expression()
- thd therad handler
+ thd thread handler
expr expression for negation
RETURN
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index d842b4b66bb..fe5ce7640ea 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -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
@@ -1063,19 +1138,1267 @@ create:
lex->name=$4.str;
lex->create_info.options=$3;
}
- | CREATE udf_func_type UDF_SYM IDENT_sys
+ | CREATE udf_func_type FUNCTION_SYM sp_name
{
LEX *lex=Lex;
- lex->sql_command = SQLCOM_CREATE_FUNCTION;
- lex->udf.name = $4;
+ lex->spname= $4;
lex->udf.type= $2;
}
- UDF_RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING_sys
+ 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->udf.returns=(Item_result) $7;
- lex->udf.dl=$9.str;
+ 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);
+ }
+ ;
+
+sp_opt_fetch_noise:
+ /* Empty */
+ | NEXT_SYM FROM
+ | FROM
+ ;
+
+sp_fetch_list:
+ ident
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *spc= lex->spcont;
+ sp_pvar_t *spv;
+
+ if (!spc || !(spv = spc->find_pvar(&$1)))
+ {
+ net_printf(YYTHD, ER_SP_UNDECLARED_VAR, $1.str);
+ YYABORT;
+ }
+ else
+ {
+ /* An SP local variable */
+ sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction();
+
+ i->add_to_varlist(spv);
+ spv->isset= TRUE;
+ }
+ }
+ |
+ sp_fetch_list ',' ident
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *spc= lex->spcont;
+ sp_pvar_t *spv;
+
+ if (!spc || !(spv = spc->find_pvar(&$3)))
+ {
+ net_printf(YYTHD, ER_SP_UNDECLARED_VAR, $3.str);
+ YYABORT;
+ }
+ else
+ {
+ /* An SP local variable */
+ sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction();
+
+ i->add_to_varlist(spv);
+ spv->isset= TRUE;
+ }
+ }
+ ;
+
+sp_if:
+ expr THEN_SYM
+ {
+ 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);
+ }
+ sp_proc_stmts1
+ {
+ 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));
+ }
+ sp_elseifs
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->backpatch(lex->spcont->pop_label());
+ }
+ ;
+
+sp_elseifs:
+ /* Empty */
+ | ELSEIF_SYM sp_if
+ | ELSE sp_proc_stmts1
+ ;
+
+sp_case:
+ expr THEN_SYM
+ {
+ 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;
+ }
+ 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
+ {
+ 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));
+ }
+ sp_whens
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->backpatch(lex->spcont->pop_label());
+ }
+ ;
+
+sp_whens:
+ /* Empty */
+ {
+ 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 {}
+ ;
+
+sp_labeled_control:
+ IDENT ':'
+ {
+ LEX *lex= Lex;
+ sp_pcontext *ctx= lex->spcont;
+ sp_label_t *lab= ctx->find_label($1.str);
+
+ if (lab)
+ {
+ net_printf(YYTHD, ER_SP_LABEL_REDEFINE, $1.str);
+ YYABORT;
+ }
+ else
+ {
+ 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->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;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+
+ 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();
+ }
+ | LOOP_SYM
+ sp_proc_stmts1 END LOOP_SYM
+ {
+ 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);
+ }
+ | WHILE_SYM expr DO_SYM
+ {
+ 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);
+ }
+ sp_proc_stmts1 END WHILE_SYM
+ {
+ 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);
+ }
+ | REPEAT_SYM sp_proc_stmts1 UNTIL_SYM expr END REPEAT_SYM
+ {
+ 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:
@@ -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);
};
@@ -1869,8 +3202,7 @@ alter:
lex->create_info.db_type= DB_TYPE_DEFAULT;
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.reset();
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; }
@@ -1895,27 +3267,27 @@ alter_list:
| alter_list ',' alter_list_item;
add_column:
- ADD opt_column
+ ADD opt_column
{
LEX *lex=Lex;
- lex->change=0;
- lex->alter_info.flags|= ALTER_ADD_COLUMN;
+ lex->change=0;
+ lex->alter_info.flags|= ALTER_ADD_COLUMN;
};
alter_list_item:
- add_column column_def opt_place { Lex->alter_info.is_simple= 0; }
- | ADD key_def
- {
- LEX *lex=Lex;
- lex->alter_info.is_simple= 0;
- lex->alter_info.flags|= ALTER_ADD_INDEX;
+ add_column column_def opt_place { }
+ | ADD key_def
+ {
+ Lex->alter_info.flags|= ALTER_ADD_INDEX;
}
- | add_column '(' field_list ')' { Lex->alter_info.is_simple= 0; }
+ | add_column '(' field_list ')'
+ {
+ Lex->alter_info.flags|= ALTER_ADD_COLUMN | ALTER_ADD_INDEX;
+ }
| CHANGE opt_column field_ident
{
LEX *lex=Lex;
- lex->change= $3.str;
- lex->alter_info.is_simple= 0;
+ lex->change= $3.str;
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
@@ -1946,17 +3317,18 @@ 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;
+ $3.str));
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
{
@@ -2012,22 +3391,20 @@ alter_list_item:
YYABORT;
}
LEX *lex= Lex;
- lex->create_info.table_charset=
+ lex->create_info.table_charset=
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
+ | create_table_options_space_separated
{
LEX *lex=Lex;
- lex->alter_info.is_simple= 0;
lex->alter_info.flags|= ALTER_OPTIONS;
}
- | order_clause
+ | 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 */ {}
@@ -2060,7 +3438,7 @@ opt_to:
*/
slave:
- START_SYM SLAVE slave_thread_opts
+ START_SYM SLAVE slave_thread_opts
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_SLAVE_START;
@@ -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,48 +4514,90 @@ simple_expr:
{ $$= new Item_func_round($3,$5,1); }
| TRUE_SYM
{ $$= new Item_int((char*) "TRUE",1,1); }
- | UDA_CHAR_SUM '(' udf_expr_list ')'
- {
- if ($3 != NULL)
- $$ = new Item_sum_udf_str($1, *$3);
- else
- $$ = new Item_sum_udf_str($1);
- }
- | UDA_FLOAT_SUM '(' udf_expr_list ')'
- {
- if ($3 != NULL)
- $$ = new Item_sum_udf_float($1, *$3);
- else
- $$ = new Item_sum_udf_float($1);
- }
- | UDA_INT_SUM '(' udf_expr_list ')'
- {
- if ($3 != NULL)
- $$ = new Item_sum_udf_int($1, *$3);
- else
- $$ = new Item_sum_udf_int($1);
- }
- | UDF_CHAR_FUNC '(' udf_expr_list ')'
- {
- if ($3 != NULL)
- $$ = new Item_func_udf_str($1, *$3);
- else
- $$ = new Item_func_udf_str($1);
- }
- | UDF_FLOAT_FUNC '(' udf_expr_list ')'
+ | ident '.' ident '(' udf_expr_list ')'
{
- if ($3 != NULL)
- $$ = new Item_func_udf_float($1, *$3);
- else
- $$ = new Item_func_udf_float($1);
- }
- | UDF_INT_FUNC '(' udf_expr_list ')'
- {
- if ($3 != NULL)
- $$ = new Item_func_udf_int($1, *$3);
+ 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_udf_int($1);
+ $$= 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(udf, *$3);
+ else
+ $$ = new Item_sum_udf_str(udf);
+ }
+ break;
+ case REAL_RESULT:
+ if (udf->type == UDFTYPE_FUNCTION)
+ {
+ if ($3 != NULL)
+ $$ = new Item_func_udf_float(udf, *$3);
+ else
+ $$ = new Item_func_udf_float(udf);
+ }
+ else
+ {
+ if ($3 != NULL)
+ $$ = new Item_sum_udf_float(udf, *$3);
+ else
+ $$ = new Item_sum_udf_float(udf);
+ }
+ break;
+ case INT_RESULT:
+ if (udf->type == UDFTYPE_FUNCTION)
+ {
+ if ($3 != NULL)
+ $$ = new Item_func_udf_int(udf, *$3);
+ else
+ $$ = new Item_func_udf_int(udf);
+ }
+ else
+ {
+ if ($3 != NULL)
+ $$ = new Item_sum_udf_int(udf, *$3);
+ else
+ $$ = new Item_sum_udf_int(udf);
+ }
+ break;
+ default:
+ YYABORT;
+ }
+ }
+ else
+#endif /* HAVE_DLOPEN */
+ {
+ 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_sp(name);
+ }
+ }
| UNIQUE_USERS '(' text_literal ',' NUM ',' NUM ',' expr_list ')'
{
$$= new Item_func_unique_users($3,atoi($5.str),atoi($7.str), * $9);
@@ -3261,8 +4705,37 @@ fulltext_options:
;
udf_expr_list:
- /* empty */ { $$= NULL; }
- | expr_list { $$= $1;};
+ /* empty */ { $$= NULL; }
+ | 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);
};
+table_ref:
+ table_factor { $$=$1; }
+ | join_table { $$=$1; }
+ {
+ LEX *lex= Lex;
+ if (!($$= lex->current_select->nest_last_join(lex->thd)))
+ YYABORT;
+ }
+ ;
+
join_table_list:
- '(' join_table_list ')' { $$=$2; }
- | 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
+ 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
+ {
+ 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($1->next,$1);
- $1->outer_join|=JOIN_TYPE_RIGHT;
- $$=$6;
+ 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($$);
}
- | '{' ident join_table LEFT OUTER JOIN_SYM join_table ON expr '}'
+ | '('
+ {
+ 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 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; }
;
@@ -3671,8 +5196,11 @@ having_clause:
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,12 +5413,33 @@ 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;
+ }
+ }
;
into:
@@ -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,7 +6079,29 @@ 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:
STATUS_SYM
@@ -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:
@@ -4925,28 +6567,108 @@ order_ident:
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;
- SELECT_LEX *sel= lex->current_select;
- if (sel->no_table_names_allowed)
- {
- my_printf_error(ER_TABLENAME_NOT_ALLOWED_HERE,
- ER(ER_TABLENAME_NOT_ALLOWED_HERE),
- MYF(0), $1.str, thd->where);
- }
- $$= (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);
- }
+
+ /*
+ 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)
+ {
+ my_printf_error(ER_TABLENAME_NOT_ALLOWED_HERE,
+ ER(ER_TABLENAME_NOT_ALLOWED_HERE),
+ MYF(0), $1.str, thd->where);
+ }
+ $$= (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);
+ }
+ }
| '.' ident '.' ident
{
THD *thd= YYTHD;
@@ -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 {}
@@ -5361,15 +7103,107 @@ 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)));
- }
+ '@' ident_or_text equal expr
+ {
+ 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;
- lex->var_list.push_back(new set_var(lex->option_type, $1.var,
- &$1.base_name, $3));
+
+ 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
{
@@ -5427,33 +7261,76 @@ option_value:
internal_variable_name:
ident
{
- sys_var *tmp=find_sys_var($1.str, $1.length);
- if (!tmp)
- YYABORT;
- $$.var= tmp;
- $$.base_name.str=0;
- $$.base_name.length=0;
- /*
- If this is time_zone variable we should open time zone
- describing tables
- */
- if (tmp == &sys_time_zone)
- Lex->time_zone_tables_used= &fake_time_zone_tables_list;
+ 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;
+ $$.var= tmp;
+ $$.base_name.str=0;
+ $$.base_name.length=0;
+ /*
+ If this is time_zone variable we should open time zone
+ describing tables
+ */
+ 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;
}
- sys_var *tmp=find_sys_var($3.str, $3.length);
- if (!tmp)
- YYABORT;
- if (!tmp->is_struct())
- net_printf(YYTHD, ER_VARIABLE_IS_NOT_STRUCT, $3.str);
- $$.var= tmp;
- $$.base_name= $1;
+ 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;
+ if (!tmp->is_struct())
+ net_printf(YYTHD, ER_VARIABLE_IS_NOT_STRUCT, $3.str);
+ $$.var= tmp;
+ $$.base_name= $1;
+ }
}
| DEFAULT '.' ident
{
@@ -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; }
+ ;
+