From bcc627846de20209d929b0fbbe929f432c1677a9 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 Oct 2007 16:16:59 +0500 Subject: [PATCH 1/2] Fixed bug #31663: if the FIELDS TERMINATED BY string in the SELECT INTO OUTFILE clause starts with a special character (one of n, t, r, b, 0, Z or N) and ENCLOSED BY is empty, every occurrence of this character within a field value is duplicated. Duplication has been avoided. New warning message has been added: "First character of the FIELDS TERMINATED string is ambiguous; please use non-optional and non-empty FIELDS ENCLOSED BY". mysql-test/r/outfile_loaddata.result: BitKeeper file /home/uchum/work/bk/5.0-opt-31663/mysql-test/r/outfile_loaddata.result Added test case for bug #31663. mysql-test/t/outfile_loaddata.test: BitKeeper file /home/uchum/work/bk/5.0-opt-31663/mysql-test/t/outfile_loaddata.test Added test case for bug #31663. sql/sql_class.h: Fixed bug #31663. The select_export::is_ambiguous_field_term field has been added. This field is true if select_export::field_sep_char contains the first char of the FIELDS TERMINATED BY (ENCLOSED BY is empty), and items can contain this character. The select_export::field_term_char field has been added (first char of the FIELDS TERMINATED BY string or INT_MAX). sql/sql_class.cc: Fixed bug #31663. The select_export::prepare method has been modified to calculate a value of the select_export::is_ambiguous_field_term field and to warn if this value is true. The select_export::send_data method has been modified to avoid escaping or duplication of the field_set_char if is_ambiguous_field_term is true. sql/share/errmsg.txt: Fixed bug #31663. The ER_AMBIGUOUS_FIELD_TERM warning has been added. --- mysql-test/r/outfile_loaddata.result | 85 ++++++++++++++++++++++++++ mysql-test/t/outfile_loaddata.test | 89 ++++++++++++++++++++++++++++ sql/share/errmsg.txt | 2 + sql/sql_class.cc | 44 +++++++++++--- sql/sql_class.h | 7 +++ 5 files changed, 218 insertions(+), 9 deletions(-) create mode 100644 mysql-test/r/outfile_loaddata.result create mode 100644 mysql-test/t/outfile_loaddata.test diff --git a/mysql-test/r/outfile_loaddata.result b/mysql-test/r/outfile_loaddata.result new file mode 100644 index 00000000000..1bcaf308b7c --- /dev/null +++ b/mysql-test/r/outfile_loaddata.result @@ -0,0 +1,85 @@ +DROP TABLE IF EXISTS t1, t2; +# +# Bug#31663 FIELDS TERMINATED BY special character +# +CREATE TABLE t1 (i1 int, i2 int, c1 VARCHAR(256), c2 VARCHAR(256)); +INSERT INTO t1 VALUES (101, 202, '-r-', '=raker='); +# FIELDS TERMINATED BY 'raker', warning: +SELECT * INTO OUTFILE 'MYSQLTEST_VARDIR/tmp/bug31663.txt' FIELDS TERMINATED BY 'raker' FROM t1; +Warnings: +Warning 1475 First character of the FIELDS TERMINATED string is ambiguous; please use non-optional and non-empty FIELDS ENCLOSED BY +SELECT LOAD_FILE('MYSQLTEST_VARDIR/tmp/bug31663.txt'); +LOAD_FILE('MYSQLTEST_VARDIR/tmp/bug31663.txt') +101raker202raker-r-raker=raker= + +CREATE TABLE t2 SELECT * FROM t1; +LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/bug31663.txt' INTO TABLE t2 FIELDS TERMINATED BY 'raker'; +Warnings: +Warning 1262 Row 1 was truncated; it contained more data than there were input columns +SELECT * FROM t2; +i1 i2 c1 c2 +101 202 -r- =raker= +101 202 -r- = +DROP TABLE t2; +# Only numeric fields, FIELDS TERMINATED BY 'r', no warnings: +SELECT i1, i2 INTO OUTFILE 'MYSQLTEST_VARDIR/tmp/bug31663.txt' FIELDS TERMINATED BY 'r' FROM t1; +SELECT LOAD_FILE('MYSQLTEST_VARDIR/tmp/bug31663.txt'); +LOAD_FILE('MYSQLTEST_VARDIR/tmp/bug31663.txt') +101r202 + +CREATE TABLE t2 SELECT i1, i2 FROM t1; +LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/bug31663.txt' INTO TABLE t2 FIELDS TERMINATED BY 'r'; +SELECT i1, i2 FROM t2; +i1 i2 +101 202 +101 202 +DROP TABLE t2; +# FIELDS TERMINATED BY '0', warning: +SELECT * INTO OUTFILE 'MYSQLTEST_VARDIR/tmp/bug31663.txt' FIELDS TERMINATED BY '0' FROM t1; +Warnings: +Warning 1475 First character of the FIELDS TERMINATED string is ambiguous; please use non-optional and non-empty FIELDS ENCLOSED BY +SELECT LOAD_FILE('MYSQLTEST_VARDIR/tmp/bug31663.txt'); +LOAD_FILE('MYSQLTEST_VARDIR/tmp/bug31663.txt') +10102020-r-0=raker= + +CREATE TABLE t2 SELECT * FROM t1; +LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/bug31663.txt' INTO TABLE t2 FIELDS TERMINATED BY '0'; +Warnings: +Warning 1262 Row 1 was truncated; it contained more data than there were input columns +SELECT * FROM t2; +i1 i2 c1 c2 +101 202 -r- =raker= +1 1 2 2 +DROP TABLE t2; +# FIELDS OPTIONALLY ENCLOSED BY '"' TERMINATED BY '0', warning: +SELECT * INTO OUTFILE 'MYSQLTEST_VARDIR/tmp/bug31663.txt' FIELDS OPTIONALLY ENCLOSED BY '"' TERMINATED BY '0' FROM t1; +Warnings: +Warning 1475 First character of the FIELDS TERMINATED string is ambiguous; please use non-optional and non-empty FIELDS ENCLOSED BY +SELECT LOAD_FILE('MYSQLTEST_VARDIR/tmp/bug31663.txt'); +LOAD_FILE('MYSQLTEST_VARDIR/tmp/bug31663.txt') +10102020"-r-"0"=raker=" + +CREATE TABLE t2 SELECT * FROM t1; +LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/bug31663.txt' INTO TABLE t2 FIELDS OPTIONALLY ENCLOSED BY '"' TERMINATED BY '0'; +Warnings: +Warning 1262 Row 1 was truncated; it contained more data than there were input columns +SELECT * FROM t2; +i1 i2 c1 c2 +101 202 -r- =raker= +1 1 2 2 +DROP TABLE t2; +# Only string fields, FIELDS OPTIONALLY ENCLOSED BY '"' TERMINATED BY '0', no warnings: +SELECT c1, c2 INTO OUTFILE 'MYSQLTEST_VARDIR/tmp/bug31663.txt' FIELDS OPTIONALLY ENCLOSED BY '"' TERMINATED BY '0' FROM t1; +SELECT LOAD_FILE('MYSQLTEST_VARDIR/tmp/bug31663.txt'); +LOAD_FILE('MYSQLTEST_VARDIR/tmp/bug31663.txt') +"-r-"0"=raker=" + +CREATE TABLE t2 SELECT c1, c2 FROM t1; +LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/bug31663.txt' INTO TABLE t2 FIELDS OPTIONALLY ENCLOSED BY '"' TERMINATED BY '0'; +SELECT c1, c2 FROM t2; +c1 c2 +-r- =raker= +-r- =raker= +DROP TABLE t2; +DROP TABLE t1; +# End of 5.0 tests. diff --git a/mysql-test/t/outfile_loaddata.test b/mysql-test/t/outfile_loaddata.test new file mode 100644 index 00000000000..2f6ac998b3d --- /dev/null +++ b/mysql-test/t/outfile_loaddata.test @@ -0,0 +1,89 @@ +--disable_warnings +DROP TABLE IF EXISTS t1, t2; +--enable_warnings + +--echo # +--echo # Bug#31663 FIELDS TERMINATED BY special character +--echo # + +CREATE TABLE t1 (i1 int, i2 int, c1 VARCHAR(256), c2 VARCHAR(256)); +INSERT INTO t1 VALUES (101, 202, '-r-', '=raker='); + +--let $fields=* +--let $clauses=FIELDS TERMINATED BY 'raker' +--echo # $clauses, warning: + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--eval SELECT $fields INTO OUTFILE '$MYSQLTEST_VARDIR/tmp/bug31663.txt' $clauses FROM t1 +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--eval SELECT LOAD_FILE('$MYSQLTEST_VARDIR/tmp/bug31663.txt') +--eval CREATE TABLE t2 SELECT $fields FROM t1 +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--eval LOAD DATA INFILE '$MYSQLTEST_VARDIR/tmp/bug31663.txt' INTO TABLE t2 $clauses +--eval SELECT $fields FROM t2 +--remove_file $MYSQLTEST_VARDIR/tmp/bug31663.txt +DROP TABLE t2; + +--let $fields=i1, i2 +--let $clauses=FIELDS TERMINATED BY 'r' +--echo # Only numeric fields, $clauses, no warnings: + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--eval SELECT $fields INTO OUTFILE '$MYSQLTEST_VARDIR/tmp/bug31663.txt' $clauses FROM t1 +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--eval SELECT LOAD_FILE('$MYSQLTEST_VARDIR/tmp/bug31663.txt') +--eval CREATE TABLE t2 SELECT $fields FROM t1 +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--eval LOAD DATA INFILE '$MYSQLTEST_VARDIR/tmp/bug31663.txt' INTO TABLE t2 $clauses +--eval SELECT $fields FROM t2 +--remove_file $MYSQLTEST_VARDIR/tmp/bug31663.txt +DROP TABLE t2; + +--let $fields=* +--let $clauses=FIELDS TERMINATED BY '0' +--echo # $clauses, warning: + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--eval SELECT $fields INTO OUTFILE '$MYSQLTEST_VARDIR/tmp/bug31663.txt' $clauses FROM t1 +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--eval SELECT LOAD_FILE('$MYSQLTEST_VARDIR/tmp/bug31663.txt') +--eval CREATE TABLE t2 SELECT $fields FROM t1 +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--eval LOAD DATA INFILE '$MYSQLTEST_VARDIR/tmp/bug31663.txt' INTO TABLE t2 $clauses +--eval SELECT $fields FROM t2 +--remove_file $MYSQLTEST_VARDIR/tmp/bug31663.txt +DROP TABLE t2; + +--let $fields=* +--let $clauses=FIELDS OPTIONALLY ENCLOSED BY '"' TERMINATED BY '0' +--echo # $clauses, warning: + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--eval SELECT $fields INTO OUTFILE '$MYSQLTEST_VARDIR/tmp/bug31663.txt' $clauses FROM t1 +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--eval SELECT LOAD_FILE('$MYSQLTEST_VARDIR/tmp/bug31663.txt') +--eval CREATE TABLE t2 SELECT $fields FROM t1 +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--eval LOAD DATA INFILE '$MYSQLTEST_VARDIR/tmp/bug31663.txt' INTO TABLE t2 $clauses +--eval SELECT $fields FROM t2 +--remove_file $MYSQLTEST_VARDIR/tmp/bug31663.txt +DROP TABLE t2; + +--let $fields=c1, c2 +--let $clauses=FIELDS OPTIONALLY ENCLOSED BY '"' TERMINATED BY '0' +--echo # Only string fields, $clauses, no warnings: + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--eval SELECT $fields INTO OUTFILE '$MYSQLTEST_VARDIR/tmp/bug31663.txt' $clauses FROM t1 +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--eval SELECT LOAD_FILE('$MYSQLTEST_VARDIR/tmp/bug31663.txt') +--eval CREATE TABLE t2 SELECT $fields FROM t1 +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--eval LOAD DATA INFILE '$MYSQLTEST_VARDIR/tmp/bug31663.txt' INTO TABLE t2 $clauses +--eval SELECT $fields FROM t2 +--remove_file $MYSQLTEST_VARDIR/tmp/bug31663.txt +DROP TABLE t2; + +DROP TABLE t1; + +--echo # End of 5.0 tests. diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 709cd1fc0a9..9e6cf462113 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5639,3 +5639,5 @@ ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT eng "Too high level of nesting for select" ER_NAME_BECOMES_EMPTY eng "Name '%-.64s' has become ''" +ER_AMBIGUOUS_FIELD_TERM + eng "First character of the FIELDS TERMINATED string is ambiguous; please use non-optional and non-empty FIELDS ENCLOSED BY" diff --git a/sql/sql_class.cc b/sql/sql_class.cc index b67f63778dc..1836989f2fa 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1194,6 +1194,7 @@ int select_export::prepare(List &list, SELECT_LEX_UNIT *u) { bool blob_flag=0; + bool string_results= FALSE, non_string_results= FALSE; unit= u; if ((uint) strlen(exchange->file_name) + NAME_LEN >= FN_REFLEN) strmake(path,exchange->file_name,FN_REFLEN-1); @@ -1211,13 +1212,18 @@ select_export::prepare(List &list, SELECT_LEX_UNIT *u) blob_flag=1; break; } + if (item->result_type() == STRING_RESULT) + string_results= TRUE; + else + non_string_results= TRUE; } } field_term_length=exchange->field_term->length(); + field_term_char= field_term_length ? (*exchange->field_term)[0] : INT_MAX; if (!exchange->line_term->length()) exchange->line_term=exchange->field_term; // Use this if it exists field_sep_char= (exchange->enclosed->length() ? (*exchange->enclosed)[0] : - field_term_length ? (*exchange->field_term)[0] : INT_MAX); + field_term_char); escape_char= (exchange->escaped->length() ? (*exchange->escaped)[0] : -1); is_ambiguous_field_sep= test(strchr(ESCAPE_CHARS, field_sep_char)); is_unsafe_field_sep= test(strchr(NUMERIC_CHARS, field_sep_char)); @@ -1229,12 +1235,25 @@ select_export::prepare(List &list, SELECT_LEX_UNIT *u) exchange->opt_enclosed=1; // A little quicker loop fixed_row_size= (!field_term_length && !exchange->enclosed->length() && !blob_flag); + if ((is_ambiguous_field_sep && exchange->enclosed->is_empty() && + (string_results || is_unsafe_field_sep)) || + (exchange->opt_enclosed && non_string_results && + field_term_length && strchr(NUMERIC_CHARS, field_term_char))) + { + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_AMBIGUOUS_FIELD_TERM, ER(ER_AMBIGUOUS_FIELD_TERM)); + is_ambiguous_field_term= TRUE; + } + else + is_ambiguous_field_term= FALSE; + return 0; } #define NEED_ESCAPING(x) ((int) (uchar) (x) == escape_char || \ - (int) (uchar) (x) == field_sep_char || \ + (enclosed ? (int) (uchar) (x) == field_sep_char \ + : (int) (uchar) (x) == field_term_char) || \ (int) (uchar) (x) == line_sep_char || \ !(x)) @@ -1263,8 +1282,10 @@ bool select_export::send_data(List &items) while ((item=li++)) { Item_result result_type=item->result_type(); + bool enclosed = (exchange->enclosed->length() && + (!exchange->opt_enclosed || result_type == STRING_RESULT)); res=item->str_result(&tmp); - if (res && (!exchange->opt_enclosed || result_type == STRING_RESULT)) + if (res && enclosed) { if (my_b_write(&cache,(byte*) exchange->enclosed->ptr(), exchange->enclosed->length())) @@ -1355,11 +1376,16 @@ bool select_export::send_data(List &items) DBUG_ASSERT before the loop makes that sure. */ - if (NEED_ESCAPING(*pos) || - (check_second_byte && - my_mbcharlen(character_set_client, (uchar) *pos) == 2 && - pos + 1 < end && - NEED_ESCAPING(pos[1]))) + if ((NEED_ESCAPING(*pos) || + (check_second_byte && + my_mbcharlen(character_set_client, (uchar) *pos) == 2 && + pos + 1 < end && + NEED_ESCAPING(pos[1]))) && + /* + Don't escape field_term_char by doubling - doubling is only + valid for ENCLOSED BY characters: + */ + (enclosed || !is_ambiguous_field_term || *pos != field_term_char)) { char tmp_buff[2]; tmp_buff[0]= ((int) *pos == field_sep_char && @@ -1398,7 +1424,7 @@ bool select_export::send_data(List &items) goto err; } } - if (res && (!exchange->opt_enclosed || result_type == STRING_RESULT)) + if (res && enclosed) { if (my_b_write(&cache, (byte*) exchange->enclosed->ptr(), exchange->enclosed->length())) diff --git a/sql/sql_class.h b/sql/sql_class.h index e6d65f3133a..62b1008e59c 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1992,12 +1992,19 @@ public: class select_export :public select_to_file { uint field_term_length; int field_sep_char,escape_char,line_sep_char; + int field_term_char; // first char of FIELDS TERMINATED BY or MAX_INT /* 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; + /* + The is_ambiguous_field_term is true if field_sep_char contains the first + char of the FIELDS TERMINATED BY (ENCLOSED BY is empty), and items can + contain this character. + */ + bool is_ambiguous_field_term; /* The is_unsafe_field_sep field is true if a value of the field_sep_char field is one of the '0'..'9', '+', '-', '.' and 'e' characters From 04ff9d4da02d0f9e66e383b13fc456636f92819a Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 25 Oct 2007 10:32:52 +0500 Subject: [PATCH 2/2] Fixed bug #27695: View should not be allowed to have empty or all space column names. The parser has been modified to check VIEW column names with the check_column_name function and to report an error on empty and all space column names (same as for TABLE column names). sql/sql_yacc.yy: Fixed bug #27695. The parser has been modified to check VIEW column aliases with the check_column_name function and to report an error on empty columns and all space columns (same as for TABLE column names). mysql-test/t/select.test: Updated test case for bug #27695. mysql-test/r/select.result: Updated test case for bug #27695. --- mysql-test/r/select.result | 26 ++++++++++++-------------- mysql-test/t/select.test | 33 ++++++++++++++++++++------------- sql/sql_yacc.yy | 6 ++++++ 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index ed120a1bbb8..52f2e84bf4e 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -4077,23 +4077,21 @@ x 1 Warnings: Warning 1466 Leading spaces are removed from name ' x' +CREATE VIEW v1 AS SELECT 1 AS ``; +ERROR 42000: Incorrect column name '' CREATE VIEW v1 AS SELECT 1 AS ` `; -Warnings: -Warning 1474 Name ' ' has become '' -SELECT `` FROM v1; - -1 -CREATE VIEW v2 AS SELECT 1 AS ` `; -Warnings: -Warning 1474 Name ' ' has become '' -SELECT `` FROM v2; - -1 -CREATE VIEW v3 AS SELECT 1 AS ` x`; +ERROR 42000: Incorrect column name ' ' +CREATE VIEW v1 AS SELECT 1 AS ` `; +ERROR 42000: Incorrect column name ' ' +CREATE VIEW v1 AS SELECT (SELECT 1 AS ` `); +ERROR 42000: Incorrect column name ' ' +CREATE VIEW v1 AS SELECT 1 AS ` x`; Warnings: Warning 1466 Leading spaces are removed from name ' x' -SELECT `x` FROM v3; +SELECT `x` FROM v1; x 1 -DROP VIEW v1, v2, v3; +ALTER VIEW v1 AS SELECT 1 AS ` `; +ERROR 42000: Incorrect column name ' ' +DROP VIEW v1; End of 5.0 tests diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 5c30a17e08e..a6ed3c854b4 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -3466,22 +3466,29 @@ DROP TABLE t1; # --disable_ps_protocol - SELECT 1 AS ` `; SELECT 1 AS ` `; SELECT 1 AS ` x`; - -CREATE VIEW v1 AS SELECT 1 AS ` `; -SELECT `` FROM v1; - -CREATE VIEW v2 AS SELECT 1 AS ` `; -SELECT `` FROM v2; - -CREATE VIEW v3 AS SELECT 1 AS ` x`; -SELECT `x` FROM v3; - -DROP VIEW v1, v2, v3; - --enable_ps_protocol +--error 1166 +CREATE VIEW v1 AS SELECT 1 AS ``; + +--error 1166 +CREATE VIEW v1 AS SELECT 1 AS ` `; + +--error 1166 +CREATE VIEW v1 AS SELECT 1 AS ` `; + +--error 1166 +CREATE VIEW v1 AS SELECT (SELECT 1 AS ` `); + +CREATE VIEW v1 AS SELECT 1 AS ` x`; +SELECT `x` FROM v1; + +--error 1166 +ALTER VIEW v1 AS SELECT 1 AS ` `; + +DROP VIEW v1; + --echo End of 5.0 tests diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 368ce5673e2..3401bf739b3 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -4305,6 +4305,12 @@ select_item: MYSQL_YYABORT; if ($4.str) { + if (Lex->sql_command == SQLCOM_CREATE_VIEW && + check_column_name($4.str)) + { + my_error(ER_WRONG_COLUMN_NAME, MYF(0), $4.str); + MYSQL_YYABORT; + } $2->is_autogenerated_name= FALSE; $2->set_name($4.str, $4.length, system_charset_info); }