From f8683bfb4439e6ac70bbe3de5d88d1cc275f6011 Mon Sep 17 00:00:00 2001 From: "igor@olga.mysql.com" <> Date: Sun, 1 Jul 2007 15:33:28 -0700 Subject: [PATCH 1/7] Fixed bug #25798. This bug may manifest itself not only with the queries for which the index-merge access method is chosen. It also may display itself for queries with DISTINCT. The bug was in how the Unique::get method used the merge_buffers function. To compare elements in the the queue employed by merge_buffers() it must use the buffpek_compare function rather than the function for binary comparison. --- mysql-test/r/innodb_mysql.result | 51 ++++++++++++++++++++++++++++++ mysql-test/t/innodb_mysql.test | 54 ++++++++++++++++++++++++++++++++ sql/filesort.cc | 18 ++++++++--- sql/sql_sort.h | 9 ++++++ sql/uniques.cc | 13 ++++---- 5 files changed, 134 insertions(+), 11 deletions(-) diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index 11c7c2aedc9..df1c2165c45 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -676,4 +676,55 @@ INSERT INTO t1 VALUES (1); switch to connection default SET AUTOCOMMIT=default; DROP TABLE t1,t2; +CREATE TABLE t1 ( +id int NOT NULL auto_increment PRIMARY KEY, +b int NOT NULL, +c datetime NOT NULL, +INDEX idx_b(b), +INDEX idx_c(c) +) ENGINE=InnoDB; +CREATE TABLE t2 ( +b int NOT NULL auto_increment PRIMARY KEY, +c datetime NOT NULL +) ENGINE= MyISAM; +INSERT INTO t2(c) VALUES ('2007-01-01'); +INSERT INTO t2(c) SELECT c FROM t2; +INSERT INTO t2(c) SELECT c FROM t2; +INSERT INTO t2(c) SELECT c FROM t2; +INSERT INTO t2(c) SELECT c FROM t2; +INSERT INTO t2(c) SELECT c FROM t2; +INSERT INTO t2(c) SELECT c FROM t2; +INSERT INTO t2(c) SELECT c FROM t2; +INSERT INTO t2(c) SELECT c FROM t2; +INSERT INTO t2(c) SELECT c FROM t2; +INSERT INTO t2(c) SELECT c FROM t2; +INSERT INTO t1(b,c) SELECT b,c FROM t2; +UPDATE t2 SET c='2007-01-02'; +INSERT INTO t1(b,c) SELECT b,c FROM t2; +UPDATE t2 SET c='2007-01-03'; +INSERT INTO t1(b,c) SELECT b,c FROM t2; +set @@sort_buffer_size=8192; +SELECT COUNT(*) FROM t1; +COUNT(*) +3072 +EXPLAIN +SELECT COUNT(*) FROM t1 +WHERE (c >= '2007-01-02' AND c <= '2007-01-03') OR b >= 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL idx_b,idx_c NULL NULL NULL 3263 Using where +SELECT COUNT(*) FROM t1 +WHERE (c >= '2007-01-02' AND c <= '2007-01-03') OR b >= 1; +COUNT(*) +3072 +EXPLAIN +SELECT COUNT(*) FROM t1 FORCE INDEX(idx_b, idx_c) +WHERE (c >= '2007-01-02' AND c <= '2007-01-03') OR b >= 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index_merge idx_b,idx_c idx_c,idx_b 8,4 NULL 3262 Using sort_union(idx_c,idx_b); Using where +SELECT COUNT(*) FROM t1 FORCE INDEX(idx_b, idx_c) +WHERE (c >= '2007-01-02' AND c <= '2007-01-03') OR b >= 1; +COUNT(*) +3072 +set @@sort_buffer_size=default; +DROP TABLE t1,t2; End of 5.0 tests diff --git a/mysql-test/t/innodb_mysql.test b/mysql-test/t/innodb_mysql.test index 25ba9a258ff..eb995a748a5 100644 --- a/mysql-test/t/innodb_mysql.test +++ b/mysql-test/t/innodb_mysql.test @@ -670,5 +670,59 @@ DISCONNECT c1; DISCONNECT c2; DROP TABLE t1,t2; +# +# Bug #25798: a query with forced index merge returns wrong result +# + +CREATE TABLE t1 ( + id int NOT NULL auto_increment PRIMARY KEY, + b int NOT NULL, + c datetime NOT NULL, + INDEX idx_b(b), + INDEX idx_c(c) +) ENGINE=InnoDB; + +CREATE TABLE t2 ( + b int NOT NULL auto_increment PRIMARY KEY, + c datetime NOT NULL +) ENGINE= MyISAM; + +INSERT INTO t2(c) VALUES ('2007-01-01'); +INSERT INTO t2(c) SELECT c FROM t2; +INSERT INTO t2(c) SELECT c FROM t2; +INSERT INTO t2(c) SELECT c FROM t2; +INSERT INTO t2(c) SELECT c FROM t2; +INSERT INTO t2(c) SELECT c FROM t2; +INSERT INTO t2(c) SELECT c FROM t2; +INSERT INTO t2(c) SELECT c FROM t2; +INSERT INTO t2(c) SELECT c FROM t2; +INSERT INTO t2(c) SELECT c FROM t2; +INSERT INTO t2(c) SELECT c FROM t2; + +INSERT INTO t1(b,c) SELECT b,c FROM t2; +UPDATE t2 SET c='2007-01-02'; +INSERT INTO t1(b,c) SELECT b,c FROM t2; +UPDATE t2 SET c='2007-01-03'; +INSERT INTO t1(b,c) SELECT b,c FROM t2; + +set @@sort_buffer_size=8192; + +SELECT COUNT(*) FROM t1; + +EXPLAIN +SELECT COUNT(*) FROM t1 + WHERE (c >= '2007-01-02' AND c <= '2007-01-03') OR b >= 1; +SELECT COUNT(*) FROM t1 + WHERE (c >= '2007-01-02' AND c <= '2007-01-03') OR b >= 1; + +EXPLAIN +SELECT COUNT(*) FROM t1 FORCE INDEX(idx_b, idx_c) + WHERE (c >= '2007-01-02' AND c <= '2007-01-03') OR b >= 1; +SELECT COUNT(*) FROM t1 FORCE INDEX(idx_b, idx_c) + WHERE (c >= '2007-01-02' AND c <= '2007-01-03') OR b >= 1; + +set @@sort_buffer_size=default; + +DROP TABLE t1,t2; --echo End of 5.0 tests diff --git a/sql/filesort.cc b/sql/filesort.cc index d518ddbb117..f8868ed6927 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -1052,6 +1052,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, BUFFPEK *buffpek; QUEUE queue; qsort2_cmp cmp; + void *first_cmp_arg; volatile THD::killed_state *killed= ¤t_thd->killed; THD::killed_state not_killable; DBUG_ENTER("merge_buffers"); @@ -1077,9 +1078,18 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, /* The following will fire if there is not enough space in sort_buffer */ DBUG_ASSERT(maxcount!=0); + if (param->unique_buff) + { + cmp= param->compare; + first_cmp_arg= (void *) ¶m->cmp_context; + } + else + { + cmp= get_ptr_compare(sort_length); + first_cmp_arg= (void*) &sort_length; + } if (init_queue(&queue, (uint) (Tb-Fb)+1, offsetof(BUFFPEK,key), 0, - (queue_compare) (cmp= get_ptr_compare(sort_length)), - (void*) &sort_length)) + (queue_compare) cmp, first_cmp_arg)) DBUG_RETURN(1); /* purecov: inspected */ for (buffpek= Fb ; buffpek <= Tb ; buffpek++) { @@ -1132,7 +1142,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, buffpek= (BUFFPEK*) queue_top(&queue); if (cmp) // Remove duplicates { - if (!(*cmp)(&sort_length, &(param->unique_buff), + if (!(*cmp)(first_cmp_arg, &(param->unique_buff), (uchar**) &buffpek->key)) goto skip_duplicate; memcpy(param->unique_buff, (uchar*) buffpek->key, rec_length); @@ -1184,7 +1194,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, */ if (cmp) { - if (!(*cmp)(&sort_length, &(param->unique_buff), (uchar**) &buffpek->key)) + if (!(*cmp)(first_cmp_arg, &(param->unique_buff), (uchar**) &buffpek->key)) { buffpek->key+= rec_length; // Remove duplicate --buffpek->mem_count; diff --git a/sql/sql_sort.h b/sql/sql_sort.h index da28ca07e2c..1e9322f7f5b 100644 --- a/sql/sql_sort.h +++ b/sql/sql_sort.h @@ -50,6 +50,12 @@ typedef struct st_buffpek { /* Struktur om sorteringsbuffrarna */ ulong max_keys; /* Max keys in buffert */ } BUFFPEK; +struct BUFFPEK_COMPARE_CONTEXT +{ + qsort_cmp2 key_compare; + void *key_compare_arg; +}; + typedef struct st_sort_param { uint rec_length; /* Length of sorted records */ uint sort_length; /* Length of sorted columns */ @@ -65,6 +71,9 @@ typedef struct st_sort_param { uchar *unique_buff; bool not_killable; char* tmp_buffer; + /* The fields below are used only by Unique class */ + qsort2_cmp compare; + BUFFPEK_COMPARE_CONTEXT cmp_context; } SORTPARAM; diff --git a/sql/uniques.cc b/sql/uniques.cc index 9eb827f62a3..7c197d2a2e9 100644 --- a/sql/uniques.cc +++ b/sql/uniques.cc @@ -361,17 +361,12 @@ Unique::reset() } /* - The comparison function, passed to queue_init() in merge_walk() must + The comparison function, passed to queue_init() in merge_walk() and in + merge_buffers() when the latter is called from Uniques::get() must use comparison function of Uniques::tree, but compare members of struct BUFFPEK. */ -struct BUFFPEK_COMPARE_CONTEXT -{ - qsort_cmp2 key_compare; - void *key_compare_arg; -}; - C_MODE_START static int buffpek_compare(void *arg, byte *key_ptr1, byte *key_ptr2) @@ -630,6 +625,10 @@ bool Unique::get(TABLE *table) sort_param.unique_buff= sort_buffer+(sort_param.keys* sort_param.sort_length); + sort_param.compare= (qsort2_cmp) buffpek_compare; + sort_param.cmp_context.key_compare= tree.compare; + sort_param.cmp_context.key_compare_arg= tree.custom_arg; + /* Merge the buffers to one file, removing duplicates */ if (merge_many_buff(&sort_param,sort_buffer,file_ptr,&maxbuffer,&file)) goto err; From de0cf5d22f413c818c567e406843a921fd2c7fb4 Mon Sep 17 00:00:00 2001 From: "sergefp@mysql.com" <> Date: Mon, 2 Jul 2007 22:18:41 +0400 Subject: [PATCH 2/7] Fix testcase to be platform-independent --- mysql-test/r/innodb_mysql.result | 6 +++--- mysql-test/t/innodb_mysql.test | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index df1c2165c45..8ddd3d2f1e8 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -1,4 +1,4 @@ -drop table if exists t1,t2; +drop table if exists t1,t2,t3,t4; create table t1 ( c_id int(11) not null default '0', org_id int(11) default null, @@ -711,7 +711,7 @@ EXPLAIN SELECT COUNT(*) FROM t1 WHERE (c >= '2007-01-02' AND c <= '2007-01-03') OR b >= 1; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL idx_b,idx_c NULL NULL NULL 3263 Using where +1 SIMPLE t1 ALL idx_b,idx_c NULL NULL NULL # Using where SELECT COUNT(*) FROM t1 WHERE (c >= '2007-01-02' AND c <= '2007-01-03') OR b >= 1; COUNT(*) @@ -720,7 +720,7 @@ EXPLAIN SELECT COUNT(*) FROM t1 FORCE INDEX(idx_b, idx_c) WHERE (c >= '2007-01-02' AND c <= '2007-01-03') OR b >= 1; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 index_merge idx_b,idx_c idx_c,idx_b 8,4 NULL 3262 Using sort_union(idx_c,idx_b); Using where +1 SIMPLE t1 index_merge idx_b,idx_c idx_c,idx_b 8,4 NULL # Using sort_union(idx_c,idx_b); Using where SELECT COUNT(*) FROM t1 FORCE INDEX(idx_b, idx_c) WHERE (c >= '2007-01-02' AND c <= '2007-01-03') OR b >= 1; COUNT(*) diff --git a/mysql-test/t/innodb_mysql.test b/mysql-test/t/innodb_mysql.test index eb995a748a5..148181ee443 100644 --- a/mysql-test/t/innodb_mysql.test +++ b/mysql-test/t/innodb_mysql.test @@ -1,7 +1,7 @@ -- source include/have_innodb.inc --disable_warnings -drop table if exists t1,t2; +drop table if exists t1,t2,t3,t4; --enable_warnings # BUG#16798: Uninitialized row buffer reads in ref-or-null optimizer @@ -709,12 +709,14 @@ set @@sort_buffer_size=8192; SELECT COUNT(*) FROM t1; +--replace_column 9 # EXPLAIN SELECT COUNT(*) FROM t1 WHERE (c >= '2007-01-02' AND c <= '2007-01-03') OR b >= 1; SELECT COUNT(*) FROM t1 WHERE (c >= '2007-01-02' AND c <= '2007-01-03') OR b >= 1; +--replace_column 9 # EXPLAIN SELECT COUNT(*) FROM t1 FORCE INDEX(idx_b, idx_c) WHERE (c >= '2007-01-02' AND c <= '2007-01-03') OR b >= 1; From bad7900a5a2138adf1d82ca32d884fd075fd76a1 Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@magare.gmz" <> Date: Tue, 3 Jul 2007 10:36:37 +0300 Subject: [PATCH 3/7] Bug #28983: 'reset master' in multiple threads and innodb tables asserts debug binary We can't reliably check if the binary log is opened without acquiring its mutex. Fixed by removing this check. --- sql/log.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sql/log.cc b/sql/log.cc index 818828f9557..dd989ebd76d 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -85,7 +85,7 @@ bool binlog_init() static int binlog_close_connection(THD *thd) { IO_CACHE *trans_log= (IO_CACHE*)thd->ha_data[binlog_hton.slot]; - DBUG_ASSERT(mysql_bin_log.is_open() && !my_b_tell(trans_log)); + DBUG_ASSERT(!my_b_tell(trans_log)); close_cached_file(trans_log); my_free((gptr)trans_log, MYF(0)); return 0; @@ -126,7 +126,7 @@ static int binlog_commit(THD *thd, bool all) { IO_CACHE *trans_log= (IO_CACHE*)thd->ha_data[binlog_hton.slot]; DBUG_ENTER("binlog_commit"); - DBUG_ASSERT(mysql_bin_log.is_open() && + DBUG_ASSERT( (all || !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))); if (my_b_tell(trans_log) == 0) @@ -155,7 +155,7 @@ static int binlog_rollback(THD *thd, bool all) unnecessary, doing extra work. The cause should be found and eliminated */ DBUG_ASSERT(all || !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))); - DBUG_ASSERT(mysql_bin_log.is_open() && my_b_tell(trans_log)); + DBUG_ASSERT(my_b_tell(trans_log)); /* Update the binary log with a BEGIN/ROLLBACK block if we have cached some queries and we updated some non-transactional @@ -198,7 +198,7 @@ static int binlog_savepoint_set(THD *thd, void *sv) { IO_CACHE *trans_log= (IO_CACHE*)thd->ha_data[binlog_hton.slot]; DBUG_ENTER("binlog_savepoint_set"); - DBUG_ASSERT(mysql_bin_log.is_open() && my_b_tell(trans_log)); + DBUG_ASSERT(my_b_tell(trans_log)); *(my_off_t *)sv= my_b_tell(trans_log); /* Write it to the binary log */ @@ -210,7 +210,7 @@ static int binlog_savepoint_rollback(THD *thd, void *sv) { IO_CACHE *trans_log= (IO_CACHE*)thd->ha_data[binlog_hton.slot]; DBUG_ENTER("binlog_savepoint_rollback"); - DBUG_ASSERT(mysql_bin_log.is_open() && my_b_tell(trans_log)); + DBUG_ASSERT(my_b_tell(trans_log)); /* Write ROLLBACK TO SAVEPOINT to the binlog cache if we have updated some From dbe4fb94cad60318d54db399206848310e1fc9d3 Mon Sep 17 00:00:00 2001 From: "gshchepa/uchum@gleb.loc" <> Date: Tue, 3 Jul 2007 19:37:46 +0500 Subject: [PATCH 4/7] 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. --- mysql-test/r/loaddata.result | 65 +++++++++++++++++++++++++++++++++++- mysql-test/t/loaddata.test | 38 ++++++++++++++++++++- sql/sql_class.cc | 5 ++- sql/sql_class.h | 9 +++++ sql/sql_load.cc | 1 + 5 files changed, 115 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/loaddata.result b/mysql-test/r/loaddata.result index e1076cd3072..0906b1708c7 100644 --- a/mysql-test/r/loaddata.result +++ b/mysql-test/r/loaddata.result @@ -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); load data infile '../../std_data/loaddata1.dat' into table t1 fields terminated by ','; Warnings: @@ -85,3 +85,66 @@ field1 field2 a"b cd"ef a"b c"d"e 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; diff --git a/mysql-test/t/loaddata.test b/mysql-test/t/loaddata.test index e989cb0b2ac..74d71985854 100644 --- a/mysql-test/t/loaddata.test +++ b/mysql-test/t/loaddata.test @@ -3,7 +3,7 @@ # --disable_warnings -drop table if exists t1; +drop table if exists t1,t2; --enable_warnings 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; 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 diff --git a/sql/sql_class.cc b/sql/sql_class.cc index b187d29021a..0f58f96cf57 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1020,6 +1020,7 @@ select_export::prepare(List &list, SELECT_LEX_UNIT *u) field_sep_char= (exchange->enclosed->length() ? (*exchange->enclosed)[0] : field_term_length ? (*exchange->field_term)[0] : INT_MAX); 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() ? (*exchange->line_term)[0] : INT_MAX); if (!field_term_length) @@ -1113,7 +1114,9 @@ bool select_export::send_data(List &items) (int) *pos == line_sep_char || !*pos) { 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'; if (my_b_write(&cache,(byte*) start,(uint) (pos-start)) || my_b_write(&cache,(byte*) tmp_buff,2)) diff --git a/sql/sql_class.h b/sql/sql_class.h index db6f65cab55..eb2c8637651 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1224,9 +1224,18 @@ public: }; +#define ESCAPE_CHARS "ntrb0ZN" // keep synchronous with READ_INFO::unescape + + class select_export :public select_to_file { uint field_term_length; 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; public: select_export(sql_exchange *ex) :select_to_file(ex) {} diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 10124e5f5ff..bac981651c3 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -611,6 +611,7 @@ read_sep_field(THD *thd,COPY_INFO &info,TABLE *table, char READ_INFO::unescape(char chr) { + /* keep this switch synchornous with the ESCAPE_CHARS macro */ switch(chr) { case 'n': return '\n'; case 't': return '\t'; From 5f5929846b0f655282f243f9acf641aae310cf2b Mon Sep 17 00:00:00 2001 From: "gshchepa/uchum@gleb.loc" <> Date: Tue, 3 Jul 2007 21:05:17 +0500 Subject: [PATCH 5/7] sql_class.cc: Windows compilation error fix. --- sql/sql_class.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 0f58f96cf57..3e918a6a07c 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1020,7 +1020,7 @@ select_export::prepare(List &list, SELECT_LEX_UNIT *u) field_sep_char= (exchange->enclosed->length() ? (*exchange->enclosed)[0] : field_term_length ? (*exchange->field_term)[0] : INT_MAX); escape_char= (exchange->escaped->length() ? (*exchange->escaped)[0] : -1); - is_ambiguous_field_sep= strchr(ESCAPE_CHARS, field_sep_char); + is_ambiguous_field_sep= test(strchr(ESCAPE_CHARS, field_sep_char)); line_sep_char= (exchange->line_term->length() ? (*exchange->line_term)[0] : INT_MAX); if (!field_term_length) From 1f85dac21c9533a17d2e9dfc9d4975785da3d175 Mon Sep 17 00:00:00 2001 From: "gshchepa/uchum@gleb.loc" <> Date: Tue, 3 Jul 2007 21:45:20 +0500 Subject: [PATCH 6/7] loaddata.result, loaddata.test: Test case update for bug #29294. --- mysql-test/r/loaddata.result | 11 +---------- mysql-test/t/loaddata.test | 3 +-- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/mysql-test/r/loaddata.result b/mysql-test/r/loaddata.result index 0906b1708c7..83a0788b793 100644 --- a/mysql-test/r/loaddata.result +++ b/mysql-test/r/loaddata.result @@ -97,8 +97,7 @@ 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'); +('.r.'), ('.rr.'), ('.rrr.'), ('.rrrr.'); SELECT * FROM t1; id c1 1 r @@ -117,10 +116,6 @@ id c1 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 @@ -138,10 +133,6 @@ 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 diff --git a/mysql-test/t/loaddata.test b/mysql-test/t/loaddata.test index 74d71985854..14f33c6b396 100644 --- a/mysql-test/t/loaddata.test +++ b/mysql-test/t/loaddata.test @@ -86,8 +86,7 @@ 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'); + ('.r.'), ('.rr.'), ('.rrr.'), ('.rrrr.'); SELECT * FROM t1; --exec rm -f $MYSQL_TEST_DIR/var/tmp/t1 From 79622efe7c6f84f0347456f5c05326239e042c48 Mon Sep 17 00:00:00 2001 From: "gshchepa/uchum@gleb.loc" <> Date: Wed, 4 Jul 2007 03:15:37 +0500 Subject: [PATCH 7/7] loaddata.result, loaddata.test: Updated test case for bug #29294. --- mysql-test/r/loaddata.result | 4 ++-- mysql-test/t/loaddata.test | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mysql-test/r/loaddata.result b/mysql-test/r/loaddata.result index 0aa95983264..031aa5c700c 100644 --- a/mysql-test/r/loaddata.result +++ b/mysql-test/r/loaddata.result @@ -117,7 +117,7 @@ id c1 14 .rr. 15 .rrr. 16 .rrrr. -SELECT * INTO OUTFILE 'MYSQL_TEST_DIR/var/tmp/t1' FIELDS ENCLOSED BY 'r' FROM t1; +SELECT * INTO OUTFILE 'MYSQLTEST_VARDIR/tmp/t1' FIELDS ENCLOSED BY 'r' FROM t1; r1r rrrr r2r rrrrrr r3r rrrrrrrr @@ -134,7 +134,7 @@ r13r r.rr.r r14r r.rrrr.r r15r r.rrrrrr.r r16r r.rrrrrrrr.r -LOAD DATA INFILE 'MYSQL_TEST_DIR/var/tmp/t1' INTO TABLE t2 FIELDS ENCLOSED BY 'r'; +LOAD DATA INFILE 'MYSQLTEST_VARDIR/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; diff --git a/mysql-test/t/loaddata.test b/mysql-test/t/loaddata.test index c7f16fc303b..cdd3bb80b6e 100644 --- a/mysql-test/t/loaddata.test +++ b/mysql-test/t/loaddata.test @@ -89,17 +89,17 @@ INSERT INTO t1 (c1) VALUES ('.r.'), ('.rr.'), ('.rrr.'), ('.rrrr.'); 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 +--exec rm -f $MYSQLTEST_VARDIR/tmp/t1 +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval SELECT * INTO OUTFILE '$MYSQLTEST_VARDIR/tmp/t1' FIELDS ENCLOSED BY 'r' FROM t1; +--exec cat $MYSQLTEST_VARDIR/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'; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval LOAD DATA INFILE '$MYSQLTEST_VARDIR/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 +--exec rm $MYSQLTEST_VARDIR/tmp/t1 DROP TABLE t1,t2; # End of 4.1 tests