summaryrefslogtreecommitdiff
path: root/sql/sql_load.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_load.cc')
-rw-r--r--sql/sql_load.cc335
1 files changed, 232 insertions, 103 deletions
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index f61687b3f43..84cee3987ff 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -19,6 +19,7 @@
/* Copy data from a textfile to table */
/* 2006-12 Erik Wetterberg : LOAD XML added */
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "sql_load.h"
@@ -28,7 +29,6 @@
#include <my_dir.h>
#include "sql_view.h" // check_key_in_view
#include "sql_insert.h" // check_that_all_fields_are_given_values,
- // prepare_triggers_for_insert_stmt,
// write_record
#include "sql_acl.h" // INSERT_ACL, UPDATE_ACL
#include "log_event.h" // Delete_file_log_event,
@@ -79,6 +79,81 @@ class READ_INFO {
NET *io_net;
int level; /* for load xml */
+
+#if MYSQL_VERSION_ID >= 100200
+#error This 10.0 and 10.1 specific fix should be removed in 10.2.
+#error Fix read_mbtail() to use my_charlen() instead of my_charlen_tmp()
+#else
+ int my_charlen_tmp(CHARSET_INFO *cs, const char *str, const char *end)
+ {
+ my_wc_t wc;
+ return cs->cset->mb_wc(cs, &wc, (const uchar *) str, (const uchar *) end);
+ }
+
+ /**
+ Read a tail of a multi-byte character.
+ The first byte of the character is assumed to be already
+ read from the file and appended to "str".
+
+ @returns true - if EOF happened unexpectedly
+ @returns false - no EOF happened: found a good multi-byte character,
+ or a bad byte sequence
+
+ Note:
+ The return value depends only on EOF:
+ - read_mbtail() returns "false" is a good character was read, but also
+ - read_mbtail() returns "false" if an incomplete byte sequence was found
+ and no EOF happened.
+
+ For example, suppose we have an ujis file with bytes 0x8FA10A, where:
+ - 0x8FA1 is an incomplete prefix of a 3-byte character
+ (it should be [8F][A1-FE][A1-FE] to make a full 3-byte character)
+ - 0x0A is a line demiliter
+ This file has some broken data, the trailing [A1-FE] is missing.
+
+ In this example it works as follows:
+ - 0x8F is read from the file and put into "data" before the call
+ for read_mbtail()
+ - 0xA1 is read from the file and put into "data" by read_mbtail()
+ - 0x0A is kept in the read queue, so the next read iteration after
+ the current read_mbtail() call will normally find it and recognize as
+ a line delimiter
+ - the current call for read_mbtail() returns "false",
+ because no EOF happened
+ */
+ bool read_mbtail(String *str)
+ {
+ int chlen;
+ if ((chlen= my_charlen_tmp(read_charset, str->end() - 1, str->end())) == 1)
+ return false; // Single byte character found
+ for (uint32 length0= str->length() - 1 ; MY_CS_IS_TOOSMALL(chlen); )
+ {
+ int chr= GET;
+ if (chr == my_b_EOF)
+ {
+ DBUG_PRINT("info", ("read_mbtail: chlen=%d; unexpected EOF", chlen));
+ return true; // EOF
+ }
+ str->append(chr);
+ chlen= my_charlen_tmp(read_charset, str->ptr() + length0, str->end());
+ if (chlen == MY_CS_ILSEQ)
+ {
+ /**
+ It has been an incomplete (but a valid) sequence so far,
+ but the last byte turned it into a bad byte sequence.
+ Unget the very last byte.
+ */
+ str->length(str->length() - 1);
+ PUSH(chr);
+ DBUG_PRINT("info", ("read_mbtail: ILSEQ"));
+ return false; // Bad byte sequence
+ }
+ }
+ DBUG_PRINT("info", ("read_mbtail: chlen=%d", chlen));
+ return false; // Good multi-byte character
+ }
+#endif
+
public:
bool error,line_cuted,found_null,enclosed;
uchar *row_start, /* Found row starts here */
@@ -226,7 +301,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
!field_term->is_ascii() ||
!ex->line_term->is_ascii() || !ex->line_start->is_ascii())
{
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED,
ER(WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED));
}
@@ -272,7 +347,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
*/
if (unique_table(thd, table_list, table_list->next_global, 0))
{
- my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name);
+ my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name,
+ "LOAD DATA");
DBUG_RETURN(TRUE);
}
@@ -294,7 +370,6 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
fields_vars.push_back(item->real_item());
}
bitmap_set_all(table->write_set);
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
/*
Let us also prepare SET clause, altough it is probably empty
in this case.
@@ -310,27 +385,28 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
setup_fields(thd, 0, set_fields, MARK_COLUMNS_WRITE, 0, NULL, 0) ||
check_that_all_fields_are_given_values(thd, table, table_list))
DBUG_RETURN(TRUE);
- /*
- Check whenever TIMESTAMP field with auto-set feature specified
- explicitly.
- */
- if (table->timestamp_field)
- {
- if (bitmap_is_set(table->write_set,
- table->timestamp_field->field_index))
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
- else
- {
- bitmap_set_bit(table->write_set,
- table->timestamp_field->field_index);
- }
- }
+ /* Add all fields with default functions to table->write_set. */
+ if (table->default_field)
+ table->mark_default_fields_for_write();
/* Fix the expressions in SET clause */
if (setup_fields(thd, 0, set_values, MARK_COLUMNS_READ, 0, NULL, 0))
DBUG_RETURN(TRUE);
}
- prepare_triggers_for_insert_stmt(table);
+ table->prepare_triggers_for_insert_stmt_or_event();
+ table->mark_columns_needed_for_insert();
+
+ if (table->vfield)
+ {
+ for (Field **vfield_ptr= table->vfield; *vfield_ptr; vfield_ptr++)
+ {
+ if ((*vfield_ptr)->stored_in_db)
+ {
+ thd->lex->unit.insert_table_with_stored_vcol= table;
+ break;
+ }
+ }
+ }
uint tot_length=0;
bool use_blobs= 0, use_vars= 0;
@@ -396,11 +472,11 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
MY_RETURN_REAL_PATH);
}
- if (thd->slave_thread)
+ if (thd->rgi_slave)
{
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
- if (strncmp(active_mi->rli.slave_patternload_file, name,
- active_mi->rli.slave_patternload_file_size))
+ if (strncmp(thd->rgi_slave->rli->slave_patternload_file, name,
+ thd->rgi_slave->rli->slave_patternload_file_size))
{
/*
LOAD DATA INFILE in the slave SQL Thread can only read from
@@ -494,8 +570,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
}
thd_proc_info(thd, "reading file");
- if (!(error=test(read_info.error)))
+ if (!(error= MY_TEST(read_info.error)))
{
+ table->reset_default_fields();
table->next_number_field=table->found_next_number_field;
if (ignore ||
handle_duplicates == DUP_REPLACE)
@@ -508,10 +585,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
table->file->ha_start_bulk_insert((ha_rows) 0);
table->copy_blobs=1;
- thd->abort_on_warning= (!ignore &&
- (thd->variables.sql_mode &
- (MODE_STRICT_TRANS_TABLES |
- MODE_STRICT_ALL_TABLES)));
+ thd->abort_on_warning= !ignore && thd->is_strict_mode();
thd_progress_init(thd, 2);
if (ex->filetype == FILETYPE_XML) /* load xml */
@@ -528,7 +602,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
*enclosed, skip_lines, ignore);
thd_proc_info(thd, "End bulk insert");
- thd_progress_next_stage(thd);
+ if (!error)
+ thd_progress_next_stage(thd);
if (thd->locked_tables_mode <= LTM_LOCK_TABLES &&
table->file->ha_end_bulk_insert() && !error)
{
@@ -624,7 +699,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
}
sprintf(name, ER(ER_LOAD_INFO), (ulong) info.records, (ulong) info.deleted,
(ulong) (info.records - info.copied),
- (ulong) thd->warning_info->statement_warn_count());
+ (long) thd->get_stmt_da()->current_statement_warn_count());
if (thd->transaction.stmt.modified_non_trans_table)
thd->transaction.all.modified_non_trans_table= TRUE;
@@ -865,12 +940,16 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (pos == read_info.row_end)
{
thd->cuted_fields++; /* Not enough fields */
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_TOO_FEW_RECORDS,
ER(ER_WARN_TOO_FEW_RECORDS),
- thd->warning_info->current_row_for_warning());
+ thd->get_stmt_da()->current_row_for_warning());
+ /*
+ Timestamp fields that are NOT NULL are autoupdated if there is no
+ corresponding value in the data file.
+ */
if (!field->maybe_null() && field->type() == FIELD_TYPE_TIMESTAMP)
- ((Field_timestamp*) field)->set_time();
+ field->set_time();
}
else
{
@@ -885,21 +964,23 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if ((pos+=length) > read_info.row_end)
pos= read_info.row_end; /* Fills rest with space */
}
+ /* Do not auto-update this field. */
+ field->set_has_explicit_value();
}
if (pos != read_info.row_end)
{
thd->cuted_fields++; /* To long row */
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_TOO_MANY_RECORDS,
ER(ER_WARN_TOO_MANY_RECORDS),
- thd->warning_info->current_row_for_warning());
+ thd->get_stmt_da()->current_row_for_warning());
}
if (thd->killed ||
- fill_record_n_invoke_before_triggers(thd, set_fields, set_values,
+ fill_record_n_invoke_before_triggers(thd, table, set_fields, set_values,
ignore_check_option_errors,
- table->triggers,
- TRG_EVENT_INSERT))
+ TRG_EVENT_INSERT) ||
+ (table->default_field && table->update_default_fields()))
DBUG_RETURN(1);
switch (table_list->view_check_option(thd,
@@ -925,15 +1006,15 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (read_info.line_cuted)
{
thd->cuted_fields++; /* To long row */
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_TOO_MANY_RECORDS,
ER(ER_WARN_TOO_MANY_RECORDS),
- thd->warning_info->current_row_for_warning());
+ thd->get_stmt_da()->current_row_for_warning());
}
- thd->warning_info->inc_current_row_for_warning();
+ thd->get_stmt_da()->inc_current_row_for_warning();
continue_loop:;
}
- DBUG_RETURN(test(read_info.error));
+ DBUG_RETURN(MY_TEST(read_info.error));
}
@@ -997,7 +1078,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
pos=read_info.row_start;
length=(uint) (read_info.row_end-pos);
- real_item= item->filed_for_view_update();
+ real_item= item->field_for_view_update();
if ((!read_info.enclosed &&
(enclosed_length && length == 4 &&
@@ -1020,18 +1101,24 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (field->reset())
{
my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0), field->field_name,
- thd->warning_info->current_row_for_warning());
+ thd->get_stmt_da()->current_row_for_warning());
DBUG_RETURN(1);
}
field->set_null();
if (!field->maybe_null())
{
+ /*
+ Timestamp fields that are NOT NULL are autoupdated if there is no
+ corresponding value in the data file.
+ */
if (field->type() == MYSQL_TYPE_TIMESTAMP)
- ((Field_timestamp*) field)->set_time();
+ field->set_time();
else if (field != table->next_number_field)
- field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN,
ER_WARN_NULL_TO_NOTNULL, 1);
}
+ /* Do not auto-update this field. */
+ field->set_has_explicit_value();
}
continue;
@@ -1055,6 +1142,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (field == table->next_number_field)
table->auto_increment_field_not_null= TRUE;
field->store((char*) pos, length, read_info.read_charset);
+ field->set_has_explicit_value();
}
}
@@ -1075,7 +1163,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
break;
for (; item ; item= it++)
{
- Item_field *real_item= item->filed_for_view_update();
+ Item_field *real_item= item->field_for_view_update();
if (item->type() == Item::STRING_ITEM)
{
((Item_user_var_as_out_param *)item)->set_null_value(
@@ -1092,11 +1180,12 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (field->reset())
{
my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0),field->field_name,
- thd->warning_info->current_row_for_warning());
+ thd->get_stmt_da()->current_row_for_warning());
DBUG_RETURN(1);
}
if (!field->maybe_null() && field->type() == FIELD_TYPE_TIMESTAMP)
- ((Field_timestamp*) field)->set_time();
+ field->set_time();
+ field->set_has_explicit_value();
/*
TODO: We probably should not throw warning for each field.
But how about intention to always have the same number
@@ -1104,19 +1193,19 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
in the end ?)
*/
thd->cuted_fields++;
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_TOO_FEW_RECORDS,
ER(ER_WARN_TOO_FEW_RECORDS),
- thd->warning_info->current_row_for_warning());
+ thd->get_stmt_da()->current_row_for_warning());
}
}
}
if (thd->killed ||
- fill_record_n_invoke_before_triggers(thd, set_fields, set_values,
+ fill_record_n_invoke_before_triggers(thd, table, set_fields, set_values,
ignore_check_option_errors,
- table->triggers,
- TRG_EVENT_INSERT))
+ TRG_EVENT_INSERT) ||
+ (table->default_field && table->update_default_fields()))
DBUG_RETURN(1);
switch (table_list->view_check_option(thd,
@@ -1141,16 +1230,16 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (read_info.line_cuted)
{
thd->cuted_fields++; /* To long row */
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_TOO_MANY_RECORDS, ER(ER_WARN_TOO_MANY_RECORDS),
- thd->warning_info->current_row_for_warning());
+ thd->get_stmt_da()->current_row_for_warning());
if (thd->killed)
DBUG_RETURN(1);
}
- thd->warning_info->inc_current_row_for_warning();
+ thd->get_stmt_da()->inc_current_row_for_warning();
continue_loop:;
}
- DBUG_RETURN(test(read_info.error));
+ DBUG_RETURN(MY_TEST(read_info.error));
}
@@ -1226,11 +1315,13 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (!field->maybe_null())
{
if (field->type() == FIELD_TYPE_TIMESTAMP)
- ((Field_timestamp *) field)->set_time();
+ field->set_time();
else if (field != table->next_number_field)
- field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN,
ER_WARN_NULL_TO_NOTNULL, 1);
}
+ /* Do not auto-update this field. */
+ field->set_has_explicit_value();
}
else
((Item_user_var_as_out_param *) item)->set_null_value(cs);
@@ -1245,6 +1336,7 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (field == table->next_number_field)
table->auto_increment_field_not_null= TRUE;
field->store((char *) tag->value.ptr(), tag->value.length(), cs);
+ field->set_has_explicit_value();
}
else
((Item_user_var_as_out_param *) item)->set_value(
@@ -1278,10 +1370,10 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
in the end ?)
*/
thd->cuted_fields++;
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_TOO_FEW_RECORDS,
ER(ER_WARN_TOO_FEW_RECORDS),
- thd->warning_info->current_row_for_warning());
+ thd->get_stmt_da()->current_row_for_warning());
}
else
((Item_user_var_as_out_param *)item)->set_null_value(cs);
@@ -1289,10 +1381,10 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
}
if (thd->killed ||
- fill_record_n_invoke_before_triggers(thd, set_fields, set_values,
+ fill_record_n_invoke_before_triggers(thd, table, set_fields, set_values,
ignore_check_option_errors,
- table->triggers,
- TRG_EVENT_INSERT))
+ TRG_EVENT_INSERT) ||
+ (table->default_field && table->update_default_fields()))
DBUG_RETURN(1);
switch (table_list->view_check_option(thd,
@@ -1312,10 +1404,10 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
its default value at the beginning of each loop iteration.
*/
thd->transaction.stmt.modified_non_trans_table= no_trans_update_stmt;
- thd->warning_info->inc_current_row_for_warning();
+ thd->get_stmt_da()->inc_current_row_for_warning();
continue_loop:;
}
- DBUG_RETURN(test(read_info.error) || thd->is_error());
+ DBUG_RETURN(MY_TEST(read_info.error) || thd->is_error());
} /* load xml end */
@@ -1391,19 +1483,19 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs,
line_term_char= line_term_length ? line_term_ptr[0] : INT_MAX;
/* Set of a stack for unget if long terminators */
- uint length= max(cs->mbmaxlen, max(field_term_length, line_term_length)) + 1;
+ uint length= MY_MAX(cs->mbmaxlen, MY_MAX(field_term_length, line_term_length)) + 1;
set_if_bigger(length,line_start.length());
stack=stack_pos=(int*) sql_alloc(sizeof(int)*length);
- if (!(buffer=(uchar*) my_malloc(buff_length+1,MYF(0))))
- error=1; /* purecov: inspected */
+ if (!(buffer=(uchar*) my_malloc(buff_length+1,MYF(MY_WME | MY_THREAD_SPECIFIC))))
+ error= 1; /* purecov: inspected */
else
{
end_of_buff=buffer+buff_length;
if (init_io_cache(&cache,(get_it_from_net) ? -1 : file, 0,
(get_it_from_net) ? READ_NET :
(is_fifo ? READ_FIFO : READ_CACHE),0L,1,
- MYF(MY_WME)))
+ MYF(MY_WME | MY_THREAD_SPECIFIC)))
{
my_free(buffer); /* purecov: inspected */
buffer= NULL;
@@ -1463,6 +1555,54 @@ inline int READ_INFO::terminator(const uchar *ptr,uint length)
}
+/**
+ Read a field.
+
+ The data in the loaded file was presumably escaped using
+ - either select_export::send_data() OUTFILE
+ - or mysql_real_escape_string()
+ using the same character set with the one specified in the current
+ "LOAD DATA INFILE ... CHARACTER SET ..." (or the default LOAD character set).
+
+ Note, non-escaped multi-byte characters are scanned as a single entity.
+ This is needed to correctly distinguish between:
+ - 0x5C as an escape character versus
+ - 0x5C as the second byte in a multi-byte sequence (big5, cp932, gbk, sjis)
+
+ Parts of escaped multi-byte characters are scanned on different loop
+ iterations. See the comment about 0x5C handling in select_export::send_data()
+ in sql_class.cc.
+
+ READ_INFO::read_field() does not check wellformedness.
+ Raising wellformedness errors or warnings in READ_INFO::read_field()
+ would be wrong, as the data after unescaping can go into a BLOB field,
+ or into a TEXT/VARCHAR field of a different character set.
+ The loop below only makes sure to revert escaping made by
+ select_export::send_data() or mysql_real_escape_string().
+ Wellformedness is checked later, during Field::store(str,length,cs) time.
+
+ Note, in some cases users can supply data which did not go through
+ escaping properly. For example, utf8 "\<C3><A4>"
+ (backslash followed by LATIN SMALL LETTER A WITH DIAERESIS)
+ is improperly escaped data that could not be generated by
+ select_export::send_data() / mysql_real_escape_string():
+ - either there should be two backslashes: "\\<C3><A4>"
+ - or there should be no backslashes at all: "<C3><A4>"
+ "\<C3>" and "<A4> are scanned on two different loop iterations and
+ store "<C3><A4>" into the field.
+
+ Note, adding useless escapes before multi-byte characters like in the
+ example above is safe in case of utf8, but is not safe in case of
+ character sets that have escape_with_backslash_is_dangerous==TRUE,
+ such as big5, cp932, gbk, sjis. This can lead to mis-interpretation of the
+ data. Suppose we have a big5 character "<EE><5C>" followed by <30> (digit 0).
+ If we add an extra escape before this sequence, then we'll get
+ <5C><EE><5C><30>. The first loop iteration will turn <5C><EE> into <EE>.
+ The second loop iteration will turn <5C><30> into <30>.
+ So the program that generates a dump file for further use with LOAD DATA
+ must make sure to use escapes properly.
+*/
+
int READ_INFO::read_field()
{
int chr,found_enclosed_char;
@@ -1499,7 +1639,8 @@ int READ_INFO::read_field()
for (;;)
{
- while ( to < end_of_buff)
+ // Make sure we have enough space for the longest multi-byte character.
+ while ( to + read_charset->mbmaxlen < end_of_buff)
{
chr = GET;
if (chr == my_b_EOF)
@@ -1587,45 +1728,33 @@ int READ_INFO::read_field()
}
}
#ifdef USE_MB
- if (my_mbcharlen(read_charset, chr) > 1 &&
- to + my_mbcharlen(read_charset, chr) <= end_of_buff)
+#endif
+ *to++ = (uchar) chr;
+#if MYSQL_VERSION_ID >= 100200
+#error This 10.0 and 10.1 specific fix should be removed in 10.2
+#else
+ if (my_mbcharlen(read_charset, (uchar) chr) > 1)
{
- uchar* p= to;
- int ml, i;
- *to++ = chr;
-
- ml= my_mbcharlen(read_charset, chr);
-
- for (i= 1; i < ml; i++)
- {
- chr= GET;
- if (chr == my_b_EOF)
- {
- /*
- Need to back up the bytes already ready from illformed
- multi-byte char
- */
- to-= i;
- goto found_eof;
- }
- *to++ = chr;
- }
- if (my_ismbchar(read_charset,
- (const char *)p,
- (const char *)to))
- continue;
- for (i= 0; i < ml; i++)
- PUSH(*--to);
- chr= GET;
+ /*
+ A known MBHEAD found. Try to scan the full multi-byte character.
+ Otherwise, a possible following second byte 0x5C would be
+ mis-interpreted as an escape on the next iteration.
+ (Important for big5, gbk, sjis, cp932).
+ */
+ String tmp((char *) to - 1, read_charset->mbmaxlen, read_charset);
+ tmp.length(1);
+ bool eof= read_mbtail(&tmp);
+ to+= tmp.length() - 1;
+ if (eof)
+ goto found_eof;
}
#endif
- *to++ = (uchar) chr;
}
/*
** We come here if buffer is too small. Enlarge it and continue
*/
if (!(new_buffer=(uchar*) my_realloc((char*) buffer,buff_length+1+IO_SIZE,
- MYF(MY_WME))))
+ MYF(MY_WME | MY_THREAD_SPECIFIC))))
return (error=1);
to=new_buffer + (to-buffer);
buffer=new_buffer;