Fixed bug #29294.
The `SELECT 'r' INTO OUTFILE ... FIELDS ENCLOSED BY 'r' ' statement encoded the 'r' string to a 4 byte string of value x'725c7272' (sequence of 4 characters: r\rr). The LOAD DATA statement decoded this string to a 1 byte string of value x'0d' (ASCII Carriage Return character) instead of the original 'r' character. The same error also happened with the FIELDS ENCLOSED BY clause followed by special characters: 'n', 't', 'r', 'b', '0', 'Z' and 'N'. NOTE 1: This is a result of the undocumented feature: the LOAD DATA INFILE recognises 2-byte input sequences like \n, \t, \r and \Z in addition to documented 2-byte sequences: \0 and \N. This feature should be documented (here backspace character is a default ESCAPED BY character, in the real-life example it may be any ESCAPED BY character). NOTE 2, changed behaviour: Now the `SELECT INTO OUTFILE' statement with the `FIELDS ENCLOSED BY' clause followed by one of: 'n', 't', 'r', 'b', '0', 'Z' or 'N' characters encodes this special character itself by doubling it ('r' --> 'rr'), not by prepending it with an escape character. sql/sql_class.h: Fixed bug #29294. The ESCAPE_CHARS macro constant is defined to enumerate symbolic names of espace-sequences like '\n', '\t' etc. The select_export::is_ambiguous_field_sep field has been added to distinguish special values of the field_sep field from another values (see ESCAPE_CHARS). sql/sql_class.cc: Fixed bug #29294. The select_export::send_data method has been modified to encode special values of the field_sep field by doubling of those values instead of prepending them with a value of the escape_char field. Example: The SELECT 'r' INTO OUTFILE FIELDS ENCLOSED BY 'r' now produces the 'rr' output string instead of x'5c72' (i.e. instead of sequence of 2 bytes: \ and r). sql/sql_load.cc: Fixed bug #29294. Added commentary for the READ_INFO::unescape method. mysql-test/t/loaddata.test: Updated test case for bug #29294. mysql-test/r/loaddata.result: Updated test case for bug #29294.
This commit is contained in:
parent
7fbf6303d2
commit
166d3c9186
@ -1,4 +1,4 @@
|
|||||||
drop table if exists t1;
|
drop table if exists t1,t2;
|
||||||
create table t1 (a date, b date, c date not null, d date);
|
create table t1 (a date, b date, c date not null, d date);
|
||||||
load data infile '../../std_data/loaddata1.dat' into table t1 fields terminated by ',';
|
load data infile '../../std_data/loaddata1.dat' into table t1 fields terminated by ',';
|
||||||
Warnings:
|
Warnings:
|
||||||
@ -85,3 +85,66 @@ field1 field2
|
|||||||
a"b cd"ef
|
a"b cd"ef
|
||||||
a"b c"d"e
|
a"b c"d"e
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
CREATE TABLE t1 (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
c1 VARCHAR(255)
|
||||||
|
);
|
||||||
|
CREATE TABLE t2 (
|
||||||
|
id INT,
|
||||||
|
c2 VARCHAR(255)
|
||||||
|
);
|
||||||
|
INSERT INTO t1 (c1) VALUES
|
||||||
|
('r'), ('rr'), ('rrr'), ('rrrr'),
|
||||||
|
('.r'), ('.rr'), ('.rrr'), ('.rrrr'),
|
||||||
|
('r.'), ('rr.'), ('rrr.'), ('rrrr.'),
|
||||||
|
('.r.'), ('.rr.'), ('.rrr.'), ('.rrrr.'),
|
||||||
|
('\r'), ('\\rr'), ('\\\rr'), ('\\\\rr');
|
||||||
|
SELECT * FROM t1;
|
||||||
|
id c1
|
||||||
|
1 r
|
||||||
|
2 rr
|
||||||
|
3 rrr
|
||||||
|
4 rrrr
|
||||||
|
5 .r
|
||||||
|
6 .rr
|
||||||
|
7 .rrr
|
||||||
|
8 .rrrr
|
||||||
|
9 r.
|
||||||
|
10 rr.
|
||||||
|
11 rrr.
|
||||||
|
12 rrrr.
|
||||||
|
13 .r.
|
||||||
|
14 .rr.
|
||||||
|
15 .rrr.
|
||||||
|
16 .rrrr.
|
||||||
|
17
|
||||||
|
18 \rr
|
||||||
|
19 \
r
|
||||||
|
20 \\rr
|
||||||
|
SELECT * INTO OUTFILE 'MYSQL_TEST_DIR/var/tmp/t1' FIELDS ENCLOSED BY 'r' FROM t1;
|
||||||
|
r1r rrrr
|
||||||
|
r2r rrrrrr
|
||||||
|
r3r rrrrrrrr
|
||||||
|
r4r rrrrrrrrrr
|
||||||
|
r5r r.rrr
|
||||||
|
r6r r.rrrrr
|
||||||
|
r7r r.rrrrrrr
|
||||||
|
r8r r.rrrrrrrrr
|
||||||
|
r9r rrr.r
|
||||||
|
r10r rrrrr.r
|
||||||
|
r11r rrrrrrr.r
|
||||||
|
r12r rrrrrrrrr.r
|
||||||
|
r13r r.rr.r
|
||||||
|
r14r r.rrrr.r
|
||||||
|
r15r r.rrrrrr.r
|
||||||
|
r16r r.rrrrrrrr.r
|
||||||
|
r17r r
r
|
||||||
|
r18r r\\rrrrr
|
||||||
|
r19r r\\
rrr
|
||||||
|
r20r r\\\\rrrrr
|
||||||
|
LOAD DATA INFILE 'MYSQL_TEST_DIR/var/tmp/t1' INTO TABLE t2 FIELDS ENCLOSED BY 'r';
|
||||||
|
SELECT t1.id, c1, c2 FROM t1 LEFT JOIN t2 ON t1.id=t2.id WHERE c1 != c2;
|
||||||
|
id c1 c2
|
||||||
|
SELECT t1.id, c1, c2 FROM t1 RIGHT JOIN t2 ON t1.id=t2.id WHERE c1 != c2;
|
||||||
|
id c1 c2
|
||||||
|
DROP TABLE t1,t2;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
--disable_warnings
|
--disable_warnings
|
||||||
drop table if exists t1;
|
drop table if exists t1,t2;
|
||||||
--enable_warnings
|
--enable_warnings
|
||||||
|
|
||||||
create table t1 (a date, b date, c date not null, d date);
|
create table t1 (a date, b date, c date not null, d date);
|
||||||
@ -67,4 +67,40 @@ load data infile '../../std_data/loaddata5.dat' into table t1 fields terminated
|
|||||||
select * from t1;
|
select * from t1;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Bug #29294 SELECT INTO OUTFILE/LOAD DATA INFILE with special
|
||||||
|
# characters in the FIELDS ENCLOSED BY clause
|
||||||
|
#
|
||||||
|
|
||||||
|
CREATE TABLE t1 (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
c1 VARCHAR(255)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE t2 (
|
||||||
|
id INT,
|
||||||
|
c2 VARCHAR(255)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO t1 (c1) VALUES
|
||||||
|
('r'), ('rr'), ('rrr'), ('rrrr'),
|
||||||
|
('.r'), ('.rr'), ('.rrr'), ('.rrrr'),
|
||||||
|
('r.'), ('rr.'), ('rrr.'), ('rrrr.'),
|
||||||
|
('.r.'), ('.rr.'), ('.rrr.'), ('.rrrr.'),
|
||||||
|
('\r'), ('\\rr'), ('\\\rr'), ('\\\\rr');
|
||||||
|
SELECT * FROM t1;
|
||||||
|
|
||||||
|
--exec rm -f $MYSQL_TEST_DIR/var/tmp/t1
|
||||||
|
--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR
|
||||||
|
eval SELECT * INTO OUTFILE '$MYSQL_TEST_DIR/var/tmp/t1' FIELDS ENCLOSED BY 'r' FROM t1;
|
||||||
|
--exec cat $MYSQL_TEST_DIR/var/tmp/t1
|
||||||
|
|
||||||
|
--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR
|
||||||
|
eval LOAD DATA INFILE '$MYSQL_TEST_DIR/var/tmp/t1' INTO TABLE t2 FIELDS ENCLOSED BY 'r';
|
||||||
|
SELECT t1.id, c1, c2 FROM t1 LEFT JOIN t2 ON t1.id=t2.id WHERE c1 != c2;
|
||||||
|
SELECT t1.id, c1, c2 FROM t1 RIGHT JOIN t2 ON t1.id=t2.id WHERE c1 != c2;
|
||||||
|
|
||||||
|
--exec rm $MYSQL_TEST_DIR/var/tmp/t1
|
||||||
|
DROP TABLE t1,t2;
|
||||||
|
|
||||||
# End of 4.1 tests
|
# End of 4.1 tests
|
||||||
|
@ -1020,6 +1020,7 @@ select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
|
|||||||
field_sep_char= (exchange->enclosed->length() ? (*exchange->enclosed)[0] :
|
field_sep_char= (exchange->enclosed->length() ? (*exchange->enclosed)[0] :
|
||||||
field_term_length ? (*exchange->field_term)[0] : INT_MAX);
|
field_term_length ? (*exchange->field_term)[0] : INT_MAX);
|
||||||
escape_char= (exchange->escaped->length() ? (*exchange->escaped)[0] : -1);
|
escape_char= (exchange->escaped->length() ? (*exchange->escaped)[0] : -1);
|
||||||
|
is_ambiguous_field_sep= strchr(ESCAPE_CHARS, field_sep_char);
|
||||||
line_sep_char= (exchange->line_term->length() ?
|
line_sep_char= (exchange->line_term->length() ?
|
||||||
(*exchange->line_term)[0] : INT_MAX);
|
(*exchange->line_term)[0] : INT_MAX);
|
||||||
if (!field_term_length)
|
if (!field_term_length)
|
||||||
@ -1113,7 +1114,9 @@ bool select_export::send_data(List<Item> &items)
|
|||||||
(int) *pos == line_sep_char || !*pos)
|
(int) *pos == line_sep_char || !*pos)
|
||||||
{
|
{
|
||||||
char tmp_buff[2];
|
char tmp_buff[2];
|
||||||
tmp_buff[0]= escape_char;
|
tmp_buff[0]= ((int) *pos == field_sep_char &&
|
||||||
|
is_ambiguous_field_sep) ?
|
||||||
|
field_sep_char : escape_char;
|
||||||
tmp_buff[1]= *pos ? *pos : '0';
|
tmp_buff[1]= *pos ? *pos : '0';
|
||||||
if (my_b_write(&cache,(byte*) start,(uint) (pos-start)) ||
|
if (my_b_write(&cache,(byte*) start,(uint) (pos-start)) ||
|
||||||
my_b_write(&cache,(byte*) tmp_buff,2))
|
my_b_write(&cache,(byte*) tmp_buff,2))
|
||||||
|
@ -1224,9 +1224,18 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#define ESCAPE_CHARS "ntrb0ZN" // keep synchronous with READ_INFO::unescape
|
||||||
|
|
||||||
|
|
||||||
class select_export :public select_to_file {
|
class select_export :public select_to_file {
|
||||||
uint field_term_length;
|
uint field_term_length;
|
||||||
int field_sep_char,escape_char,line_sep_char;
|
int field_sep_char,escape_char,line_sep_char;
|
||||||
|
/*
|
||||||
|
The is_ambiguous_field_sep field is true if a value of the field_sep_char
|
||||||
|
field is one of the 'n', 't', 'r' etc characters
|
||||||
|
(see the READ_INFO::unescape method and the ESCAPE_CHARS constant value).
|
||||||
|
*/
|
||||||
|
bool is_ambiguous_field_sep;
|
||||||
bool fixed_row_size;
|
bool fixed_row_size;
|
||||||
public:
|
public:
|
||||||
select_export(sql_exchange *ex) :select_to_file(ex) {}
|
select_export(sql_exchange *ex) :select_to_file(ex) {}
|
||||||
|
@ -611,6 +611,7 @@ read_sep_field(THD *thd,COPY_INFO &info,TABLE *table,
|
|||||||
char
|
char
|
||||||
READ_INFO::unescape(char chr)
|
READ_INFO::unescape(char chr)
|
||||||
{
|
{
|
||||||
|
/* keep this switch synchornous with the ESCAPE_CHARS macro */
|
||||||
switch(chr) {
|
switch(chr) {
|
||||||
case 'n': return '\n';
|
case 'n': return '\n';
|
||||||
case 't': return '\t';
|
case 't': return '\t';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user