summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <tsmith@maint1.mysql.com>2007-06-21 20:55:37 +0200
committerunknown <tsmith@maint1.mysql.com>2007-06-21 20:55:37 +0200
commit2da92ead65f1bf6189e556d42fe9a11108ef343e (patch)
tree9396a2750231f593b88b423264adf6f3b7ba6af9 /sql
parenta1c0a4b45d5d60322e8615ae7407e90cfcebb576 (diff)
parent8ddf61d382cf98ab8adc23c04c0aced95bc4f4ef (diff)
downloadmariadb-git-2da92ead65f1bf6189e556d42fe9a11108ef343e.tar.gz
Merge maint1.mysql.com:/data/localhome/tsmith/bk/maint/50
into maint1.mysql.com:/data/localhome/tsmith/bk/maint/51 configure.in: Auto merged include/m_ctype.h: Auto merged mysql-test/Makefile.am: Auto merged mysql-test/t/innodb.test: Auto merged mysys/charset-def.c: Auto merged mysys/charset.c: Auto merged sql/log_event.cc: Auto merged sql/sql_acl.cc: Auto merged sql/sql_class.cc: Auto merged strings/ctype-big5.c: Auto merged strings/ctype-gbk.c: Auto merged strings/ctype-sjis.c: Auto merged strings/ctype-uca.c: Auto merged strings/ctype.c: Auto merged mysql-test/r/innodb.result: Manual merge mysql-test/r/multi_update.result: Manual merge mysql-test/t/multi_update.test: Manual merge sql/sql_update.cc: SCCS merged
Diffstat (limited to 'sql')
-rw-r--r--sql/sql_acl.cc8
-rw-r--r--sql/sql_class.cc59
-rw-r--r--sql/sql_update.cc90
3 files changed, 135 insertions, 22 deletions
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 1b57e9363e4..dfdd6f5c5b4 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -5539,6 +5539,12 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
VOID(pthread_mutex_unlock(&acl_cache->lock));
+ if (result)
+ my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe());
+
+ DBUG_PRINT("info", ("thd->net.last_errno: %d", thd->net.last_errno));
+ DBUG_PRINT("info", ("thd->net.last_error: %s", thd->net.last_error));
+
if (mysql_bin_log.is_open())
{
thd->binlog_query(THD::MYSQL_QUERY_TYPE,
@@ -5547,8 +5553,6 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
rw_unlock(&LOCK_grant);
close_thread_tables(thd);
- if (result)
- my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe());
DBUG_RETURN(result);
}
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 62f6706d717..e83432519d4 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1457,6 +1457,11 @@ select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
}
+#define NEED_ESCAPING(x) ((int) (uchar) (x) == escape_char || \
+ (int) (uchar) (x) == field_sep_char || \
+ (int) (uchar) (x) == line_sep_char || \
+ !(x))
+
bool select_export::send_data(List<Item> &items)
{
@@ -1516,14 +1521,20 @@ bool select_export::send_data(List<Item> &items)
used_length=res->length();
if (result_type == STRING_RESULT && escape_char != -1)
{
- char *pos,*start,*end;
-
+ char *pos, *start, *end;
+ CHARSET_INFO *res_charset= res->charset();
+ CHARSET_INFO *character_set_client= thd->variables.
+ character_set_client;
+ bool check_second_byte= (res_charset == &my_charset_bin) &&
+ character_set_client->
+ escape_with_backslash_is_dangerous;
+ DBUG_ASSERT(character_set_client->mbmaxlen == 2 ||
+ !character_set_client->escape_with_backslash_is_dangerous);
for (start=pos=(char*) res->ptr(),end=pos+used_length ;
pos != end ;
pos++)
{
#ifdef USE_MB
- CHARSET_INFO *res_charset=res->charset();
if (use_mb(res_charset))
{
int l;
@@ -1534,9 +1545,45 @@ bool select_export::send_data(List<Item> &items)
}
}
#endif
- if ((int) *pos == escape_char || (int) *pos == field_sep_char ||
- (int) *pos == line_sep_char || !*pos)
- {
+
+ /*
+ Special case when dumping BINARY/VARBINARY/BLOB values
+ for the clients with character sets big5, cp932, gbk and sjis,
+ which can have the escape character (0x5C "\" by default)
+ as the second byte of a multi-byte sequence.
+
+ If
+ - pos[0] is a valid multi-byte head (e.g 0xEE) and
+ - pos[1] is 0x00, which will be escaped as "\0",
+
+ then we'll get "0xEE + 0x5C + 0x30" in the output file.
+
+ If this file is later loaded using this sequence of commands:
+
+ mysql> create table t1 (a varchar(128)) character set big5;
+ mysql> LOAD DATA INFILE 'dump.txt' INTO TABLE t1;
+
+ then 0x5C will be misinterpreted as the second byte
+ of a multi-byte character "0xEE + 0x5C", instead of
+ escape character for 0x00.
+
+ To avoid this confusion, we'll escape the multi-byte
+ head character too, so the sequence "0xEE + 0x00" will be
+ dumped as "0x5C + 0xEE + 0x5C + 0x30".
+
+ Note, in the condition below we only check if
+ mbcharlen is equal to 2, because there are no
+ character sets with mbmaxlen longer than 2
+ and with escape_with_backslash_is_dangerous set.
+ DBUG_ASSERT before the loop makes that sure.
+ */
+
+ if (NEED_ESCAPING(*pos) ||
+ (check_second_byte &&
+ my_mbcharlen(character_set_client, (uchar) *pos) == 2 &&
+ pos + 1 < end &&
+ NEED_ESCAPING(pos[1])))
+ {
char tmp_buff[2];
tmp_buff[0]= escape_char;
tmp_buff[1]= *pos ? *pos : '0';
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index d9bf6b4d918..33ec8152837 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -1069,6 +1069,7 @@ bool mysql_multi_update(THD *thd,
SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex)
{
multi_update *result;
+ bool res;
DBUG_ENTER("mysql_multi_update");
if (!(result= new multi_update(table_list,
@@ -1083,7 +1084,7 @@ bool mysql_multi_update(THD *thd,
MODE_STRICT_ALL_TABLES));
List<Item> total_list;
- (void) mysql_select(thd, &select_lex->ref_pointer_array,
+ res= mysql_select(thd, &select_lex->ref_pointer_array,
table_list, select_lex->with_wild,
total_list,
conds, 0, (ORDER *) NULL, (ORDER *)NULL, (Item *) NULL,
@@ -1091,6 +1092,15 @@ bool mysql_multi_update(THD *thd,
options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
OPTION_SETUP_TABLES_DONE,
result, unit, select_lex);
+ DBUG_PRINT("info",("res: %d report_error: %d", res,
+ thd->net.report_error));
+ res|= thd->net.report_error;
+ if (unlikely(res))
+ {
+ /* If we had a another error reported earlier then this will be ignored */
+ result->send_error(ER_UNKNOWN_ERROR, ER(ER_UNKNOWN_ERROR));
+ result->abort();
+ }
delete result;
thd->abort_on_warning= 0;
DBUG_RETURN(FALSE);
@@ -1441,8 +1451,9 @@ multi_update::~multi_update()
if (copy_field)
delete [] copy_field;
thd->count_cuted_fields= CHECK_FIELD_IGNORE; // Restore this setting
- if (!trans_safe)
+ if (!trans_safe) // todo: remove since redundant
thd->no_trans_update.all= TRUE;
+ DBUG_ASSERT(trans_safe || thd->no_trans_update.all);
}
@@ -1531,8 +1542,15 @@ bool multi_update::send_data(List<Item> &not_used_values)
}
else
{
- if (!table->file->has_transactions())
+ /* non-transactional or transactional table got modified */
+ /* either multi_update class' flag is raised in its branch */
+ if (table->file->has_transactions())
+ transactional_tables= 1;
+ else
+ {
+ trans_safe= 0;
thd->no_trans_update.stmt= TRUE;
+ }
if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
TRG_ACTION_AFTER, TRUE))
@@ -1589,8 +1607,8 @@ void multi_update::send_error(uint errcode,const char *err)
my_error(errcode, MYF(0), err);
/* If nothing updated return */
- if (!updated)
- return;
+ if (updated == 0) /* the counter might be reset in send_eof */
+ return; /* and then the query has been binlogged */
/* Something already updated so we have to invalidate cache */
query_cache_invalidate3(thd, update_tables, 1);
@@ -1601,11 +1619,43 @@ void multi_update::send_error(uint errcode,const char *err)
*/
if (trans_safe)
- ha_rollback_stmt(thd);
- else if (do_update && table_count > 1)
{
- /* Add warning here */
- VOID(do_updates(0));
+ DBUG_ASSERT(transactional_tables);
+ (void) ha_autocommit_or_rollback(thd, 1);
+ }
+ else
+ {
+ DBUG_ASSERT(thd->no_trans_update.stmt);
+ if (do_update && table_count > 1)
+ {
+ /* Add warning here */
+ /*
+ todo/fixme: do_update() is never called with the arg 1.
+ should it change the signature to become argless?
+ */
+ VOID(do_updates(0));
+ }
+ }
+ if (thd->no_trans_update.stmt)
+ {
+ /*
+ The query has to binlog because there's a modified non-transactional table
+ either from the query's list or via a stored routine: bug#13270,23333
+ */
+ if (mysql_bin_log.is_open())
+ {
+ Query_log_event qinfo(thd, thd->query, thd->query_length,
+ transactional_tables, FALSE);
+ mysql_bin_log.write(&qinfo);
+ }
+ if (!trans_safe)
+ thd->no_trans_update.all= TRUE;
+ }
+ DBUG_ASSERT(trans_safe || !updated || thd->no_trans_update.stmt);
+
+ if (transactional_tables)
+ {
+ (void) ha_autocommit_or_rollback(thd, 1);
}
}
@@ -1736,9 +1786,12 @@ int multi_update::do_updates(bool from_send_error)
if (updated != org_updated)
{
if (table->file->has_transactions())
- transactional_tables= 1;
+ transactional_tables= 1;
else
- trans_safe= 0; // Can't do safe rollback
+ {
+ trans_safe= 0; // Can't do safe rollback
+ thd->no_trans_update.stmt= TRUE;
+ }
}
(void) table->file->ha_rnd_end();
(void) tmp_table->file->ha_rnd_end();
@@ -1768,7 +1821,10 @@ err2:
if (table->file->has_transactions())
transactional_tables= 1;
else
+ {
trans_safe= 0;
+ thd->no_trans_update.stmt= TRUE;
+ }
}
DBUG_RETURN(1);
}
@@ -1798,14 +1854,20 @@ bool multi_update::send_eof()
Write the SQL statement to the binlog if we updated
rows and we succeeded or if we updated some non
transactional tables.
+
+ The query has to binlog because there's a modified non-transactional table
+ either from the query's list or via a stored routine: bug#13270,23333
*/
- if ((local_error == 0) || (updated && !trans_safe))
+ DBUG_ASSERT(trans_safe || !updated || thd->no_trans_update.stmt);
+ if (local_error == 0 || thd->no_trans_update.stmt)
{
if (mysql_bin_log.is_open())
{
if (local_error == 0)
thd->clear_error();
+ else
+ updated= 0; /* if there's an error binlog it here not in ::send_error */
if (thd->binlog_query(THD::ROW_QUERY_TYPE,
thd->query, thd->query_length,
transactional_tables, FALSE) &&
@@ -1814,7 +1876,7 @@ bool multi_update::send_eof()
local_error= 1; // Rollback update
}
}
- if (!transactional_tables)
+ if (!trans_safe)
thd->no_trans_update.all= TRUE;
}
@@ -1826,7 +1888,7 @@ bool multi_update::send_eof()
if (local_error > 0) // if the above log write did not fail ...
{
- /* Safety: If we haven't got an error before (should not happen) */
+ /* Safety: If we haven't got an error before (can happen in do_updates) */
my_message(ER_UNKNOWN_ERROR, "An error occured in multi-table update",
MYF(0));
return TRUE;