summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <guilhem@mysql.com>2004-06-03 23:19:01 +0200
committerunknown <guilhem@mysql.com>2004-06-03 23:19:01 +0200
commit948903fc4f47af2cf328424931a64030bec44442 (patch)
tree7167f46c01fca0ac351957979f203eb7255612be /sql
parent0f7a9f61023d97b60286ed7b72f27a9fb343f083 (diff)
parent934bb37d39b7c18c8d80eccdd914dc08f988c646 (diff)
downloadmariadb-git-948903fc4f47af2cf328424931a64030bec44442.tar.gz
Merge gbichot@213.136.52.20:/home/bk/mysql-4.1
into mysql.com:/home/mysql_src/mysql-4.1 sql/set_var.cc: Auto merged sql/slave.cc: Auto merged sql/sql_class.cc: Auto merged sql/sql_class.h: Auto merged sql/sql_parse.cc: Auto merged
Diffstat (limited to 'sql')
-rw-r--r--sql/lex.h1
-rw-r--r--sql/log.cc34
-rw-r--r--sql/log_event.cc71
-rw-r--r--sql/set_var.cc123
-rw-r--r--sql/set_var.h56
-rw-r--r--sql/slave.cc83
-rw-r--r--sql/sql_class.cc1
-rw-r--r--sql/sql_class.h2
-rw-r--r--sql/sql_lex.h2
-rw-r--r--sql/sql_parse.cc42
-rw-r--r--sql/sql_yacc.yy4
11 files changed, 348 insertions, 71 deletions
diff --git a/sql/lex.h b/sql/lex.h
index e5bc537c213..f8ead8a8d2d 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -310,6 +310,7 @@ static SYMBOL symbols[] = {
{ "OFFSET", SYM(OFFSET_SYM)},
{ "OLD_PASSWORD", SYM(OLD_PASSWORD)},
{ "ON", SYM(ON)},
+ { "ONE_SHOT", SYM(ONE_SHOT_SYM)},
{ "OPEN", SYM(OPEN_SYM)},
{ "OPTIMIZE", SYM(OPTIMIZE)},
{ "OPTION", SYM(OPTION)},
diff --git a/sql/log.cc b/sql/log.cc
index 8df5ea5096b..e7a142230b1 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -1231,6 +1231,40 @@ bool MYSQL_LOG::write(Log_event* event_info)
if (thd)
{
+#if MYSQL_VERSION_ID < 50000
+ /*
+ To make replication of charsets working in 4.1 we are writing values
+ of charset related variables before every statement in the binlog,
+ if values of those variables differ from global server-wide defaults.
+ We are using SET ONE_SHOT command so that the charset vars get reset
+ to default after the first non-SET statement.
+ In the next 5.0 this won't be needed as we will use the new binlog
+ format to store charset info.
+ */
+ if ((thd->variables.character_set_client->number !=
+ global_system_variables.collation_server->number) ||
+ (thd->variables.character_set_client->number !=
+ thd->variables.collation_connection->number) ||
+ (thd->variables.collation_server->number !=
+ thd->variables.collation_connection->number))
+ {
+ char buf[200];
+ int written= my_snprintf(buf, sizeof(buf)-1,
+ "SET ONE_SHOT CHARACTER_SET_CLIENT=%lu,\
+COLLATION_CONNECTION=%lu,COLLATION_DATABASE=%lu,COLLATION_SERVER=%lu",
+ thd->variables.character_set_client->number,
+ thd->variables.collation_connection->number,
+ thd->variables.collation_database->number,
+ thd->variables.collation_server->number);
+ Query_log_event e(thd, buf, written, 0);
+ e.set_log_pos(this);
+ if (e.write(file))
+ goto err;
+ }
+#endif
+
+ /* Add logging of timezones here */
+
if (thd->last_insert_id_used)
{
Intvar_log_event e(thd,(uchar) LAST_INSERT_ID_EVENT,
diff --git a/sql/log_event.cc b/sql/log_event.cc
index fd65ec64a76..a76725a95e0 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -2176,7 +2176,7 @@ int Rand_log_event::exec_event(struct st_relay_log_info* rli)
void User_var_log_event::pack_info(Protocol* protocol)
{
char *buf= 0;
- uint val_offset= 2 + name_len;
+ uint val_offset= 4 + name_len;
uint event_len= val_offset;
if (is_null)
@@ -2200,16 +2200,21 @@ void User_var_log_event::pack_info(Protocol* protocol)
event_len= longlong10_to_str(uint8korr(val), buf + val_offset,-10)-buf;
break;
case STRING_RESULT:
- /*
- This is correct as pack_info is used for SHOW BINLOG command
- only. But be carefull this is may be incorrect in other cases as
- string may contain \ and '.
- */
- event_len= val_offset + 2 + val_len;
- buf= my_malloc(event_len, MYF(MY_WME));
- buf[val_offset]= '\'';
- memcpy(buf + val_offset + 1, val, val_len);
- buf[val_offset + val_len + 1]= '\'';
+ /* 15 is for 'COLLATE' and other chars */
+ buf= my_malloc(event_len+val_len*2+1+2*MY_CS_NAME_SIZE+15, MYF(MY_WME));
+ CHARSET_INFO *cs;
+ if (!(cs= get_charset(charset_number, MYF(0))))
+ {
+ strmov(buf+val_offset, "???");
+ event_len+= 3;
+ }
+ else
+ {
+ char *p= strxmov(buf + val_offset, "_", cs->csname, "'", NullS);
+ p+= escape_string_for_mysql(&my_charset_bin, p, val, val_len);
+ p= strxmov(p, "' COLLATE ", cs->name, NullS);
+ event_len= p-buf;
+ }
break;
case ROW_RESULT:
default:
@@ -2218,8 +2223,10 @@ void User_var_log_event::pack_info(Protocol* protocol)
}
}
buf[0]= '@';
- buf[1+name_len]= '=';
- memcpy(buf+1, name, name_len);
+ buf[1]= '`';
+ buf[2+name_len]= '`';
+ buf[3+name_len]= '=';
+ memcpy(buf+2, name, name_len);
protocol->store(buf, event_len, &my_charset_bin);
my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
}
@@ -2311,8 +2318,9 @@ void User_var_log_event::print(FILE* file, bool short_form, char* last_db)
fprintf(file, "\tUser_var\n");
}
- fprintf(file, "SET @");
+ fprintf(file, "SET @`");
my_fwrite(file, (byte*) name, (uint) (name_len), MYF(MY_NABP | MY_WME));
+ fprintf(file, "`");
if (is_null)
{
@@ -2332,7 +2340,36 @@ void User_var_log_event::print(FILE* file, bool short_form, char* last_db)
fprintf(file, ":=%s;\n", int_buf);
break;
case STRING_RESULT:
- fprintf(file, ":='%s';\n", val);
+ {
+ char *p;
+ if (!(p= (char *)my_alloca(2*val_len+1)))
+ break; // no error, as we are 'void'
+ escape_string_for_mysql(&my_charset_bin, p, val, val_len);
+#if MYSQL_VERSION_ID < 50000
+ /*
+ For proper behaviour when mysqlbinlog|mysql, we need to explicitely
+ specify the variable's collation. It will however cause problems when
+ people want to mysqlbinlog|mysql into another server not supporting the
+ character set. But there's not much to do about this and it's unlikely.
+ */
+ CHARSET_INFO *cs;
+ if (!(cs= get_charset(charset_number, MYF(0))))
+ /*
+ Generate an unusable command (=> syntax error) is probably the best
+ thing we can do here.
+ */
+ fprintf(file, ":=???;\n");
+ else
+ fprintf(file, ":=_%s'%s' COLLATE %s;\n", cs->csname, p, cs->name);
+#else
+ /*
+ In 5.0 we will have some SET CHARACTER_SET_ect automatically printed
+ for all events where it's needed.
+ */
+ fprintf(file, ":='%s';\n", p);
+#endif
+ my_afree(p);
+ }
break;
case ROW_RESULT:
default:
@@ -2353,7 +2390,9 @@ void User_var_log_event::print(FILE* file, bool short_form, char* last_db)
int User_var_log_event::exec_event(struct st_relay_log_info* rli)
{
Item *it= 0;
- CHARSET_INFO *charset= get_charset(charset_number, MYF(0));
+ CHARSET_INFO *charset;
+ if (!(charset= get_charset(charset_number, MYF(MY_WME))))
+ return 1;
LEX_STRING user_var_name;
user_var_name.str= name;
user_var_name.length= name_len;
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 517a3c65ab8..b0935232f29 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -1710,19 +1710,31 @@ CHARSET_INFO *get_old_charset_by_name(const char *name)
bool sys_var_collation::check(THD *thd, set_var *var)
{
CHARSET_INFO *tmp;
- char buff[80];
- String str(buff,sizeof(buff), system_charset_info), *res;
- if (!(res=var->value->val_str(&str)))
+ if (var->value->result_type() == STRING_RESULT)
{
- my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, "NULL");
- return 1;
+ char buff[80];
+ String str(buff,sizeof(buff), system_charset_info), *res;
+ if (!(res=var->value->val_str(&str)))
+ {
+ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, "NULL");
+ return 1;
+ }
+ if (!(tmp=get_charset_by_name(res->c_ptr(),MYF(0))))
+ {
+ my_error(ER_UNKNOWN_COLLATION, MYF(0), res->c_ptr());
+ return 1;
+ }
}
-
- if (!(tmp=get_charset_by_name(res->c_ptr(),MYF(0))))
+ else // INT_RESULT
{
- my_error(ER_UNKNOWN_COLLATION, MYF(0), res->c_ptr());
- return 1;
+ if (!(tmp=get_charset(var->value->val_int(),MYF(0))))
+ {
+ char buf[20];
+ int10_to_str(var->value->val_int(), buf, -10);
+ my_error(ER_UNKNOWN_COLLATION, MYF(0), buf);
+ return 1;
+ }
}
var->save_result.charset= tmp; // Save for update
return 0;
@@ -1732,23 +1744,36 @@ bool sys_var_collation::check(THD *thd, set_var *var)
bool sys_var_character_set::check(THD *thd, set_var *var)
{
CHARSET_INFO *tmp;
- char buff[80];
- String str(buff,sizeof(buff), system_charset_info), *res;
- if (!(res=var->value->val_str(&str)))
- {
- if (!nullable)
+ if (var->value->result_type() == STRING_RESULT)
+ {
+ char buff[80];
+ String str(buff,sizeof(buff), system_charset_info), *res;
+ if (!(res=var->value->val_str(&str)))
{
- my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, "NULL");
+ if (!nullable)
+ {
+ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, "NULL");
+ return 1;
+ }
+ tmp= NULL;
+ }
+ else if (!(tmp=get_charset_by_csname(res->c_ptr(),MY_CS_PRIMARY,MYF(0))) &&
+ !(tmp=get_old_charset_by_name(res->c_ptr())))
+ {
+ my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), res->c_ptr());
return 1;
}
- tmp= NULL;
}
- else if (!(tmp=get_charset_by_csname(res->c_ptr(),MY_CS_PRIMARY,MYF(0))) &&
- !(tmp=get_old_charset_by_name(res->c_ptr())))
+ else // INT_RESULT
{
- my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), res->c_ptr());
- return 1;
+ if (!(tmp=get_charset(var->value->val_int(),MYF(0))))
+ {
+ char buf[20];
+ int10_to_str(var->value->val_int(), buf, -10);
+ my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), buf);
+ return 1;
+ }
}
var->save_result.charset= tmp; // Save for update
return 0;
@@ -1861,6 +1886,20 @@ void sys_var_character_set_server::set_default(THD *thd, enum_var_type type)
}
}
+#if defined(HAVE_REPLICATION) && (MYSQL_VERSION_ID < 50000)
+bool sys_var_character_set_server::check(THD *thd, set_var *var)
+{
+ if ((var->type == OPT_GLOBAL) &&
+ (mysql_bin_log.is_open() ||
+ active_mi->slave_running || active_mi->rli.slave_running))
+ {
+ my_printf_error(0, "Binary logging and replication forbid changing \
+the global server character set or collation", MYF(0));
+ return 1;
+ }
+ return sys_var_character_set::check(thd,var);
+}
+#endif
CHARSET_INFO ** sys_var_character_set_database::ci_ptr(THD *thd,
enum_var_type type)
@@ -1954,6 +1993,20 @@ void sys_var_collation_database::set_default(THD *thd, enum_var_type type)
}
}
+#if defined(HAVE_REPLICATION) && (MYSQL_VERSION_ID < 50000)
+bool sys_var_collation_server::check(THD *thd, set_var *var)
+{
+ if ((var->type == OPT_GLOBAL) &&
+ (mysql_bin_log.is_open() ||
+ active_mi->slave_running || active_mi->rli.slave_running))
+ {
+ my_printf_error(0, "Binary logging and replication forbid changing \
+the global server character set or collation", MYF(0));
+ return 1;
+ }
+ return sys_var_collation::check(thd,var);
+}
+#endif
bool sys_var_collation_server::update(THD *thd, set_var *var)
{
@@ -2526,6 +2579,36 @@ int sql_set_variables(THD *thd, List<set_var_base> *var_list)
}
+/*
+ Say if all variables set by a SET support the ONE_SHOT keyword (currently,
+ only character set and collation do; later timezones will).
+
+ SYNOPSIS
+
+ not_all_support_one_shot
+ set_var List of variables to update
+
+ NOTES
+ It has a "not_" because it makes faster tests (no need to "!")
+
+ RETURN VALUE
+ 0 all variables of the list support ONE_SHOT
+ 1 at least one does not support ONE_SHOT
+*/
+
+bool not_all_support_one_shot(List<set_var_base> *var_list)
+{
+ List_iterator_fast<set_var_base> it(*var_list);
+ set_var_base *var;
+ while ((var= it++))
+ {
+ if (var->no_support_one_shot())
+ return 1;
+ }
+ return 0;
+}
+
+
/*****************************************************************************
Functions to handle SET mysql_internal_variable=const_expr
*****************************************************************************/
diff --git a/sql/set_var.h b/sql/set_var.h
index 699f320bbd9..64bdfdb718b 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -49,10 +49,20 @@ public:
const char *name;
sys_after_update_func after_update;
- sys_var(const char *name_arg) :name(name_arg),after_update(0)
- {}
+#if MYSQL_VERSION_ID < 50000
+ bool no_support_one_shot;
+#endif
+ sys_var(const char *name_arg)
+ :name(name_arg), after_update(0)
+#if MYSQL_VERSION_ID < 50000
+ , no_support_one_shot(1)
+#endif
+ {}
sys_var(const char *name_arg,sys_after_update_func func)
- :name(name_arg),after_update(func)
+ :name(name_arg), after_update(func)
+#if MYSQL_VERSION_ID < 50000
+ , no_support_one_shot(1)
+#endif
{}
virtual ~sys_var() {}
virtual bool check(THD *thd, set_var *var);
@@ -487,12 +497,17 @@ public:
class sys_var_collation :public sys_var_thd
{
public:
- sys_var_collation(const char *name_arg) :sys_var_thd(name_arg) {}
+ sys_var_collation(const char *name_arg) :sys_var_thd(name_arg)
+ {
+#if MYSQL_VERSION_ID < 50000
+ no_support_one_shot= 0;
+#endif
+ }
bool check(THD *thd, set_var *var);
SHOW_TYPE type() { return SHOW_CHAR; }
bool check_update_type(Item_result type)
{
- return type != STRING_RESULT; /* Only accept strings */
+ return ((type != STRING_RESULT) && (type != INT_RESULT));
}
bool check_default(enum_var_type type) { return 0; }
virtual void set_default(THD *thd, enum_var_type type)= 0;
@@ -502,13 +517,23 @@ class sys_var_character_set :public sys_var_thd
{
public:
bool nullable;
- sys_var_character_set(const char *name_arg) :sys_var_thd(name_arg)
- { nullable= 0; }
+ sys_var_character_set(const char *name_arg) :
+ sys_var_thd(name_arg)
+ {
+ nullable= 0;
+#if MYSQL_VERSION_ID < 50000
+ /*
+ In fact only almost all variables derived from sys_var_character_set
+ support ONE_SHOT; character_set_results doesn't. But that's good enough.
+ */
+ no_support_one_shot= 0;
+#endif
+ }
bool check(THD *thd, set_var *var);
SHOW_TYPE type() { return SHOW_CHAR; }
bool check_update_type(Item_result type)
{
- return type != STRING_RESULT; /* Only accept strings */
+ return ((type != STRING_RESULT) && (type != INT_RESULT));
}
bool check_default(enum_var_type type) { return 0; }
bool update(THD *thd, set_var *var);
@@ -541,6 +566,9 @@ class sys_var_character_set_server :public sys_var_character_set
public:
sys_var_character_set_server(const char *name_arg) :
sys_var_character_set(name_arg) {}
+#if defined(HAVE_REPLICATION) && (MYSQL_VERSION_ID < 50000)
+ bool check(THD *thd, set_var *var);
+#endif
void set_default(THD *thd, enum_var_type type);
CHARSET_INFO **ci_ptr(THD *thd, enum_var_type type);
};
@@ -576,6 +604,9 @@ class sys_var_collation_server :public sys_var_collation
{
public:
sys_var_collation_server(const char *name_arg) :sys_var_collation(name_arg) {}
+#if defined(HAVE_REPLICATION) && (MYSQL_VERSION_ID < 50000)
+ bool check(THD *thd, set_var *var);
+#endif
bool update(THD *thd, set_var *var);
void set_default(THD *thd, enum_var_type type);
byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
@@ -689,7 +720,10 @@ public:
virtual int check(THD *thd)=0; /* To check privileges etc. */
virtual int update(THD *thd)=0; /* To set the value */
/* light check for PS */
- virtual int light_check(THD *thd) { return check(thd); }
+ virtual int light_check(THD *thd) { return check(thd); }
+#if MYSQL_VERSION_ID < 50000
+ virtual bool no_support_one_shot() { return 1; }
+#endif
};
@@ -731,6 +765,9 @@ public:
int check(THD *thd);
int update(THD *thd);
int light_check(THD *thd);
+#if MYSQL_VERSION_ID < 50000
+ bool no_support_one_shot() { return var->no_support_one_shot; }
+#endif
};
@@ -833,6 +870,7 @@ void set_var_init();
void set_var_free();
sys_var *find_sys_var(const char *str, uint length=0);
int sql_set_variables(THD *thd, List<set_var_base> *var_list);
+bool not_all_support_one_shot(List<set_var_base> *var_list);
void fix_delay_key_write(THD *thd, enum_var_type type);
ulong fix_sql_mode(ulong sql_mode);
extern sys_var_str sys_charset_system;
diff --git a/sql/slave.cc b/sql/slave.cc
index 59af7c663e9..a9b598d73db 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -72,7 +72,7 @@ static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed,
static int request_table_dump(MYSQL* mysql, const char* db, const char* table);
static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
const char* table_name, bool overwrite);
-static int check_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi);
+static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi);
/*
@@ -1187,38 +1187,75 @@ slaves can't replicate a 5.0 or newer master.";
break;
}
- MYSQL_RES *master_clock_res;
- MYSQL_ROW master_clock_row;
- time_t slave_clock;
-
- if (mysql_real_query(mysql, "SELECT UNIX_TIMESTAMP()", 23))
- errmsg= "\"SELECT UNIX_TIMESTAMP()\" failed on master";
- else if (!(master_clock_res= mysql_store_result(mysql)))
+ /*
+ Compare the master and slave's clock. Do not die if master's clock is
+ unavailable (very old master not supporting UNIX_TIMESTAMP()?).
+ */
+ MYSQL_RES *master_res= 0;
+ MYSQL_ROW master_row;
+
+ if (!mysql_real_query(mysql, "SELECT UNIX_TIMESTAMP()", 23) &&
+ (master_res= mysql_store_result(mysql)) &&
+ (master_row= mysql_fetch_row(master_res)))
{
- errmsg= "Could not read the result of \"SELECT UNIX_TIMESTAMP()\" on \
-master";
+ mi->clock_diff_with_master=
+ (long) (time((time_t*) 0) - strtoul(master_row[0], 0, 10));
}
- else
+ else
{
- if (!(master_clock_row= mysql_fetch_row(master_clock_res)))
- errmsg= "Could not read a row from the result of \"SELECT \
-UNIX_TIMESTAMP()\" on master";
- else
- {
- slave_clock= time((time_t*) 0);
- mi->clock_diff_with_master= (long) (slave_clock -
- strtoul(master_clock_row[0], 0, 10));
- DBUG_PRINT("info",("slave_clock=%lu, master_clock=%s",
- slave_clock, master_clock_row[0]));
- }
- mysql_free_result(master_clock_res);
+ mi->clock_diff_with_master= 0; /* The "most sensible" value */
+ sql_print_error("Warning: \"SELECT UNIX_TIMESTAMP()\" failed on master, \
+do not trust column Seconds_Behind_Master of SHOW SLAVE STATUS");
+ }
+ if (master_res)
+ mysql_free_result(master_res);
+
+ /*
+ Check that the master's server id and ours are different. Because if they
+ are equal (which can result from a simple copy of master's datadir to slave,
+ thus copying some my.cnf), replication will work but all events will be
+ skipped.
+ Do not die if SHOW VARIABLES LIKE 'SERVER_ID' fails on master (very old
+ master?).
+ Note: we could have put a @@SERVER_ID in the previous SELECT
+ UNIX_TIMESTAMP() instead, but this would not have worked on 3.23 masters.
+ */
+ if (!mysql_real_query(mysql, "SHOW VARIABLES LIKE 'SERVER_ID'", 31) &&
+ (master_res= mysql_store_result(mysql)))
+ {
+ if ((master_row= mysql_fetch_row(master_res)) &&
+ (::server_id == strtoul(master_row[1], 0, 10)) &&
+ !replicate_same_server_id)
+ errmsg= "The slave I/O thread stops because master and slave have equal \
+MySQL server ids; these ids must be different for replication to work (or \
+the --replicate-same-server-id option must be used on slave but this does \
+not always make sense; please check the manual before using it).";
+ mysql_free_result(master_res);
+ }
+
+ /*
+ Check that the master's global character_set_server and ours are the same.
+ Not fatal if query fails (old master?).
+ */
+ if (!mysql_real_query(mysql, "SELECT @@GLOBAL.COLLATION_SERVER", 32) &&
+ (master_res= mysql_store_result(mysql)))
+ {
+ if ((master_row= mysql_fetch_row(master_res)) &&
+ strcmp(master_row[0], global_system_variables.collation_server->name))
+ errmsg= "The slave I/O thread stops because master and slave have \
+different values for the COLLATION_SERVER global variable. The values must \
+be equal for replication to work";
+ mysql_free_result(master_res);
}
+ /* Add a timezones check here */
+
if (errmsg)
{
sql_print_error(errmsg);
return 1;
}
+
return 0;
}
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index d16d1de7607..3ee9bd609a2 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -181,6 +181,7 @@ THD::THD():user_time(0), current_statement(0), is_fatal_error(0),
current_linfo = 0;
slave_thread = 0;
variables.pseudo_thread_id= 0;
+ one_shot_set= 0;
file_id = 0;
warn_id= 0;
db_charset= global_system_variables.collation_database;
diff --git a/sql/sql_class.h b/sql/sql_class.h
index a3e398dc1d6..26f41b2912f 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -801,7 +801,7 @@ public:
/* scramble - random string sent to client on handshake */
char scramble[SCRAMBLE_LENGTH+1];
- bool slave_thread;
+ bool slave_thread, one_shot_set;
bool locked, some_tables_deleted;
bool last_cuted_field;
bool no_errors, password, is_fatal_error;
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 92ed53cf814..3884c8f2674 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -611,7 +611,7 @@ typedef struct st_lex
uint fk_delete_opt, fk_update_opt, fk_match_option;
uint slave_thd_opt;
uint8 describe;
- bool drop_if_exists, drop_temporary, local_file;
+ bool drop_if_exists, drop_temporary, local_file, one_shot_set;
bool in_comment, ignore_space, verbose, no_write_to_binlog;
bool derived_tables;
bool safe_to_cache_query;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index a7ecda72905..e83260b5de0 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2918,14 +2918,31 @@ unsent_create_error:
}
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))))
break;
- if (!(res= sql_set_variables(thd, &lex->var_list)))
+ if (lex->one_shot_set && not_all_support_one_shot(lex_var_list))
+ {
+ my_printf_error(0, "The SET ONE_SHOT syntax is reserved for \
+purposes internal to the MySQL server", MYF(0));
+ res= -1;
+ break;
+ }
+ if (!(res= sql_set_variables(thd, lex_var_list)))
+ {
+ /*
+ If the previous command was a SET ONE_SHOT, we don't want to forget
+ about the ONE_SHOT property of that SET. So we use a |= instead of = .
+ */
+ thd->one_shot_set|= lex->one_shot_set;
send_ok(thd);
+ }
if (thd->net.report_error)
res= -1;
break;
+ }
case SQLCOM_UNLOCK_TABLES:
unlock_locked_tables(thd);
@@ -3377,6 +3394,29 @@ unsent_create_error:
break;
}
thd->proc_info="query end"; // QQ
+ if (thd->one_shot_set)
+ {
+ /*
+ If this is a SET, do nothing. This is to allow mysqlbinlog to print
+ many SET commands (in this case we want the charset temp setting to
+ live until the real query). This is also needed so that SET
+ CHARACTER_SET_CLIENT... does not cancel itself immediately.
+ */
+ if (lex->sql_command != SQLCOM_SET_OPTION)
+ {
+ thd->variables.character_set_client=
+ global_system_variables.character_set_client;
+ thd->variables.collation_connection=
+ global_system_variables.collation_connection;
+ thd->variables.collation_database=
+ global_system_variables.collation_database;
+ thd->variables.collation_server=
+ global_system_variables.collation_server;
+ thd->update_charset();
+ /* Add timezone stuff here */
+ thd->one_shot_set= 0;
+ }
+ }
if (res < 0)
send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 6a40dc3c23a..04aab392285 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -316,6 +316,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token NUM
%token OFFSET_SYM
%token ON
+%token ONE_SHOT_SYM
%token OPEN_SYM
%token OPTION
%token OPTIONALLY
@@ -5002,6 +5003,7 @@ keyword:
| NVARCHAR_SYM {}
| OFFSET_SYM {}
| OLD_PASSWORD {}
+ | ONE_SHOT_SYM {}
| OPEN_SYM {}
| PACK_KEYS_SYM {}
| PARTIAL {}
@@ -5088,6 +5090,7 @@ set:
lex->sql_command= SQLCOM_SET_OPTION;
lex->option_type=OPT_SESSION;
lex->var_list.empty();
+ lex->one_shot_set= 0;
}
option_value_list
{}
@@ -5106,6 +5109,7 @@ option_type:
| GLOBAL_SYM { Lex->option_type= OPT_GLOBAL; }
| LOCAL_SYM { Lex->option_type= OPT_SESSION; }
| SESSION_SYM { Lex->option_type= OPT_SESSION; }
+ | ONE_SHOT_SYM { Lex->option_type= OPT_SESSION; Lex->one_shot_set= 1; }
;
opt_var_type: