From f120273dd284ff4df9649191e8cc37b2d84dd033 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 30 Jan 2004 10:46:30 +0100 Subject: [PATCH] Fixed parsing of column names and foreign key constraints in Innobase to handle quoted identifiers and identifiers with space. (Bug #1725) Fix optimizer tuning bug when first used key part was a constant. (Bug #1679) innobase/dict/dict0dict.c: Fixed parsing of column names and foreign key constraints to handle quoted identifiers and identifiers with space. (Bug #1725) mysql-test/r/innodb.result: Test of innodb internal parsing mysql-test/t/innodb.test: Test of innodb internal parsing sql/sql_class.cc: Safety fix for select into outfile and select into dumpfile. Before calling send_error() could cause end_io_cache() to be called several times. sql/sql_class.h: Add path to dumpfile so that we can delete the generated file if something goes wrong. sql/sql_select.cc: Fix optimizer tuning bug when first used key part was a constant. Previously all keys that had this key part first was regarded as equal, even if the query used more key parts for some of the keys. Now we use the range optimizer results to just limit the number of estimated rows if not all key parts where constants. (Bug #1679) --- innobase/dict/dict0dict.c | 73 +++++++++++++++++++++++--------------- mysql-test/r/innodb.result | 3 ++ mysql-test/t/innodb.test | 9 +++++ sql/sql_class.cc | 22 +++++++----- sql/sql_class.h | 4 ++- sql/sql_select.cc | 15 ++++++-- 6 files changed, 86 insertions(+), 40 deletions(-) diff --git a/innobase/dict/dict0dict.c b/innobase/dict/dict0dict.c index a576a886b97..dc7acfcba36 100644 --- a/innobase/dict/dict0dict.c +++ b/innobase/dict/dict0dict.c @@ -2138,19 +2138,37 @@ dict_scan_col( return(ptr); } - if (*ptr == '`') { - ptr++; - } + if (*ptr == '`' || *ptr == '"') { + /* + The identifier is quoted. Search for end quote. + We can't use the general code here as the name may contain + special characters like space. + */ + char quote= *ptr++; - old_ptr = ptr; + old_ptr= ptr; + /* + The colum name should always end with 'quote' but we check for + end zero just to be safe if this is called outside of MySQL + */ + while (*ptr && *ptr != quote) + ptr++; + *column_name_len = (ulint)(ptr - old_ptr); + + if (*ptr) /* Skip end quote */ + ptr++; + } + else + { + old_ptr = ptr; - while (!isspace(*ptr) && *ptr != ',' && *ptr != ')' && *ptr != '`' - && *ptr != '\0') { - + while (!isspace(*ptr) && *ptr != ',' && *ptr != ')' + && *ptr != '\0') { ptr++; + } + *column_name_len = (ulint)(ptr - old_ptr); } - *column_name_len = (ulint)(ptr - old_ptr); if (table == NULL) { *success = TRUE; @@ -2161,9 +2179,9 @@ dict_scan_col( col = dict_table_get_nth_col(table, i); - if (ut_strlen(col->name) == (ulint)(ptr - old_ptr) + if (ut_strlen(col->name) == *column_name_len && 0 == ut_cmp_in_lower_case(col->name, old_ptr, - (ulint)(ptr - old_ptr))) { + *column_name_len)) { /* Found */ *success = TRUE; @@ -2175,10 +2193,6 @@ dict_scan_col( } } - if (*ptr == '`') { - ptr++; - } - return(ptr); } @@ -2200,6 +2214,7 @@ dict_scan_table_name( char* dot_ptr = NULL; char* old_ptr; ulint i; + char quote = 0; *success = FALSE; *table = NULL; @@ -2213,14 +2228,16 @@ dict_scan_table_name( return(ptr); } - if (*ptr == '`') { - ptr++; + if (*ptr == '`' || *ptr == '"') { + quote= *ptr++; } old_ptr = ptr; - while (!isspace(*ptr) && *ptr != '(' && *ptr != '`' && *ptr != '\0') { - if (*ptr == '.') { + while (*ptr != quote && + (quote || (!isspace(*ptr) && *ptr != '(')) && + *ptr != '\0') { + if (!quote && *ptr == '.') { dot_ptr = ptr; } @@ -2273,7 +2290,7 @@ dict_scan_table_name( *table = dict_table_get_low(second_table_name); - if (*ptr == '`') { + if (*ptr && *ptr == quote) { ptr++; } @@ -2293,7 +2310,7 @@ dict_scan_id( scannable */ ulint* len) /* out: length of the id */ { - ibool scanned_backquote = FALSE; + char quote = 0; *start = NULL; @@ -2306,23 +2323,23 @@ dict_scan_id( return(ptr); } - if (*ptr == '`') { - scanned_backquote = TRUE; - ptr++; + if (*ptr == '`' || *ptr == '"') { + quote = *ptr++; } *start = ptr; - while (!isspace(*ptr) && *ptr != ',' && *ptr != '(' && *ptr != ')' - && *ptr != '\0' && *ptr != '`') { - + while (*ptr != quote && + (!quote || (!isspace(*ptr) && *ptr != ',' && *ptr != '(' && + *ptr != ')')) + && *ptr != '\0') { ptr++; } *len = (ulint) (ptr - *start); - if (scanned_backquote) { - if (*ptr == '`') { + if (quote) { + if (*ptr == quote) { ptr++; } else { /* Syntax error */ diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result index ce5491a718f..ca4a49fea4e 100644 --- a/mysql-test/r/innodb.result +++ b/mysql-test/r/innodb.result @@ -1243,3 +1243,6 @@ a 3 4 drop table t1; +CREATE TABLE t1 (`id 1` INT NOT NULL, PRIMARY KEY (`id 1`)) TYPE=INNODB; +CREATE TABLE t2 (id INT PRIMARY KEY, t1_id INT, INDEX par_ind (t1_id), FOREIGN KEY (`t1_id`) REFERENCES `t1`(`id 1`) ON DELETE CASCADE ) TYPE=INNODB; +drop table t1,t2; diff --git a/mysql-test/t/innodb.test b/mysql-test/t/innodb.test index 7cc509caf74..51fadccdc1c 100644 --- a/mysql-test/t/innodb.test +++ b/mysql-test/t/innodb.test @@ -869,3 +869,12 @@ truncate table t1; insert into t1 (a) values (NULL),(NULL); SELECT * from t1; drop table t1; + +# +# Test dictionary handling with spaceand quoting +# + +CREATE TABLE t1 (`id 1` INT NOT NULL, PRIMARY KEY (`id 1`)) TYPE=INNODB; +CREATE TABLE t2 (id INT PRIMARY KEY, t1_id INT, INDEX par_ind (t1_id), FOREIGN KEY (`t1_id`) REFERENCES `t1`(`id 1`) ON DELETE CASCADE ) TYPE=INNODB; +#show create table t2; +drop table t1,t2; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 3ea61da28fc..fc83131e98a 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -538,7 +538,6 @@ select_export::~select_export() int select_export::prepare(List &list) { - char path[FN_REFLEN]; uint option=4; bool blob_flag=0; #ifdef DONT_ALLOW_FULL_LOAD_DATA_PATHS @@ -739,9 +738,13 @@ err: void select_export::send_error(uint errcode,const char *err) { ::send_error(&thd->net,errcode,err); - (void) end_io_cache(&cache); - (void) my_close(file,MYF(0)); - file= -1; + if (file > 0) + { + (void) end_io_cache(&cache); + (void) my_close(file,MYF(0)); + (void) my_delete(path,MYF(0)); // Delete file on error + file= -1; + } } @@ -849,10 +852,13 @@ err: void select_dump::send_error(uint errcode,const char *err) { ::send_error(&thd->net,errcode,err); - (void) end_io_cache(&cache); - (void) my_close(file,MYF(0)); - (void) my_delete(path,MYF(0)); // Delete file on error - file= -1; + if (file > 0) + { + (void) end_io_cache(&cache); + (void) my_close(file,MYF(0)); + (void) my_delete(path,MYF(0)); // Delete file on error + file= -1; + } } diff --git a/sql/sql_class.h b/sql/sql_class.h index f4fc7b4770f..d96c6bb53cc 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -665,11 +665,13 @@ class select_export :public select_result { File file; IO_CACHE cache; ha_rows row_count; + char path[FN_REFLEN]; uint field_term_length; int field_sep_char,escape_char,line_sep_char; bool fixed_row_size; public: - select_export(sql_exchange *ex) :exchange(ex),file(-1),row_count(0L) {} + select_export(sql_exchange *ex) :exchange(ex),file(-1),row_count(0L) + { path[0]=0; } ~select_export(); int prepare(List &list); bool send_fields(List &list, diff --git a/sql/sql_select.cc b/sql/sql_select.cc index e3bd3c8b570..36ef97cbf30 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1912,7 +1912,8 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, read_time+=record_count/(double) TIME_FOR_COMPARE; if (join->sort_by_table && - join->sort_by_table != join->positions[join->const_tables].table->table) + join->sort_by_table != + join->positions[join->const_tables].table->table) read_time+=record_count; // We have to make a temp table if (read_time < join->best_read) { @@ -1946,7 +1947,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, uint max_key_part=0; /* Test how we can use keys */ - rec= s->records/MATCHING_ROWS_IN_OTHER_TABLE; /* Assumed records/key */ + rec= s->records/MATCHING_ROWS_IN_OTHER_TABLE; // Assumed records/key for (keyuse=s->keyuse ; keyuse->table == table ;) { key_map found_part=0; @@ -2085,7 +2086,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, will match */ if (table->quick_keys & ((key_map) 1 << key) && - table->quick_key_parts[key] <= max_key_part) + table->quick_key_parts[key] == max_key_part) tmp=records= (double) table->quick_rows[key]; else { @@ -2127,6 +2128,14 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, } records=(ulong) tmp; } + /* + If quick_select was used on a part of this key, we know + the maximum number of rows that the key can match. + */ + if (table->quick_keys & ((key_map) 1 << key) && + table->quick_key_parts[key] <= max_key_part && + records > (double) table->quick_rows[key]) + tmp= records= (double) table->quick_rows[key]; } /* Limit the number of matched rows */ set_if_smaller(tmp, (double) thd->variables.max_seeks_for_key);