Commit 1b538291 authored by Gleb Shchepa's avatar Gleb Shchepa

Bug# 30946: mysqldump silently ignores --default-character-set

            when used with --tab

1) New syntax: added CHARACTER SET clause to the
  SELECT ... INTO OUTFILE (to complement the same clause in
  LOAD DATA INFILE).
  mysqldump is updated to use this in --tab mode.

2) ESCAPED BY/ENCLOSED BY field parameters are documented as
   accepting CHAR argument, however SELECT .. INTO OUTFILE
   silently ignored rests of multisymbol arguments.
   For the symmetrical behavior with LOAD DATA INFILE the
   server has been modified to fail with the same error:

     ERROR 42000: Field separator argument is not what is
                  expected; check the manual

3) Current LOAD DATA INFILE recognizes field/line separators
   "as is" without converting from client charset to data
   file charset. So, it is supposed, that input file of
   LOAD DATA INFILE consists of data in one charset and
   separators in other charset. For the compatibility with
   that [buggy] behaviour SELECT INTO OUTFILE implementation
   has been saved "as is" too, but the new warning message
   has been added:

     Non-ASCII separator arguments are not fully supported

   This message warns on field/line separators that contain
   non-ASCII symbols.


client/mysqldump.c:
  mysqldump has been updated to call SELECT ... INTO OUTFILE
  statement with a charset from the --default-charset command
  line parameter.
mysql-test/r/mysqldump.result:
  Added test case for bug #30946.
mysql-test/r/outfile_loaddata.result:
  Added test case for bug #30946.
mysql-test/t/mysqldump.test:
  Added test case for bug #30946.
mysql-test/t/outfile_loaddata.test:
  Added test case for bug #30946.
sql/field.cc:
  String conversion code has been moved from check_string_copy_error()
  to convert_to_printable() for reuse.
sql/share/errmsg.txt:
  New WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED message has been added.
sql/sql_class.cc:
  The select_export::prepare() method has been modified to:
  
    1) raise the ER_WRONG_FIELD_TERMINATORS error on multisymbol
       ENCLOSED BY/ESCAPED BY field arguments like LOAD DATA INFILE;
  
    2) warn with a new WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED
       message on non-ASCII field or line separators.
  
  The select_export::send_data() merhod has been modified to
  convert item data to output charset (see new SELECT INTO OUTFILE
  syntax). By default the BINARY charset is used for backward
  compatibility.
sql/sql_class.h:
  The select_export::write_cs field added to keep output
  charset.
sql/sql_load.cc:
  mysql_load has been modified to warn on non-ASCII field or
  line separators with a new WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED
  message.
sql/sql_string.cc:
  New global function has been added: convert_to_printable()
  (common code has been moved from check_string_copy_error()).
sql/sql_string.h:
  New String::is_ascii() method and new global convert_to_printable()
  function have been added.
sql/sql_yacc.yy:
  New syntax: added CHARACTER SET clause to the
  SELECT ... INTO OUTFILE (to complement the same clause in
  LOAD DATA INFILE). By default the BINARY charset is used for
  backward compatibility.
