From 07dcc80023674120eb84d41a37dd748da8e00de7 Mon Sep 17 00:00:00 2001 From: unknown 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: Added a test case for bug #25798. mysql-test/t/innodb_mysql.test: Added a test case for bug #25798. sql/filesort.cc: Fixed bug #25798. The function merge_buffers() when called from the Uniques::get method must use function buffpek_compare to compare elements in the queue it employs. The pointer to buffpek_compare and the info for the function that compares sorted records are passed to merge_buffers through certain designated fields of the SORTPARAM structure. sql/sql_sort.h: Fixed bug #25798. Added fields to the SORTPARAM structure to be used in the function merge_buffers when called by the Uniques::get method. sql/uniques.cc: Fixed bug 25798. The function merge_buffers() when called from the Uniques::get method must use function buffpek_compare to compare elements in the queue it employes. --- 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 b0696f545706255cbedf291b0e8f70aa10410b79 Mon Sep 17 00:00:00 2001 From: unknown 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 841cf4053257a96eafb203eeea796bc44a8d9330 Mon Sep 17 00:00:00 2001 From: unknown 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: Bug #28983: can't reliably check if bin_log is open outside of its mutex --- 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 166d3c9186539e4ebd738e368be453bdee119cc2 Mon Sep 17 00:00:00 2001 From: unknown 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. 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. --- 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 19a5d20ce3c55dece7da483964d24d9421bd63c0 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 3 Jul 2007 21:05:17 +0500 Subject: [PATCH 5/7] sql_class.cc: Windows compilation error fix. sql/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 bc3e00868803009e5f4aff258bf2427e3896c255 Mon Sep 17 00:00:00 2001 From: unknown 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/t/loaddata.test: Test case update for bug #29294. mysql-test/r/loaddata.result: 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 8d035c57f0c5c65ecf9652bd913b3d2f988c8f6e Mon Sep 17 00:00:00 2001 From: unknown 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/t/loaddata.test: Updated test case for bug #29294. mysql-test/r/loaddata.result: 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