parent ad1c06b0
...@@ -3137,6 +3137,12 @@ static void dump_table(char *table, char *db) ...@@ -3137,6 +3137,12 @@ static void dump_table(char *table, char *db)
dynstr_append_checked(&query_string, filename); dynstr_append_checked(&query_string, filename);
dynstr_append_checked(&query_string, "'"); dynstr_append_checked(&query_string, "'");
dynstr_append_checked(&query_string, " /*!50137 CHARACTER SET ");
dynstr_append_checked(&query_string, default_charset == mysql_universal_client_charset ?
my_charset_bin.name : /* backward compatibility */
default_charset);
dynstr_append_checked(&query_string, " */");
if (fields_terminated || enclosed || opt_enclosed || escaped) if (fields_terminated || enclosed || opt_enclosed || escaped)
dynstr_append_checked(&query_string, " FIELDS"); dynstr_append_checked(&query_string, " FIELDS");
......
...@@ -4502,5 +4502,64 @@ DROP PROCEDURE IF EXISTS pr1; ...@@ -4502,5 +4502,64 @@ DROP PROCEDURE IF EXISTS pr1;
DROP TRIGGER IF EXISTS tr1; DROP TRIGGER IF EXISTS tr1;
DROP TABLE IF EXISTS t1; DROP TABLE IF EXISTS t1;
# #
# Bug #30946: mysqldump silently ignores --default-character-set
# when used with --tab
#
# Also see outfile_loaddata.test
#
SET NAMES utf8;
CREATE TABLE t1 (a INT, b CHAR(10) CHARSET koi8r, c CHAR(10) CHARSET latin1);
CREATE TABLE t2 LIKE t1;
INSERT INTO t1 VALUES (1, 'ABC-АБВ', 'DEF-ÂÃÄ'), (2, NULL, NULL);
# error on multi-character ENCLOSED/ESCAPED BY
# default '--default-charset' (binary):
##################################################
1 ABC- DEF-
2 \N \N
##################################################
TRUNCATE t2;
LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' INTO TABLE t2 CHARACTER SET binary;
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a, b, c;
a b c
1 ABC-АБВ DEF-ÂÃÄ
2 NULL NULL
# utf8:
##################################################
1 ABC-АБВ DEF-ÂÃÄ
2 \N \N
##################################################
TRUNCATE t2;
LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' INTO TABLE t2 CHARACTER SET utf8;
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a, b, c;
a b c
1 ABC-АБВ DEF-ÂÃÄ
2 NULL NULL
# latin1 (data corruption is expected):
##################################################
1 ABC-??? DEF-
2 \N \N
##################################################
TRUNCATE t2;
LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' INTO TABLE t2 CHARACTER SET latin1 ;
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a, b, c;
a b c
1 ABC-??? DEF-ÂÃÄ
1 ABC-АБВ DEF-ÂÃÄ
2 NULL NULL
# koi8r (data corruption is expected):
##################################################
1 ABC- DEF-???
2 \N \N
##################################################
TRUNCATE t2;
LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' INTO TABLE t2 CHARACTER SET koi8r;
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a, b, c;
a b c
1 ABC-АБВ DEF-???
1 ABC-АБВ DEF-ÂÃÄ
2 NULL NULL
SET NAMES default;
DROP TABLE t1, t2;
#
# End of 5.1 tests # End of 5.1 tests
# #
...@@ -91,13 +91,152 @@ SELECT HEX(c1) FROM t1; ...@@ -91,13 +91,152 @@ SELECT HEX(c1) FROM t1;
HEX(c1) HEX(c1)
C3 C3
SELECT * INTO OUTFILE 'MYSQLTEST_VARDIR/tmp/bug32533.txt' FIELDS ENCLOSED BY 0xC3 FROM t1; SELECT * INTO OUTFILE 'MYSQLTEST_VARDIR/tmp/bug32533.txt' FIELDS ENCLOSED BY 0xC3 FROM t1;
Warnings:
Warning 1638 Non-ASCII separator arguments are not fully supported
TRUNCATE t1; TRUNCATE t1;
SELECT HEX(LOAD_FILE('MYSQLTEST_VARDIR/tmp/bug32533.txt')); SELECT HEX(LOAD_FILE('MYSQLTEST_VARDIR/tmp/bug32533.txt'));
HEX(LOAD_FILE('MYSQLTEST_VARDIR/tmp/bug32533.txt')) HEX(LOAD_FILE('MYSQLTEST_VARDIR/tmp/bug32533.txt'))
C35CC3C30A C35CC3C30A
LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/bug32533.txt' INTO TABLE t1 FIELDS ENCLOSED BY 0xC3; LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/bug32533.txt' INTO TABLE t1 FIELDS ENCLOSED BY 0xC3;
Warnings:
Warning 1638 Non-ASCII separator arguments are not fully supported
SELECT HEX(c1) FROM t1; SELECT HEX(c1) FROM t1;
HEX(c1) HEX(c1)
C3 C3
DROP TABLE t1; DROP TABLE t1;
# End of 5.0 tests. # End of 5.0 tests.
#
# Bug #30946: mysqldump silently ignores --default-character-set
# when used with --tab
#
# Also see mysqldump.test
#
SET NAMES utf8;
CREATE TABLE t1 (a INT, b CHAR(10) CHARSET koi8r, c CHAR(10) CHARSET latin1);
CREATE TABLE t2 LIKE t1;
INSERT INTO t1 VALUES (1, 'ABC-АБВ', 'DEF-ÂÃÄ'), (2, NULL, NULL);
# Error on multi-character ENCLOSED/ESCAPED BY
SELECT * FROM t1 INTO OUTFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' FIELDS ENCLOSED BY '12345';
ERROR 42000: Field separator argument is not what is expected; check the manual
SELECT * FROM t1 INTO OUTFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' FIELDS ESCAPED BY '12345';
ERROR 42000: Field separator argument is not what is expected; check the manual
# "Not implemented" warning on multibyte ENCLOSED/ESCAPED BY character,
# LOAD DATA rises error or has unpredictable result -- to be fixed later
SELECT * FROM t1 INTO OUTFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' FIELDS ENCLOSED BY 'ъ';
Warnings:
Warning 1638 Non-ASCII separator arguments are not fully supported
LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' INTO TABLE t2 CHARACTER SET binary FIELDS ENCLOSED BY 'ъ';
ERROR 42000: Field separator argument is not what is expected; check the manual
SELECT * FROM t1 INTO OUTFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' FIELDS ESCAPED BY 'ъ';
Warnings:
Warning 1638 Non-ASCII separator arguments are not fully supported
LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' INTO TABLE t2 CHARACTER SET binary FIELDS ESCAPED BY 'ъ';
ERROR 42000: Field separator argument is not what is expected; check the manual
SELECT * FROM t1 INTO OUTFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' FIELDS TERMINATED BY 'ъ';
Warnings:
Warning 1638 Non-ASCII separator arguments are not fully supported
##################################################
1ъABC-ъDEF-
2ъ\Nъ\N
##################################################
TRUNCATE t2;
LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' INTO TABLE t2 CHARACTER SET binary FIELDS TERMINATED BY 'ъ';
Warnings:
Warning 1638 Non-ASCII separator arguments are not fully supported
Warning 1265 Data truncated for column 'a' at row 1
Warning 1261 Row 1 doesn't contain data for all columns
Warning 1261 Row 1 doesn't contain data for all columns
Warning 1265 Data truncated for column 'a' at row 2
Warning 1261 Row 2 doesn't contain data for all columns
Warning 1261 Row 2 doesn't contain data for all columns
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a, b, c;
a b c
1 NULL NULL
1 ABC-АБВ DEF-ÂÃÄ
2 NULL NULL
SELECT * FROM t1 INTO OUTFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' LINES STARTING BY 'ъ';
Warnings:
Warning 1638 Non-ASCII separator arguments are not fully supported
##################################################
ъ1 ABC- DEF-
ъ2 \N \N
##################################################
TRUNCATE t2;
LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' INTO TABLE t2 CHARACTER SET binary LINES STARTING BY 'ъ';
Warnings:
Warning 1638 Non-ASCII separator arguments are not fully supported
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a, b, c;
a b c
1 ABC-АБВ DEF-ÂÃÄ
2 NULL NULL
SELECT * FROM t1 INTO OUTFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' LINES TERMINATED BY 'ъ';
Warnings:
Warning 1638 Non-ASCII separator arguments are not fully supported
##################################################
1 ABC- DEF-ъ2 \N \Nъ##################################################
TRUNCATE t2;
LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' INTO TABLE t2 CHARACTER SET binary LINES TERMINATED BY 'ъ';
Warnings:
Warning 1638 Non-ASCII separator arguments are not fully supported
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a, b, c;
a b c
1 ABC-АБВ DEF-ÂÃÄ
1 ABC-АБВ DEF-ÂÃÄÑŠ2
2 NULL NULL
# Default (binary) charset:
SELECT * INTO OUTFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' FROM t1;
##################################################
1 ABC- DEF-
2 \N \N
##################################################
TRUNCATE t2;
LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' INTO TABLE t2 CHARACTER SET binary;
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a, b, c;
a b c
1 ABC-АБВ DEF-ÂÃÄ
2 NULL NULL
# latin1 charset (INTO OUTFILE warning is expected):
SELECT * INTO OUTFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' CHARACTER SET latin1 FROM t1;
Warnings:
Warning 1366 Incorrect string value: '\xE1\xE2\xF7' for column 'b' at row 1
##################################################
1 ABC-??? DEF-
2 \N \N
##################################################
TRUNCATE t2;
LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' INTO TABLE t2 CHARACTER SET latin1 ;
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a, b, c;
a b c
1 ABC-??? DEF-ÂÃÄ
1 ABC-АБВ DEF-ÂÃÄ
2 NULL NULL
# KOI8-R charset (INTO OUTFILE warning is expected):
SELECT * INTO OUTFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' CHARACTER SET koi8r FROM t1;
Warnings:
Warning 1366 Incorrect string value: '\xC2\xC3\xC4' for column 'c' at row 1
##################################################
1 ABC- DEF-???
2 \N \N
##################################################
TRUNCATE t2;
LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' INTO TABLE t2 CHARACTER SET koi8r;
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a, b, c;
a b c
1 ABC-АБВ DEF-???
1 ABC-АБВ DEF-ÂÃÄ
2 NULL NULL
# UTF-8 charset:
SELECT * INTO OUTFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' CHARACTER SET utf8 FROM t1;
##################################################
1 ABC-АБВ DEF-ÂÃÄ
2 \N \N
##################################################
TRUNCATE t2;
LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/t1.txt' INTO TABLE t2 CHARACTER SET utf8;
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a, b, c;
a b c
1 ABC-АБВ DEF-ÂÃÄ
2 NULL NULL
SET NAMES default;
DROP TABLE t1, t2;
# End of 5.1 tests.
This diff is collapsed.
...@@ -111,3 +111,146 @@ SELECT HEX(c1) FROM t1; ...@@ -111,3 +111,146 @@ SELECT HEX(c1) FROM t1;
DROP TABLE t1; DROP TABLE t1;
--echo # End of 5.0 tests. --echo # End of 5.0 tests.
###########################################################################
--echo #
--echo # Bug #30946: mysqldump silently ignores --default-character-set
--echo # when used with --tab
--echo #
--echo # Also see mysqldump.test
--echo #
SET NAMES utf8;
CREATE TABLE t1 (a INT, b CHAR(10) CHARSET koi8r, c CHAR(10) CHARSET latin1);
CREATE TABLE t2 LIKE t1;
INSERT INTO t1 VALUES (1, 'ABC-АБВ', 'DEF-ÂÃÄ'), (2, NULL, NULL);
--let $file=$MYSQLTEST_VARDIR/tmp/t1.txt
--echo # Error on multi-character ENCLOSED/ESCAPED BY
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--error 1083
--eval SELECT * FROM t1 INTO OUTFILE '$file' FIELDS ENCLOSED BY '12345'
--remove_file $file
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--error 1083
--eval SELECT * FROM t1 INTO OUTFILE '$file' FIELDS ESCAPED BY '12345'
--remove_file $file
--echo # "Not implemented" warning on multibyte ENCLOSED/ESCAPED BY character,
--echo # LOAD DATA rises error or has unpredictable result -- to be fixed later
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--eval SELECT * FROM t1 INTO OUTFILE '$file' FIELDS ENCLOSED BY 'ъ'
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--error 1083 # backward compatibility
--eval LOAD DATA INFILE '$file' INTO TABLE t2 CHARACTER SET binary FIELDS ENCLOSED BY 'ъ'
--remove_file $file
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--eval SELECT * FROM t1 INTO OUTFILE '$file' FIELDS ESCAPED BY 'ъ'
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--error 1083 # backward compatibility
--eval LOAD DATA INFILE '$file' INTO TABLE t2 CHARACTER SET binary FIELDS ESCAPED BY 'ъ'
--remove_file $file
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--eval SELECT * FROM t1 INTO OUTFILE '$file' FIELDS TERMINATED BY 'ъ'
--echo ##################################################
--cat_file $file
--echo ##################################################
TRUNCATE t2;
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--eval LOAD DATA INFILE '$file' INTO TABLE t2 CHARACTER SET binary FIELDS TERMINATED BY 'ъ'
--remove_file $file
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a, b, c;
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--eval SELECT * FROM t1 INTO OUTFILE '$file' LINES STARTING BY 'ъ'
--echo ##################################################
--cat_file $file
--echo ##################################################
TRUNCATE t2;
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--eval LOAD DATA INFILE '$file' INTO TABLE t2 CHARACTER SET binary LINES STARTING BY 'ъ'
--remove_file $file
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a, b, c;
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--eval SELECT * FROM t1 INTO OUTFILE '$file' LINES TERMINATED BY 'ъ'
--echo ##################################################
--cat_file $file
--echo ##################################################
TRUNCATE t2;
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--eval LOAD DATA INFILE '$file' INTO TABLE t2 CHARACTER SET binary LINES TERMINATED BY 'ъ'
--remove_file $file
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a, b, c;
--echo # Default (binary) charset:
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--eval SELECT * INTO OUTFILE '$file' FROM t1
--echo ##################################################
--cat_file $file
--echo ##################################################
TRUNCATE t2;
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--eval LOAD DATA INFILE '$file' INTO TABLE t2 CHARACTER SET binary
--remove_file $file
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a, b, c;
--echo # latin1 charset (INTO OUTFILE warning is expected):
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--eval SELECT * INTO OUTFILE '$file' CHARACTER SET latin1 FROM t1
--echo ##################################################
--cat_file $file
--echo ##################################################
TRUNCATE t2;
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--eval LOAD DATA INFILE '$file' INTO TABLE t2 CHARACTER SET latin1
--remove_file $file
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a, b, c;
--echo # KOI8-R charset (INTO OUTFILE warning is expected):
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--eval SELECT * INTO OUTFILE '$file' CHARACTER SET koi8r FROM t1
--echo ##################################################
--cat_file $file
--echo ##################################################
TRUNCATE t2;
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--eval LOAD DATA INFILE '$file' INTO TABLE t2 CHARACTER SET koi8r
--remove_file $file
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a, b, c;
--echo # UTF-8 charset:
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--eval SELECT * INTO OUTFILE '$file' CHARACTER SET utf8 FROM t1
--echo ##################################################
--cat_file $file
--echo ##################################################
TRUNCATE t2;
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--eval LOAD DATA INFILE '$file' INTO TABLE t2 CHARACTER SET utf8
--remove_file $file
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY a, b, c;
SET NAMES default;
DROP TABLE t1, t2;
###########################################################################
--echo # End of 5.1 tests.
...@@ -6271,48 +6271,15 @@ check_string_copy_error(Field_str *field, ...@@ -6271,48 +6271,15 @@ check_string_copy_error(Field_str *field,
const char *end, const char *end,
CHARSET_INFO *cs) CHARSET_INFO *cs)
{ {
const char *pos, *end_orig; const char *pos;
char tmp[64], *t; char tmp[32];
if (!(pos= well_formed_error_pos) && if (!(pos= well_formed_error_pos) &&
!(pos= cannot_convert_error_pos)) !(pos= cannot_convert_error_pos))
return FALSE; return FALSE;
end_orig= end; convert_to_printable(tmp, sizeof(tmp), pos, (end - pos), cs, 6);
set_if_smaller(end, pos + 6);
for (t= tmp; pos < end; pos++)
{
/*
If the source string is ASCII compatible (mbminlen==1)
and the source character is in ASCII printable range (0x20..0x7F),
then display the character as is.
Otherwise, if the source string is not ASCII compatible (e.g. UCS2),
or the source character is not in the printable range,
then print the character using HEX notation.
*/
if (((unsigned char) *pos) >= 0x20 &&
((unsigned char) *pos) <= 0x7F &&
cs->mbminlen == 1)
{
*t++= *pos;
}
else
{
*t++= '\\';
*t++= 'x';
*t++= _dig_vec_upper[((unsigned char) *pos) >> 4];
*t++= _dig_vec_upper[((unsigned char) *pos) & 15];
}
}
if (end_orig > end)
{
*t++= '.';
*t++= '.';
*t++= '.';
}
*t= '\0';
push_warning_printf(field->table->in_use, push_warning_printf(field->table->in_use,
field->table->in_use->abort_on_warning ? field->table->in_use->abort_on_warning ?
MYSQL_ERROR::WARN_LEVEL_ERROR : MYSQL_ERROR::WARN_LEVEL_ERROR :
......
...@@ -6203,3 +6203,6 @@ ER_RENAMED_NAME ...@@ -6203,3 +6203,6 @@ ER_RENAMED_NAME
swe "Namnndrad" swe "Namnndrad"
ER_TOO_MANY_CONCURRENT_TRXS ER_TOO_MANY_CONCURRENT_TRXS
eng "Too many active concurrent transactions" eng "Too many active concurrent transactions"
WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED
eng "Non-ASCII separator arguments are not fully supported"
...@@ -1790,6 +1790,8 @@ select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u) ...@@ -1790,6 +1790,8 @@ select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
if ((uint) strlen(exchange->file_name) + NAME_LEN >= FN_REFLEN) if ((uint) strlen(exchange->file_name) + NAME_LEN >= FN_REFLEN)
strmake(path,exchange->file_name,FN_REFLEN-1); strmake(path,exchange->file_name,FN_REFLEN-1);
write_cs= exchange->cs ? exchange->cs : &my_charset_bin;
if ((file= create_file(thd, path, exchange, &cache)) < 0) if ((file= create_file(thd, path, exchange, &cache)) < 0)
return 1; return 1;
/* Check if there is any blobs in data */ /* Check if there is any blobs in data */
...@@ -1809,6 +1811,31 @@ select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u) ...@@ -1809,6 +1811,31 @@ select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
non_string_results= TRUE; non_string_results= TRUE;
} }
} }
if (exchange->escaped->numchars() > 1 || exchange->enclosed->numchars() > 1)
{
my_error(ER_WRONG_FIELD_TERMINATORS, MYF(0));
return TRUE;
}
if (exchange->escaped->length() > 1 || exchange->enclosed->length() > 1 ||
!my_isascii(exchange->escaped->ptr()[0]) ||
!my_isascii(exchange->enclosed->ptr()[0]) ||
!exchange->field_term->is_ascii() || !exchange->line_term->is_ascii() ||
!exchange->line_start->is_ascii())
{
/*
Current LOAD DATA INFILE recognizes field/line separators "as is" without
converting from client charset to data file charset. So, it is supposed,
that input file of LOAD DATA INFILE consists of data in one charset and
separators in other charset. For the compatibility with that [buggy]
behaviour SELECT INTO OUTFILE implementation has been saved "as is" too,
but the new warning message has been added:
Non-ASCII separator arguments are not fully supported
*/
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED,
ER(WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED));
}
field_term_length=exchange->field_term->length(); field_term_length=exchange->field_term->length();
field_term_char= field_term_length ? field_term_char= field_term_length ?
(int) (uchar) (*exchange->field_term)[0] : INT_MAX; (int) (uchar) (*exchange->field_term)[0] : INT_MAX;
...@@ -1858,6 +1885,8 @@ bool select_export::send_data(List<Item> &items) ...@@ -1858,6 +1885,8 @@ bool select_export::send_data(List<Item> &items)
DBUG_ENTER("select_export::send_data"); DBUG_ENTER("select_export::send_data");
char buff[MAX_FIELD_WIDTH],null_buff[2],space[MAX_FIELD_WIDTH]; char buff[MAX_FIELD_WIDTH],null_buff[2],space[MAX_FIELD_WIDTH];
char cvt_buff[MAX_FIELD_WIDTH];
String cvt_str(cvt_buff, sizeof(cvt_buff), write_cs);
bool space_inited=0; bool space_inited=0;
String tmp(buff,sizeof(buff),&my_charset_bin),*res; String tmp(buff,sizeof(buff),&my_charset_bin),*res;
tmp.length(0); tmp.length(0);
...@@ -1881,6 +1910,37 @@ bool select_export::send_data(List<Item> &items) ...@@ -1881,6 +1910,37 @@ bool select_export::send_data(List<Item> &items)
bool enclosed = (exchange->enclosed->length() && bool enclosed = (exchange->enclosed->length() &&
(!exchange->opt_enclosed || result_type == STRING_RESULT)); (!exchange->opt_enclosed || result_type == STRING_RESULT));
res=item->str_result(&tmp); res=item->str_result(&tmp);
if (res && !my_charset_same(write_cs, res->charset()) &&
!my_charset_same(write_cs, &my_charset_bin))
{
const char *well_formed_error_pos;
const char *cannot_convert_error_pos;
const char *from_end_pos;
const char *error_pos;
uint32 bytes;
bytes= well_formed_copy_nchars(write_cs, cvt_buff, sizeof(cvt_buff),
res->charset(), res->ptr(), res->length(),
sizeof(cvt_buff),
&well_formed_error_pos,
&cannot_convert_error_pos,
&from_end_pos);
error_pos= well_formed_error_pos ? well_formed_error_pos
: cannot_convert_error_pos;
if (error_pos)
{
char printable_buff[32];
convert_to_printable(printable_buff, sizeof(printable_buff),
error_pos, res->ptr() + res->length() - error_pos,
res->charset(), 6);
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
"string", printable_buff,
item->name, row_count);
}
cvt_str.length(bytes);
res= &cvt_str;
}
if (res && enclosed) if (res && enclosed)
{ {
if (my_b_write(&cache,(uchar*) exchange->enclosed->ptr(), if (my_b_write(&cache,(uchar*) exchange->enclosed->ptr(),
......
...@@ -2493,6 +2493,7 @@ class select_export :public select_to_file { ...@@ -2493,6 +2493,7 @@ class select_export :public select_to_file {
*/ */
bool is_unsafe_field_sep; bool is_unsafe_field_sep;
bool fixed_row_size; bool fixed_row_size;
CHARSET_INFO *write_cs; // output charset
public: public:
select_export(sql_exchange *ex) :select_to_file(ex) {} select_export(sql_exchange *ex) :select_to_file(ex) {}
/** /**
......
...@@ -148,6 +148,17 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, ...@@ -148,6 +148,17 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
MYF(0)); MYF(0));
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
/* Report problems with non-ascii separators */
if (!escaped->is_ascii() || !enclosed->is_ascii() ||
!field_term->is_ascii() ||
!ex->line_term->is_ascii() || !ex->line_start->is_ascii())
{
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED,
ER(WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED));
}
if (open_and_lock_tables(thd, table_list)) if (open_and_lock_tables(thd, table_list))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
......
...@@ -1119,3 +1119,76 @@ void String::swap(String &s) ...@@ -1119,3 +1119,76 @@ void String::swap(String &s)
swap_variables(bool, alloced, s.alloced); swap_variables(bool, alloced, s.alloced);
swap_variables(CHARSET_INFO*, str_charset, s.str_charset); swap_variables(CHARSET_INFO*, str_charset, s.str_charset);
} }
/**
Convert string to printable ASCII string
@details This function converts input string "from" replacing non-ASCII bytes
with hexadecimal sequences ("\xXX") optionally appending "..." to the end of
the resulting string.
This function used in the ER_TRUNCATED_WRONG_VALUE_FOR_FIELD error messages,
e.g. when a string cannot be converted to a result charset.
@param to output buffer
@param to_len size of the output buffer (8 bytes or greater)
@param from input string
@param from_len size of the input string
@param from_cs input charset
@param nbytes maximal number of bytes to convert (from_len if 0)
@return number of bytes in the output string
*/
uint convert_to_printable(char *to, size_t to_len,
const char *from, size_t from_len,
CHARSET_INFO *from_cs, size_t nbytes /*= 0*/)
{
/* needs at least 8 bytes for '\xXX...' and zero byte */
DBUG_ASSERT(to_len >= 8);
char *t= to;
char *t_end= to + to_len - 1; // '- 1' is for the '\0' at the end
const char *f= from;
const char *f_end= from + (nbytes ? min(from_len, nbytes) : from_len);
char *dots= to; // last safe place to append '...'
if (!f || t == t_end)
return 0;
for (; t < t_end && f < f_end; f++)
{
/*
If the source string is ASCII compatible (mbminlen==1)
and the source character is in ASCII printable range (0x20..0x7F),
then display the character as is.
Otherwise, if the source string is not ASCII compatible (e.g. UCS2),
or the source character is not in the printable range,
then print the character using HEX notation.
*/
if (((unsigned char) *f) >= 0x20 &&
((unsigned char) *f) <= 0x7F &&
from_cs->mbminlen == 1)
{
*t++= *f;
}
else
{
if (t_end - t < 4) // \xXX
break;
*t++= '\\';
*t++= 'x';
*t++= _dig_vec_upper[((unsigned char) *f) >> 4];
*t++= _dig_vec_upper[((unsigned char) *f) & 0x0F];
}
if (t_end - t >= 3) // '...'
dots= t;
}
if (f < from + from_len)
memcpy(dots, STRING_WITH_LEN("...\0"));
else
*t= '\0';
return t - to;
}
...@@ -40,6 +40,9 @@ uint32 well_formed_copy_nchars(CHARSET_INFO *to_cs, ...@@ -40,6 +40,9 @@ uint32 well_formed_copy_nchars(CHARSET_INFO *to_cs,
size_t my_copy_with_hex_escaping(CHARSET_INFO *cs, size_t my_copy_with_hex_escaping(CHARSET_INFO *cs,
char *dst, size_t dstlen, char *dst, size_t dstlen,
const char *src, size_t srclen); const char *src, size_t srclen);
uint convert_to_printable(char *to, size_t to_len,
const char *from, size_t from_len,
CHARSET_INFO *from_cs, size_t nbytes= 0);
class String class String
{ {
...@@ -366,6 +369,19 @@ class String ...@@ -366,6 +369,19 @@ class String
{ {
return (s->alloced && Ptr >= s->Ptr && Ptr < s->Ptr + s->str_length); return (s->alloced && Ptr >= s->Ptr && Ptr < s->Ptr + s->str_length);
} }
bool is_ascii() const
{
if (length() == 0)
return TRUE;
if (charset()->mbminlen > 1)
return FALSE;
for (const char *c= ptr(), *end= c + length(); c < end; c++)
{
if (!my_isascii(*c))
return FALSE;
}
return TRUE;
}
}; };
static inline bool check_if_only_end_space(CHARSET_INFO *cs, char *str, static inline bool check_if_only_end_space(CHARSET_INFO *cs, char *str,
......
...@@ -9213,6 +9213,8 @@ into_destination: ...@@ -9213,6 +9213,8 @@ into_destination:
!(lex->result= new select_export(lex->exchange, lex->nest_level))) !(lex->result= new select_export(lex->exchange, lex->nest_level)))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
opt_load_data_charset
{ Lex->exchange->cs= $4; }
opt_field_term opt_line_term opt_field_term opt_line_term
| DUMPFILE TEXT_STRING_filesystem | DUMPFILE TEXT_STRING_filesystem
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment