diff options
57 files changed, 798 insertions, 472 deletions
diff --git a/Docs/manual.texi b/Docs/manual.texi index 776bdc65482..57764d239be 100644 --- a/Docs/manual.texi +++ b/Docs/manual.texi @@ -45712,6 +45712,10 @@ Our TODO section contains what we plan to have in 4.0. @xref{TODO MySQL 4.0}. @itemize @bullet @item +Speed up all internal list handling. +@item +Added support for @code{UNION}. +@item Allow ANSI SQL syntax @code{X'hexadecimal-number'} @item Tree-like cache to speed up bulk inserts and diff --git a/include/mysqld_error.h b/include/mysqld_error.h index b858ae89ef1..5f05841ae50 100644 --- a/include/mysqld_error.h +++ b/include/mysqld_error.h @@ -213,4 +213,6 @@ #define ER_CONNECT_TO_MASTER 1210 #define ER_QUERY_ON_MASTER 1211 #define ER_ERROR_WHEN_EXECUTING_COMMAND 1212 -#define ER_ERROR_MESSAGES 213 +#define ER_WRONG_USAGE 1213 +#define ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT 1214 +#define ER_ERROR_MESSAGES 215 diff --git a/mysql-test/r/union.result b/mysql-test/r/union.result new file mode 100644 index 00000000000..a25ea3f4dcd --- /dev/null +++ b/mysql-test/r/union.result @@ -0,0 +1,60 @@ +a b +1 a +2 b +3 c +4 d +5 f +6 e +a b +1 a +2 b +3 c +3 c +3 c +4 d +5 f +6 e +a b +1 a +2 b +3 c +3 c +3 c +4 d +6 e +5 f +a b +1 a +2 b +3 c +3 c +3 c +4 d +5 f +6 e +7 g +0 # +0 # +1 a +2 b +3 c +3 c +3 c +4 d +5 f +6 e +7 g +a b +1 a +2 b +3 c +t1 b count(*) +t1 a 1 +t1 b 1 +t1 c 2 +t2 c 1 +t2 d 1 +t2 e 1 +t2 f 1 +table type possible_keys key key_len ref rows Extra +t2 ALL NULL NULL NULL NULL 4 diff --git a/mysql-test/r/unions_one.result b/mysql-test/r/unions_one.result deleted file mode 100644 index 66cffd9aec1..00000000000 --- a/mysql-test/r/unions_one.result +++ /dev/null @@ -1,15 +0,0 @@ -a b -1 a -2 b -3 c -4 d -5 e -6 f -a b -1 a -2 b -3 c -3 c -4 d -5 e -6 f diff --git a/mysql-test/t/analyse.test b/mysql-test/t/analyse.test index 1b5022f6e4c..3f56b3e47ce 100644 --- a/mysql-test/t/analyse.test +++ b/mysql-test/t/analyse.test @@ -2,6 +2,7 @@ # Test of procedure analyse # +drop table if exists t1,t2; create table t1 (i int, j int); insert into t1 values (1,2), (3,4), (5,6), (7,8); select * from t1 procedure analyse(); diff --git a/mysql-test/t/union.test b/mysql-test/t/union.test new file mode 100644 index 00000000000..b831be7db28 --- /dev/null +++ b/mysql-test/t/union.test @@ -0,0 +1,29 @@ +# +# Test of unions +# + +drop table if exists t1,t2; +CREATE TABLE t1 (a int not null, b char (10) not null); +insert into t1 values(1,'a'),(2,'b'),(3,'c'),(3,'c'); +CREATE TABLE t2 (a int not null, b char (10) not null); +insert into t2 values (3,'c'),(4,'d'),(5,'f'),(6,'e'); + +select a,b from t1 union select a,b from t2; +select a,b from t1 union all select a,b from t2; +select a,b from t1 union all select a,b from t2 order by b; +select a,b from t1 union all select a,b from t2 union select 7,'g'; +select 0,'#' union select a,b from t1 union all select a,b from t2 union select 7,'gg'; +select a,b from t1 union select a,b from t1; +select 't1',b,count(*) from t1 group by b UNION select 't2',b,count(*) from t2 group by b; + +explain select a,b from t1 union all select a,b from t2; + +# Test some error conditions with UNION +--error 1213 +select a,b from t1 into outfile 'skr' union select a,b from t2; +--error 1213 +select a,b from t1 order by a union select a,b from t2; +--error 1214 +select a,b from t1 union select a from t2; + +drop table t1,t2; diff --git a/mysql-test/t/unions_one.test b/mysql-test/t/unions_one.test deleted file mode 100644 index 54ade3aa453..00000000000 --- a/mysql-test/t/unions_one.test +++ /dev/null @@ -1,16 +0,0 @@ -# -# Test of unions -# - -drop table if exists t1,t2; -CREATE TABLE t1 (a int not null, b char (10) not null); -insert into t1 values(1,"a"),(2,"b"),(3,"c"); -CREATE TABLE t2 (a int not null, b char (10) not null); -insert into t2 values (3,"c"),(4,"d"),(5,"e"),(6,"f"); - - -select a,b from t1 union select a,b from t2; - -select a,b from t1 union all select a,b from t2; - -drop table t1,t2; diff --git a/sql/Makefile.am b/sql/Makefile.am index 70415be03a4..02ac8aa6cc7 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -83,7 +83,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \ sql_udf.cc sql_analyse.cc sql_analyse.h sql_cache.cc \ slave.cc sql_repl.cc \ mini_client.cc mini_client_errors.c \ - md5.c stacktrace.c sql_unions.cc + md5.c stacktrace.c sql_union.cc gen_lex_hash_SOURCES = gen_lex_hash.cc gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS) mysqlbinlog_SOURCES = mysqlbinlog.cc mini_client.cc net_serv.cc \ diff --git a/sql/item.cc b/sql/item.cc index 44bbf9a9cbc..c0cdac51b1e 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -677,5 +677,6 @@ bool field_is_equal_to_item(Field *field,Item *item) #ifdef __GNUC__ template class List<Item>; template class List_iterator<Item>; +template class List_iterator_fast<Item>; template class List<List_item>; #endif diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 373aede7b6b..b18237ca4cf 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1129,7 +1129,7 @@ void Item_cond::update_used_tables() { used_tables_cache=0; const_item_cache=1; - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; while ((item=li++)) { @@ -1143,7 +1143,7 @@ void Item_cond::update_used_tables() void Item_cond::print(String *str) { str->append('('); - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; if ((item=li++)) item->print(str); @@ -1160,7 +1160,7 @@ void Item_cond::print(String *str) longlong Item_cond_and::val_int() { - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; while ((item=li++)) { @@ -1179,7 +1179,7 @@ longlong Item_cond_and::val_int() longlong Item_cond_or::val_int() { - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; null_value=0; while ((item=li++)) diff --git a/sql/item_func.cc b/sql/item_func.cc index e540f850063..b76bee78b2e 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -43,7 +43,7 @@ Item_func::Item_func(List<Item> &list) if ((args=(Item**) sql_alloc(sizeof(Item*)*arg_count))) { uint i=0; - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; while ((item=li++)) @@ -1983,7 +1983,7 @@ bool Item_func_match::fix_fields(THD *thd,struct st_table_list *tlist) bool Item_func_match::fix_index() { - List_iterator<Item> li(fields); + List_iterator_fast<Item> li(fields); Item_field *item; uint ft_to_key[MAX_KEY], ft_cnt[MAX_KEY], fts=0, key; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 8d025891877..c79e909658c 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -30,7 +30,7 @@ Item_sum::Item_sum(List<Item> &list) if ((args=(Item**) sql_alloc(sizeof(Item*)*arg_count))) { uint i=0; - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; while ((item=li++)) @@ -790,74 +790,71 @@ String *Item_std_field::val_str(String *str) static int simple_raw_key_cmp(void* arg, byte* key1, byte* key2) { - return memcmp(key1, key2, (int)arg); + return memcmp(key1, key2, (int) arg); } static int simple_str_key_cmp(void* arg, byte* key1, byte* key2) { - return my_sortcmp(key1, key2, (int)arg); + return my_sortcmp(key1, key2, (int) arg); } -// did not make this one static - at least gcc gets confused when -// I try to declare a static function as a friend. If you can figure -// out the syntax to make a static function a friend, make this one -// static +/* + Did not make this one static - at least gcc gets confused when + I try to declare a static function as a friend. If you can figure + out the syntax to make a static function a friend, make this one + static +*/ + int composite_key_cmp(void* arg, byte* key1, byte* key2) { Item_sum_count_distinct* item = (Item_sum_count_distinct*)arg; - Field** field = item->table->field, **field_end; - field_end = field + item->table->fields; - for(; field < field_end; ++field) - { - int res; - Field* f = *field; - int len = f->pack_length(); - switch((*field)->type()) - { - case FIELD_TYPE_STRING: - case FIELD_TYPE_VAR_STRING: - res = f->key_cmp(key1, key2); - break; - default: - res = memcmp(key1, key2, len); - break; - } - if(res) - return res; - key1 += len; - key2 += len; - } + Field **field = item->table->field; + Field **field_end= field + item->table->fields; + uint32 *lengths=item->field_lengths; + for (; field < field_end; ++field) + { + Field* f = *field; + int len = *lengths++; + int res = f->key_cmp(key1, key2); + if (res) + return res; + key1 += len; + key2 += len; + } return 0; } -// helper function for walking the tree when we dump it to MyISAM - -// tree_walk will call it for each -// leaf +/* + helper function for walking the tree when we dump it to MyISAM - + tree_walk will call it for each leaf +*/ int dump_leaf(byte* key, uint32 count __attribute__((unused)), Item_sum_count_distinct* item) { char* buf = item->table->record[0]; int error; - // the first item->rec_offset bytes are taken care of with - // restore_record(table,2) in setup() + /* + The first item->rec_offset bytes are taken care of with + restore_record(table,2) in setup() + */ memcpy(buf + item->rec_offset, key, item->tree.size_of_element); if ((error = item->table->file->write_row(buf))) { - if (error != HA_ERR_FOUND_DUPP_KEY && - error != HA_ERR_FOUND_DUPP_UNIQUE) - return 1; + if (error != HA_ERR_FOUND_DUPP_KEY && + error != HA_ERR_FOUND_DUPP_UNIQUE) + return 1; } - return 0; } + Item_sum_count_distinct::~Item_sum_count_distinct() { if (table) free_tmp_table(current_thd, table); delete tmp_table_param; - if(use_tree) + if (use_tree) delete_tree(&tree); } @@ -895,91 +892,108 @@ bool Item_sum_count_distinct::setup(THD *thd) tmp_table_param->cleanup(); } if (!(table=create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, 1, - 0, 0, current_lex->select->options | thd->options))) + 0, 0, + current_lex->select->options | thd->options))) return 1; table->file->extra(HA_EXTRA_NO_ROWS); // Don't update rows table->no_rows=1; - if(table->db_type == DB_TYPE_HEAP) // no blobs, otherwise it would be - // MyISAM - { - qsort_cmp2 compare_key; - void* cmp_arg; - int key_len; + // no blobs, otherwise it would be MyISAM + if (table->db_type == DB_TYPE_HEAP) + { + qsort_cmp2 compare_key; + void* cmp_arg; + int key_len; - // to make things easier for dump_leaf if we ever have to dump to - // MyISAM - restore_record(table,2); + // to make things easier for dump_leaf if we ever have to dump to MyISAM + restore_record(table,2); - if(table->fields == 1) // if we have only one field, which is - // the most common use of count(distinct), it is much faster - // to use a simpler key compare method that can take advantage - // of not having to worry about other fields - { - Field* field = table->field[0]; - switch(field->type()) - { - // if we have a string, we must take care of charsets - // and case sensitivity - case FIELD_TYPE_STRING: - case FIELD_TYPE_VAR_STRING: - compare_key = (qsort_cmp2)(field->binary() ? simple_raw_key_cmp: - simple_str_key_cmp); - break; - default: // since at this point we cannot have blobs - // anything else can be compared with memcmp - compare_key = (qsort_cmp2)simple_raw_key_cmp; - break; - } - cmp_arg = (void*)(key_len = field->pack_length()); - rec_offset = 1; - } - else // too bad, cannot cheat - there is more than one field - { - bool all_binary = 1; - Field** field, **field_end; - field_end = (field = table->field) + table->fields; - for(key_len = 0; field < field_end; ++field) - { - key_len += (*field)->pack_length(); - if(!(*field)->binary()) - all_binary = 0; - } - rec_offset = table->reclength - key_len; - if(all_binary) - { - compare_key = (qsort_cmp2)simple_raw_key_cmp; - cmp_arg = (void*)key_len; - } - else - { - compare_key = (qsort_cmp2)composite_key_cmp ; - cmp_arg = (void*)this; - } - } - - init_tree(&tree, min(max_heap_table_size, sortbuff_size/16), 0, - key_len, compare_key, 0, NULL, cmp_arg); - use_tree = 1; - - // the only time key_len could be 0 is if someone does - // count(distinct) on a char(0) field - stupid thing to do, - // but this has to be handled - otherwise someone can crash - // the server with a DoS attack - max_elements_in_tree = (key_len) ? max_heap_table_size/key_len : - 1; + if (table->fields == 1) + { + /* + If we have only one field, which is the most common use of + count(distinct), it is much faster to use a simpler key + compare method that can take advantage of not having to worry + about other fields + */ + Field* field = table->field[0]; + switch(field->type()) + { + /* + If we have a string, we must take care of charsets and case + sensitivity + */ + case FIELD_TYPE_STRING: + case FIELD_TYPE_VAR_STRING: + compare_key = (qsort_cmp2)(field->binary() ? simple_raw_key_cmp: + simple_str_key_cmp); + break; + default: + /* + Since at this point we cannot have blobs anything else can + be compared with memcmp + */ + compare_key = (qsort_cmp2)simple_raw_key_cmp; + break; + } + cmp_arg = (void*)(key_len = field->pack_length()); + rec_offset = 1; } - + else // too bad, cannot cheat - there is more than one field + { + bool all_binary = 1; + Field** field, **field_end; + field_end = (field = table->field) + table->fields; + uint32 *lengths; + if (!(field_lengths= + (uint32*) thd->alloc(sizeof(uint32) * table->fields))) + return 1; + + for (key_len = 0, lengths=field_lengths; field < field_end; ++field) + { + uint32 length= (*field)->pack_length(); + key_len += length; + *lengths++ = length; + if (!(*field)->binary()) + all_binary = 0; // Can't break loop here + } + rec_offset = table->reclength - key_len; + if (all_binary) + { + compare_key = (qsort_cmp2)simple_raw_key_cmp; + cmp_arg = (void*)key_len; + } + else + { + compare_key = (qsort_cmp2) composite_key_cmp ; + cmp_arg = (void*)this; + } + } + + init_tree(&tree, min(max_heap_table_size, sortbuff_size/16), 0, + key_len, compare_key, 0, NULL, cmp_arg); + use_tree = 1; + + /* + The only time key_len could be 0 is if someone does + count(distinct) on a char(0) field - stupid thing to do, + but this has to be handled - otherwise someone can crash + the server with a DoS attack + */ + max_elements_in_tree = ((key_len) ? max_heap_table_size/key_len : + 1); + } return 0; } + int Item_sum_count_distinct::tree_to_myisam() { - if(create_myisam_from_heap(table, tmp_table_param, - HA_ERR_RECORD_FILE_FULL, 1) || - tree_walk(&tree, (tree_walk_action)&dump_leaf, (void*)this, - left_root_right)) + if (create_myisam_from_heap(table, tmp_table_param, + HA_ERR_RECORD_FILE_FULL, 1) || + tree_walk(&tree, (tree_walk_action)&dump_leaf, (void*)this, + left_root_right)) return 1; delete_tree(&tree); use_tree = 0; @@ -1011,18 +1025,20 @@ bool Item_sum_count_distinct::add() if ((*field)->is_real_null(0)) return 0; // Don't count NULL - if(use_tree) + if (use_tree) + { + /* + If the tree got too big, convert to MyISAM, otherwise insert into the + tree. + */ + if (tree.elements_in_tree > max_elements_in_tree) { - // if the tree got too big, convert to MyISAM, otherwise - // insert into the tree - if(tree.elements_in_tree > max_elements_in_tree) - { - if(tree_to_myisam()) - return 1; - } - else if(!tree_insert(&tree, table->record[0] + rec_offset, 0)) + if(tree_to_myisam()) return 1; } + else if (!tree_insert(&tree, table->record[0] + rec_offset, 0)) + return 1; + } else if ((error=table->file->write_row(table->record[0]))) { if (error != HA_ERR_FOUND_DUPP_KEY && @@ -1039,7 +1055,7 @@ longlong Item_sum_count_distinct::val_int() { if (!table) // Empty query return LL(0); - if(use_tree) + if (use_tree) return tree.elements_in_tree; table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); return table->file->records; diff --git a/sql/item_sum.h b/sql/item_sum.h index 753a9de8b48..5500afa43ca 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -146,6 +146,7 @@ class Item_sum_count_distinct :public Item_sum_int TABLE *table; table_map used_table_cache; bool fix_fields(THD *thd,TABLE_LIST *tables); + uint32 *field_lengths; TMP_TABLE_PARAM *tmp_table_param; TREE tree; diff --git a/sql/key.cc b/sql/key.cc index 80a33bc45d3..2c7c9361eb3 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -250,7 +250,7 @@ void key_unpack(String *to,TABLE *table,uint idx) bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields) { - List_iterator<Item> f(fields); + List_iterator_fast<Item> f(fields); KEY_PART_INFO *key_part,*key_part_end; for (key_part=table->key_info[idx].key_part,key_part_end=key_part+ table->key_info[idx].key_parts ; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 676118c1fbe..d02a2eb729e 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -174,6 +174,9 @@ char* query_table_status(THD *thd,const char *db,const char *table_name); #define QUERY_NO_INDEX_USED OPTION_STATUS_NO_TRANS_UPDATE*2 #define QUERY_NO_GOOD_INDEX_USED QUERY_NO_INDEX_USED*2 +#define SELECT_NO_UNLOCK (QUERY_NO_GOOD_INDEX_USED*2) +#define TMP_TABLE_ALL_COLUMNS (SELECT_NO_UNLOCK*2) + #define RAID_BLOCK_SIZE 1024 /* BINLOG_DUMP options */ @@ -305,8 +308,8 @@ int setup_order(THD *thd,TABLE_LIST *tables, List<Item> &fields, int mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &list,COND *conds, List<Item_func_match> &ftfuncs, ORDER *order, ORDER *group,Item *having,ORDER *proc_param, - uint select_type,select_result *result); -int mysql_union(THD *thd,LEX *lex, uint no); + ulong select_type,select_result *result); +int mysql_union(THD *thd,LEX *lex); Field *create_tmp_field(TABLE *table,Item *item, Item::Type type, Item_result_field ***copy_func, Field **from_field, bool group,bool modify_item); @@ -422,7 +425,8 @@ bool insert_fields(THD *thd,TABLE_LIST *tables, List_iterator<Item> *it); bool setup_tables(TABLE_LIST *tables); int setup_fields(THD *thd,TABLE_LIST *tables,List<Item> &item, - bool set_query_id,List<Item> *sum_func_list); + bool set_query_id,List<Item> *sum_func_list, + bool allow_sum_func); int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds); int setup_ftfuncs(THD *thd,TABLE_LIST *tables, List<Item_func_match> &ftfuncs); void wait_for_refresh(THD *thd); diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index df49d52d54a..c16c0d919d4 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -32,7 +32,7 @@ static bool find_range_key(TABLE_REF *ref, Field* field,COND *cond); int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) { - List_iterator<Item> it(all_fields); + List_iterator_fast<Item> it(all_fields); int const_result=1; bool recalc_const_item=0; table_map removed_tables=0; @@ -205,7 +205,7 @@ uint count_table_entries(COND *cond,TABLE *table) if (((Item_cond*) cond)->functype() == Item_func::COND_OR_FUNC) return (cond->used_tables() & table->map) ? MAX_REF_PARTS+1 : 0; - List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list()); Item *item; uint count=0; while ((item=li++)) @@ -250,7 +250,7 @@ bool part_of_cond(COND *cond,Field *field) if (((Item_cond*) cond)->functype() == Item_func::COND_OR_FUNC) return 0; // Already checked - List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list()); Item *item; while ((item=li++)) { diff --git a/sql/share/Makefile.am b/sql/share/Makefile.am index 6bc05aa33cd..cb3056b5f5a 100644 --- a/sql/share/Makefile.am +++ b/sql/share/Makefile.am @@ -27,5 +27,11 @@ install-data-local: $(INSTALL_DATA) $(srcdir)/charsets/Index $(DESTDIR)$(pkgdatadir)/charsets/Index $(INSTALL_DATA) $(srcdir)/charsets/*.conf $(DESTDIR)$(pkgdatadir)/charsets +fix_errors: + for lang in @AVAILABLE_LANGUAGES@; \ + do \ + ../../extra/comp_err $(srcdir)/$$lang/errmsg.txt $(srcdir)/$$lang/errmsg.sys; \ + done + # Don't update the files from bitkeeper %::SCCS/s.% diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index 8fb28bb5aa7..53f50a93267 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -223,3 +223,5 @@ "Error connecting to master: %-.128s", "Error running query on master: %-.128s", "Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index aa5ce318779..2cb175a06ad 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -217,3 +217,5 @@ "Error connecting to master: %-.128s", "Error running query on master: %-.128s", "Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index 5aebadb910a..903e8af55a7 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -214,3 +214,5 @@ "Error connecting to master: %-.128s", "Error running query on master: %-.128s", "Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index 975227538c5..046b474b805 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -214,3 +214,5 @@ "Error connecting to master: %-.128s", "Error running query on master: %-.128s", "Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index 33c061e1eeb..d342c061dd9 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -218,3 +218,5 @@ "Error connecting to master: %-.128s", "Error running query on master: %-.128s", "Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index bbdfbf8e1b5..f55ec6dc158 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -214,3 +214,5 @@ "Error connecting to master: %-.128s", "Error running query on master: %-.128s", "Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index d12dc7caedc..1c58f50ac64 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -217,3 +217,5 @@ "Error connecting to master: %-.128s", "Error running query on master: %-.128s", "Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index b86e34e8cc4..4e03d8c75c8 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -214,3 +214,5 @@ "Error connecting to master: %-.128s", "Error running query on master: %-.128s", "Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index 80cc9da65f0..5bd9f3c2dba 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -216,3 +216,5 @@ "Error connecting to master: %-.128s", "Error running query on master: %-.128s", "Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index 08c91323c97..bc57116b207 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -214,3 +214,5 @@ "Error connecting to master: %-.128s", "Error running query on master: %-.128s", "Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index ffea1e1ceee..efcb0743f69 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -216,3 +216,5 @@ "Error connecting to master: %-.128s", "Error running query on master: %-.128s", "Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index 5359ed17c69..5b7d3fd4f65 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -214,3 +214,5 @@ "Error connecting to master: %-.128s", "Error running query on master: %-.128s", "Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index cf4d0de9200..e61c18c2fd2 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -216,3 +216,5 @@ "Error connecting to master: %-.128s", "Error running query on master: %-.128s", "Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index 22d4e4b52bd..2ec5580f305 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -216,3 +216,5 @@ "Error connecting to master: %-.128s", "Error running query on master: %-.128s", "Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index 31627a41d5c..44c6ed1696d 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -218,3 +218,5 @@ "Error connecting to master: %-.128s", "Error running query on master: %-.128s", "Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index 8542b661698..f450a5ef1ff 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -214,3 +214,5 @@ "Error connecting to master: %-.128s", "Error running query on master: %-.128s", "Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index ed211d3694b..dc62192e394 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -218,3 +218,5 @@ "Error connecting to master: %-.128s", "Error running query on master: %-.128s", "Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index 7965ba0b789..bacf534af40 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -217,3 +217,5 @@ "Error connecting to master: %-.128s", "Error running query on master: %-.128s", "Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index 9504f817d1c..4e9f553c18d 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -222,3 +222,5 @@ "Error connecting to master: %-.128s", "Error running query on master: %-.128s", "Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index 47565ab7358..408d78bc25e 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -215,3 +215,5 @@ "Error de coneccion a master: %-128s", "Error executando el query en master: %-128%", "Error de %s: %-128%", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index 9e866bc510a..bed72637191 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -205,12 +205,14 @@ "Kunde inte initializera replications-strukturerna. Kontrollera privilegerna för 'master.info'", "Kunde inte starta en tråd för replikering", "Användare '%-.64s' har redan 'max_user_connections' aktiva inloggningar", -"Du kan endast använda konstant-uttryck med SET", -"Lock wait timeout exceeded", -"The total number of locks exceeds the lock table size", -"Update locks cannot be acquired during a READ UNCOMMITTED transaction", +"Man kan endast använda konstant-uttryck med SET", +"Fick inte ett lås i tid", +"Antal lås överskrider antalet reserverade lås", +"Updaterings-lås kan inte göras när man använder READ UNCOMMITTED", "DROP DATABASE är inte tillåtet när man har ett globalt läs-lås", "CREATE DATABASE är inte tillåtet när man har ett globalt läs-lås", "Fick fel vid anslutning till master: %-.128s", "Fick fel vid utförande av command på mastern: %-.128s", "Fick fel vid utförande av %s: %-.128s", +"Felaktig använding av %s and %s", +"SELECT kommandona har olika antal kolumner" diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index 3cc53f1ef49..161e0f9b2e7 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -127,7 +127,7 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result, pc->f_end = pc->f_info + field_list.elements; pc->fields = field_list; - List_iterator<Item> it(pc->fields); + List_iterator_fast<Item> it(pc->fields); f_info = pc->f_info; Item *item; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index ea3d77c5158..31cadf6f778 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -189,7 +189,7 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *wild) bool send_fields(THD *thd,List<Item> &list,uint flag) { - List_iterator<Item> it(list); + List_iterator_fast<Item> it(list); Item *item; char buff[80]; CONVERT *convert= (flag & 4) ? (CONVERT*) 0 : thd->convert_set; @@ -1738,14 +1738,15 @@ find_item_in_list(Item *find,List<Item> &items) ****************************************************************************/ int setup_fields(THD *thd, TABLE_LIST *tables, List<Item> &fields, - bool set_query_id, List<Item> *sum_func_list) + bool set_query_id, List<Item> *sum_func_list, + bool allow_sum_func) { reg2 Item *item; List_iterator<Item> it(fields); DBUG_ENTER("setup_fields"); thd->set_query_id=set_query_id; - thd->allow_sum_func= test(sum_func_list); + thd->allow_sum_func= allow_sum_func; thd->where="field list"; while ((item=it++)) @@ -1761,7 +1762,8 @@ int setup_fields(THD *thd, TABLE_LIST *tables, List<Item> &fields, { if (item->fix_fields(thd,tables)) DBUG_RETURN(-1); /* purecov: inspected */ - if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) + if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM && + sum_func_list) item->split_sum_func(*sum_func_list); thd->used_tables|=item->used_tables(); } @@ -1816,7 +1818,7 @@ static key_map get_key_map_from_key_list(TABLE *table, List<String> *index_list) { key_map map=0; - List_iterator<String> it(*index_list); + List_iterator_fast<String> it(*index_list); String *name; uint pos; while ((name=it++)) @@ -1996,7 +1998,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) int fill_record(List<Item> &fields,List<Item> &values) { - List_iterator<Item> f(fields),v(values); + List_iterator_fast<Item> f(fields),v(values); Item *value; Item_field *field; DBUG_ENTER("fill_record"); @@ -2014,7 +2016,7 @@ fill_record(List<Item> &fields,List<Item> &values) int fill_record(Field **ptr,List<Item> &values) { - List_iterator<Item> v(values); + List_iterator_fast<Item> v(values); Item *value; DBUG_ENTER("fill_record"); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 992cd30a02c..85cea022e5d 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -274,7 +274,7 @@ bool select_send::send_fields(List<Item> &list,uint flag) bool select_send::send_data(List<Item> &items) { - List_iterator<Item> li(items); + List_iterator_fast<Item> li(items); String *packet= &thd->packet; DBUG_ENTER("send_data"); @@ -299,12 +299,6 @@ bool select_send::send_data(List<Item> &items) DBUG_RETURN(error); } - -void select_send::send_error(uint errcode,const char *err) -{ - ::send_error(&thd->net,errcode,err); -} - bool select_send::send_eof() { /* Unlock tables before sending packet to gain some speed */ @@ -367,7 +361,7 @@ select_export::prepare(List<Item> &list) } /* Check if there is any blobs in data */ { - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; while ((item=li++)) { @@ -414,7 +408,7 @@ bool select_export::send_data(List<Item> &items) Item *item; char *buff_ptr=buff; uint used_length=0,items_left=items.elements; - List_iterator<Item> li(items); + List_iterator_fast<Item> li(items); if (my_b_write(&cache,(byte*) exchange->line_start->ptr(), exchange->line_start->length())) @@ -607,7 +601,7 @@ select_dump::prepare(List<Item> &list __attribute__((unused))) bool select_dump::send_data(List<Item> &items) { - List_iterator<Item> li(items); + List_iterator_fast<Item> li(items); char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff)),*res; tmp.length(0); diff --git a/sql/sql_class.h b/sql/sql_class.h index 959460a6f4b..ded8468fc80 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -413,6 +413,8 @@ public: class JOIN; +void send_error(NET *net,uint sql_errno=0, const char *err=0); + class select_result :public Sql_alloc { protected: THD *thd; @@ -423,7 +425,10 @@ public: virtual bool send_fields(List<Item> &list,uint flag)=0; virtual bool send_data(List<Item> &items)=0; virtual void initialize_tables (JOIN *join=0) {} - virtual void send_error(uint errcode,const char *err)=0; + virtual void send_error(uint errcode,const char *err) + { + ::send_error(&thd->net,errcode,err); + } virtual bool send_eof()=0; virtual void abort() {} }; @@ -434,7 +439,6 @@ public: select_send() {} bool send_fields(List<Item> &list,uint flag); bool send_data(List<Item> &items); - void send_error(uint errcode,const char *err); bool send_eof(); }; @@ -458,6 +462,7 @@ public: bool send_eof(); }; + class select_dump :public select_result { sql_exchange *exchange; File file; @@ -475,30 +480,31 @@ public: void send_error(uint errcode,const char *err); bool send_eof(); }; + + class select_insert :public select_result { public: TABLE *table; List<Item> *fields; - uint save_time_stamp; ulonglong last_insert_id; COPY_INFO info; - bool unions; + uint save_time_stamp; - select_insert(TABLE *table_par,List<Item> *fields_par,enum_duplicates duplic, bool u=false) - :table(table_par),fields(fields_par), save_time_stamp(0),last_insert_id(0) - { - bzero((char*) &info,sizeof(info)); - info.handle_duplicates=duplic; unions = u; - } + select_insert(TABLE *table_par,List<Item> *fields_par,enum_duplicates duplic) + :table(table_par),fields(fields_par), last_insert_id(0), save_time_stamp(0) { + bzero((char*) &info,sizeof(info)); + info.handle_duplicates=duplic; + } ~select_insert(); int prepare(List<Item> &list); - bool send_fields(List<Item> &list, - uint flag) { return 0; } + bool send_fields(List<Item> &list, uint flag) + { return 0; } bool send_data(List<Item> &items); void send_error(uint errcode,const char *err); bool send_eof(); }; + class select_create: public select_insert { ORDER *group; const char *db; @@ -513,8 +519,8 @@ public: HA_CREATE_INFO *create_info_par, List<create_field> &fields_par, List<Key> &keys_par, - List<Item> &select_fields,enum_duplicates duplic, bool u=false) - :select_insert (NULL, &select_fields, duplic, u), db(db_name), + List<Item> &select_fields,enum_duplicates duplic) + :select_insert (NULL, &select_fields, duplic), db(db_name), name(table_name), extra_fields(&fields_par),keys(&keys_par), create_info(create_info_par), lock(0) @@ -525,6 +531,22 @@ public: void abort(); }; +class select_union :public select_result { + public: + TABLE *table; + COPY_INFO info; + uint save_time_stamp; + + select_union(TABLE *table_par); + ~select_union(); + int prepare(List<Item> &list); + bool send_fields(List<Item> &list, uint flag) + { return 0; } + bool send_data(List<Item> &items); + bool send_eof(); + bool flush(); +}; + /* Structs used when sorting */ typedef struct st_sort_field { diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index a605984aef7..98cc523dc9b 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -152,7 +152,7 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables, MYF(0),keyinfo->key_parts); goto err; } - List_iterator<Item> it_ke(*key_expr); + List_iterator_fast<Item> it_ke(*key_expr); Item *item; for (key_len=0 ; (item=it_ke++) ; key_part++) { diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 5ac55ca6eac..c21e7668a02 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -78,7 +78,8 @@ check_insert_fields(THD *thd,TABLE *table,List<Item> &fields, table_list.grant=table->grant; thd->dupp_field=0; - if (setup_tables(&table_list) || setup_fields(thd,&table_list,fields,1,0)) + if (setup_tables(&table_list) || + setup_fields(thd,&table_list,fields,1,0,0)) return -1; if (thd->dupp_field) { @@ -109,7 +110,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, ulonglong id; COPY_INFO info; TABLE *table; - List_iterator<List_item> its(values_list); + List_iterator_fast<List_item> its(values_list); List_item *values; char *query=thd->query; DBUG_ENTER("mysql_insert"); @@ -151,7 +152,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, save_time_stamp=table->time_stamp; values= its++; if (check_insert_fields(thd,table,fields,*values,1) || - setup_tables(table_list) || setup_fields(thd,table_list,*values,0,0)) + setup_tables(table_list) || setup_fields(thd,table_list,*values,0,0,0)) { table->time_stamp=save_time_stamp; goto abort; @@ -168,7 +169,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, table->time_stamp=save_time_stamp; goto abort; } - if (setup_fields(thd,table_list,*values,0,0)) + if (setup_fields(thd,table_list,*values,0,0,0)) { table->time_stamp=save_time_stamp; goto abort; @@ -1237,14 +1238,14 @@ select_insert::prepare(List<Item> &values) restore_record(table,2); // Get empty record table->next_number_field=table->found_next_number_field; - thd->count_cuted_fields=1; /* calc cuted fields */ + thd->count_cuted_fields=1; // calc cuted fields thd->cuted_fields=0; - if (info.handle_duplicates != DUP_REPLACE) - table->file->extra(HA_EXTRA_WRITE_CACHE); - if (info.handle_duplicates == DUP_IGNORE || - info.handle_duplicates == DUP_REPLACE) - table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); - table->file->deactivate_non_unique_index((ha_rows) 0); + if (info.handle_duplicates != DUP_REPLACE) + table->file->extra(HA_EXTRA_WRITE_CACHE); + if (info.handle_duplicates == DUP_IGNORE || + info.handle_duplicates == DUP_REPLACE) + table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); + table->file->deactivate_non_unique_index((ha_rows) 0); DBUG_RETURN(0); } @@ -1319,8 +1320,7 @@ bool select_insert::send_eof() thd->cuted_fields); if (last_insert_id) thd->insert_id(last_insert_id); // For update log - if (!unions) - ::send_ok(&thd->net,info.copied,last_insert_id,buff); + ::send_ok(&thd->net,info.copied,last_insert_id,buff); mysql_update_log.write(thd,thd->query,thd->query_length); if (mysql_bin_log.is_open()) { @@ -1390,6 +1390,7 @@ bool select_create::send_data(List<Item> &values) extern HASH open_cache; + bool select_create::send_eof() { bool tmp=select_insert::send_eof(); @@ -1403,8 +1404,7 @@ bool select_create::send_eof() if (!table->tmp_table) hash_delete(&open_cache,(byte*) table); lock=0; - if (!unions) - table=0; + table=0; VOID(pthread_mutex_unlock(&LOCK_open)); } return tmp; @@ -1436,7 +1436,7 @@ void select_create::abort() *****************************************************************************/ #ifdef __GNUC__ -template class List_iterator<List_item>; +template class List_iterator_fast<List_item>; template class I_List<delayed_insert>; template class I_List_iterator<delayed_insert>; template class I_List<delayed_row>; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 627c1a23de3..dadabe999c4 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -115,7 +115,8 @@ typedef struct st_select_lex { List<List_item> when_list; SQL_LIST order_list,table_list,group_list; List<Item> item_list; - List<String> interval_list,use_index, *use_index_ptr, ignore_index, *ignore_index_ptr; + List<String> interval_list,use_index, *use_index_ptr, + ignore_index, *ignore_index_ptr; List<Item_func_match> ftfunc_list; uint in_sum_expr, sort_default; bool create_refs; diff --git a/sql/sql_list.cc b/sql/sql_list.cc index 7d5fc442121..be630dfc038 100644 --- a/sql/sql_list.cc +++ b/sql/sql_list.cc @@ -20,3 +20,5 @@ #endif #include "mysql_priv.h" + +list_node end_of_list; diff --git a/sql/sql_list.h b/sql/sql_list.h index b86250f70b6..1486502148f 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -34,25 +34,40 @@ public: /* ** basic single linked list ** Used for item and item_buffs. +** All list ends with a pointer to the 'end_of_list' element, which +** data pointer is a null pointer and the next pointer points to itself. +** This makes it very fast to traverse lists as we don't have to +** test for a specialend condition for list that can't contain a null +** pointer. */ +class list_node :public Sql_alloc +{ +public: + list_node *next; + void *info; + list_node(void *info_par,list_node *next_par) + :next(next_par),info(info_par) + {} + list_node() /* For end_of_list */ + { + info=0; + next= this; + } + friend class base_list; + friend class base_list_iterator; +}; + +extern list_node end_of_list; + class base_list :public Sql_alloc { protected: - class list_node :public Sql_alloc - { - public: - list_node *next; - void *info; - list_node(void *info_par,list_node *next_par) : next(next_par),info(info_par) {} - friend class base_list; - friend class base_list_iterator; - }; list_node *first,**last; public: uint elements; - inline void empty() { elements=0; first=0; last=&first;} + inline void empty() { elements=0; first= &end_of_list; last=&first;} inline base_list() { empty(); } inline base_list(const base_list &tmp) :Sql_alloc() { @@ -62,7 +77,7 @@ public: } inline bool push_back(void *info) { - if (((*last)=new list_node(info,0))) + if (((*last)=new list_node(info, &end_of_list))) { last= &(*last)->next; elements++; @@ -75,7 +90,7 @@ public: list_node *node=new list_node(info,first); if (node) { - if (!first) + if (last == &first) last= &node->next; first=node; elements++; @@ -89,22 +104,21 @@ public: delete *prev; *prev=node; if (!--elements) - { last= &first; - first=0; - } } inline void *pop(void) { - if (!first) return 0; + if (first == &end_of_list) return 0; list_node *tmp=first; first=first->next; if (!--elements) last= &first; return tmp->info; } - inline void *head() { return first ? first->info : 0; } - inline void **head_ref() { return first ? &first->info : 0; } + inline void *head() { return first->info; } + inline void **head_ref() { return first != &end_of_list ? &first->info : 0; } + inline bool is_empty() { return first == &end_of_list ; } + inline list_node *last_ref() { return &end_of_list; } friend class base_list_iterator; protected: @@ -122,7 +136,7 @@ protected: class base_list_iterator { base_list *list; - base_list::list_node **el,**prev,*current; + list_node **el,**prev,*current; public: base_list_iterator(base_list &list_par) :list(&list_par),el(&list_par.first), prev(0),current(0) @@ -130,16 +144,22 @@ public: inline void *next(void) { prev=el; - if (!(current= *el)) - return 0; + current= *el; el= ¤t->next; return current->info; } + inline void *next_fast(void) + { + list_node *tmp; + tmp= *el; + el= &tmp->next; + return tmp->info; + } inline void rewind(void) { el= &list->first; } - void *replace(void *element) + inline void *replace(void *element) { // Return old element void *tmp=current->info; current->info=element; @@ -148,7 +168,7 @@ public: void *replace(base_list &new_list) { void *ret_value=current->info; - if (new_list.first) + if (!new_list.is_empty()) { *new_list.last=current->next; current->info=new_list.first->info; @@ -175,7 +195,7 @@ public: } inline bool is_last(void) { - return *el == 0; + return el == &list->last_ref()->next; } }; @@ -193,7 +213,7 @@ public: void delete_elements(void) { list_node *element,*next; - for (element=first; element ; element=next) + for (element=first; element != &end_of_list; element=next) { next=element->next; delete (T*) element->info; @@ -208,13 +228,25 @@ template <class T> class List_iterator :public base_list_iterator public: List_iterator(List<T> &a) : base_list_iterator(a) {} inline T* operator++(int) { return (T*) base_list_iterator::next(); } - inline void rewind(void) { base_list_iterator::rewind(); } inline T *replace(T *a) { return (T*) base_list_iterator::replace(a); } inline T *replace(List<T> &a) { return (T*) base_list_iterator::replace(a); } - inline void remove(void) { base_list_iterator::remove(); } inline void after(T *a) { base_list_iterator::after(a); } inline T** ref(void) { return (T**) base_list_iterator::ref(); } - inline bool is_last(void) { return base_list_iterator::is_last(); } +}; + +template <class T> class List_iterator_fast :public base_list_iterator +{ +protected: + inline T *replace(T *a) { return (T*) 0; } + inline T *replace(List<T> &a) { return (T*) 0; } + inline void remove(void) { } + inline void after(T *a) { } + inline T** ref(void) { return (T**) 0; } + +public: + List_iterator_fast(List<T> &a) : base_list_iterator(a) {} + inline T* operator++(int) { return (T*) base_list_iterator::next_fast(); } + inline void rewind(void) { base_list_iterator::rewind(); } }; diff --git a/sql/sql_load.cc b/sql/sql_load.cc index ce8e34b9265..53e22a3c1dd 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -91,7 +91,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, else { // Part field list thd->dupp_field=0; - if (setup_tables(table_list) || setup_fields(thd,table_list,fields,1,0)) + if (setup_tables(table_list) || setup_fields(thd,table_list,fields,1,0,0)) DBUG_RETURN(-1); if (thd->dupp_field) { @@ -102,7 +102,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, uint tot_length=0; bool use_blobs=0,use_timestamp=0; - List_iterator<Item> it(fields); + List_iterator_fast<Item> it(fields); Item_field *field; while ((field=(Item_field*) it++)) @@ -269,7 +269,7 @@ static int read_fixed_length(THD *thd,COPY_INFO &info,TABLE *table,List<Item> &fields, READ_INFO &read_info) { - List_iterator<Item> it(fields); + List_iterator_fast<Item> it(fields); Item_field *sql_field; DBUG_ENTER("read_fixed_length"); @@ -332,7 +332,7 @@ read_sep_field(THD *thd,COPY_INFO &info,TABLE *table, List<Item> &fields, READ_INFO &read_info, String &enclosed) { - List_iterator<Item> it(fields); + List_iterator_fast<Item> it(fields); Item_field *sql_field; uint enclosed_length; DBUG_ENTER("read_sep_field"); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 6078d3ce22a..6c3205c2feb 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -47,7 +47,8 @@ static void mysql_init_query(THD *thd); static void remove_escape(char *name); static void refresh_status(void); static bool append_file_to_dir(char **filename_ptr, char *table_name); -static int link_in_large_list_and_check_acl(THD *thd,LEX *lex,SQL_LIST *tables); +static int create_total_list_and_check_acl(THD *thd, LEX *lex, + TABLE_LIST **result); const char *any_db="*any*"; // Special symbol for check_access @@ -1737,10 +1738,10 @@ mysql_execute_command(void) } case SQLCOM_UNION_SELECT: { - SQL_LIST *total=(SQL_LIST *) thd->calloc(sizeof(SQL_LIST)); + TABLE_LIST *total; if (select_lex->options & SELECT_DESCRIBE) lex->exchange=0; - if ((res = link_in_large_list_and_check_acl(thd,lex,total)) == -1) + if ((res = create_total_list_and_check_acl(thd,lex,&total)) == -1) { res=0; break; @@ -1753,31 +1754,31 @@ mysql_execute_command(void) res=0; break; } - if (!(res=open_and_lock_tables(thd,(TABLE_LIST *)total->first))) - { - /* Fix tables--to-be-unioned-from list to point at opened tables */ - for (SELECT_LEX *sl=&lex->select_lex;sl;sl=sl->next) - { - for (TABLE_LIST *cursor=(TABLE_LIST *)sl->table_list.first;cursor;cursor=cursor->next) - cursor->table= ((TABLE_LIST*) cursor->table)->table; - } - ha_rows save_it=thd->offset_limit; thd->offset_limit=0; - res=mysql_union(thd,lex, select_lex->select_number+1); - thd->offset_limit=save_it; - } + if (!(res=open_and_lock_tables(thd, total))) + { + /* Fix tables--to-be-unioned-from list to point at opened tables */ + for (SELECT_LEX *sl=&lex->select_lex; sl; sl=sl->next) + { + for (TABLE_LIST *cursor= (TABLE_LIST *)sl->table_list.first; + cursor; + cursor=cursor->next) + cursor->table= ((TABLE_LIST*) cursor->table)->table; + } + res=mysql_union(thd,lex); + } close_thread_tables(thd); break; } case SQLCOM_DROP_TABLE: - { - if (check_table_access(thd,DROP_ACL,tables)) - goto error; /* purecov: inspected */ - if (end_active_trans(thd)) - res= -1; - else - res = mysql_rm_table(thd,tables,lex->drop_if_exists); - } - break; + { + if (check_table_access(thd,DROP_ACL,tables)) + goto error; /* purecov: inspected */ + if (end_active_trans(thd)) + res= -1; + else + res = mysql_rm_table(thd,tables,lex->drop_if_exists); + } + break; case SQLCOM_DROP_INDEX: if (!tables->db) tables->db=thd->db; @@ -2901,53 +2902,66 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, DBUG_RETURN(ptr); } -static int link_in_large_list_and_check_acl(THD *thd,LEX *lex,SQL_LIST *tables) + +/* +** This is used for UNION to create a new table list of all used tables +** The table_list->table entry in all used tables are set to point +** to the entries in this list. +*/ + +static int create_total_list_and_check_acl(THD *thd, LEX *lex, + TABLE_LIST **result) { - SELECT_LEX *sl; const char *current_db=thd->db ? thd->db : ""; - TABLE_LIST *ptr; - for (sl=&lex->select_lex;sl;sl=sl->next) + SELECT_LEX *sl; + TABLE_LIST **new_table_list= result, *aux; + const char *current_db=thd->db ? thd->db : ""; // QQ; To be removed + + *new_table_list=0; // end result list + for (sl=&lex->select_lex; sl; sl=sl->next) { - if ((lex->sql_command == SQLCOM_UNION_SELECT) && (sl->order_list.first != (byte *)NULL) && (sl->next != (st_select_lex *)NULL)) + if ((lex->sql_command == SQLCOM_UNION_SELECT) && + sl->order_list.first && sl->next) { - net_printf(&thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); // correct error message will come here; only last SELECT can have ORDER BY + net_printf(&thd->net,ER_WRONG_USAGE,"UNION","ORDER BY"); return -1; } - if (sl->table_list.first == (byte *)NULL) continue; - TABLE_LIST *cursor,*aux=(TABLE_LIST*) sl->table_list.first; + aux= (TABLE_LIST*) sl->table_list.first; if (aux) { - if (check_table_access(thd, lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL , aux)) - return -1; - for (;aux;aux=aux->next) + TABLE_LIST *next; + if (check_table_access(thd, + lex->exchange ? + SELECT_ACL | FILE_ACL : SELECT_ACL , aux)) + return -1; + for (; aux; aux=next) { - if (!aux->db) - aux->db=(char *)current_db; - for (cursor=(TABLE_LIST *)tables->first;cursor;cursor=cursor->next) - if (!strcmp(cursor->db,aux->db) && (!strcmp(cursor->real_name,aux->real_name))) - break; - if (!cursor || !tables->first) - { - aux->lock_type= lex->lock_option; - if (!tables->next) - tables->next= (byte**) &tables->first; - if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST)))) - return 1; - ptr->db= aux->db; ptr->real_name=aux->real_name; - ptr->name=aux->name; ptr->lock_type=aux->lock_type; - ptr->updating=aux->updating; - ptr->use_index=aux->use_index; - ptr->ignore_index=aux->use_index; - aux->table=(TABLE *)ptr; - link_in_list(tables,(byte*)ptr,(byte**) &ptr->next); - } - else - aux->table=(TABLE *)cursor; + TABLE_LIST *cursor; + next= aux->next; + if (!aux->db) + aux->db=(char *)current_db; // QQ; To be removed + for (cursor= *result; cursor; cursor=cursor->next) + if (!strcmp(cursor->db,aux->db) && + (!strcmp(cursor->real_name,aux->real_name))) + break; + if (!cursor) + { + /* Add not used table to the total table list */ + aux->lock_type= lex->lock_option; + if (!(cursor = (TABLE_LIST *) thd->memdup((byte*) aux, + sizeof(*aux)))) + return 1; + *new_table_list= cursor; + new_table_list= &cursor->next; + *new_table_list=0; // end result list + } + aux->table=(TABLE *) cursor; } } } - return (tables->first) ? 0 : 1; + return 0; } + void add_join_on(TABLE_LIST *b,Item *expr) { if (!b->on_expr) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index eff200e9bdf..cd6caf213f8 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -152,7 +152,7 @@ int mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, List<Item_func_match> &ftfuncs, ORDER *order, ORDER *group,Item *having,ORDER *proc_param, - uint select_options,select_result *result) + ulong select_options,select_result *result) { TABLE *tmp_table; int error,tmp; @@ -178,7 +178,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, thd->used_tables=0; // Updated by setup_fields if (setup_tables(tables) || - setup_fields(thd,tables,fields,1,&all_fields) || + setup_fields(thd,tables,fields,1,&all_fields,1) || setup_conds(thd,tables,&conds) || setup_order(thd,tables,fields,all_fields,order) || setup_group(thd,tables,fields,all_fields,group,&hidden_group_fields)) @@ -207,7 +207,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, if (!group) { uint flag=0; - List_iterator<Item> it(fields); + List_iterator_fast<Item> it(fields); Item *item; while ((item= it++)) { @@ -373,7 +373,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, error=(int) result->send_eof(); } delete procedure; - DBUG_RETURN(0); + DBUG_RETURN(error); } error = -1; @@ -403,7 +403,8 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, error= 1; /* purecov: inspected */ goto err; /* purecov: inspected */ } - if (join.const_tables && !thd->locked_tables) + if (join.const_tables && !thd->locked_tables && + !(select_options & SELECT_NO_UNLOCK)) { TABLE **table, **end; for (table=join.table, end=table + join.const_tables ; @@ -571,7 +572,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, /* Perform FULLTEXT search before all regular searches */ if (ftfuncs.elements) { - List_iterator<Item_func_match> li(ftfuncs); + List_iterator_fast<Item_func_match> li(ftfuncs); Item_func_match *ifm; DBUG_PRINT("info",("Performing FULLTEXT search")); thd->proc_info="FULLTEXT searching"; @@ -1268,7 +1269,7 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level, { if (cond->type() == Item_func::COND_ITEM) { - List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list()); KEY_FIELD *org_key_fields= *key_fields; if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) @@ -1433,7 +1434,7 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array, } else if (cond->type() == Item::COND_ITEM) { - List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list()); if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) { @@ -2241,9 +2242,9 @@ make_simple_join(JOIN *join,TABLE *tmp_table) join->tables=1; join->const_tables=0; join->const_table_map=0; - join->tmp_table_param.copy_field_count=join->tmp_table_param.field_count= - join->tmp_table_param.sum_func_count= join->tmp_table_param.func_count=0; - join->tmp_table_param.copy_field=0; + join->tmp_table_param.field_count= join->tmp_table_param.sum_func_count= + join->tmp_table_param.func_count=0; + join->tmp_table_param.copy_field=join->tmp_table_param.copy_field_end=0; join->first_record=join->sort_and_group=0; join->sum_funcs=0; join->send_records=(ha_rows) 0; @@ -2591,7 +2592,8 @@ join_free(JOIN *join) } // We are not using tables anymore // Unlock all tables. We may be in an INSERT .... SELECT statement. - if (join->lock && join->thd->lock) + if (join->lock && join->thd->lock && + !(join->select_options & SELECT_NO_UNLOCK)) { mysql_unlock_read_tables(join->thd, join->lock);// Don't free join->lock join->lock=0; @@ -2949,7 +2951,7 @@ propagate_cond_constants(I_List<COND_CMP> *save_list,COND *and_level, { bool and_level= ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC; - List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list()); Item *item; I_List<COND_CMP> save; while ((item=li++)) @@ -3176,7 +3178,7 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item) { bool and_level= (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC); - List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list()); Item *item; while ((item=li++)) { @@ -3348,6 +3350,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, hidden_null_count, hidden_null_pack_length, hidden_field_count, blob_count,group_null_items; bool using_unique_constraint=0; + bool not_all_columns= !(select_options & TMP_TABLE_ALL_COLUMNS); char *tmpname,path[FN_REFLEN]; byte *pos,*group_buff; uchar *null_flags; @@ -3438,24 +3441,27 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, reclength=blob_count=null_count=hidden_null_count=group_null_items=0; param->using_indirect_summary_function=0; - List_iterator<Item> li(fields); + List_iterator_fast<Item> li(fields); Item *item; Field **tmp_from_field=from_field; while ((item=li++)) { Item::Type type=item->type(); - if (item->with_sum_func && type != Item::SUM_FUNC_ITEM) + if (not_all_columns) { - /* - Mark that the we have ignored an item that refers to a summary - function. We need to know this if someone is going to use - DISTINCT on the result. - */ - param->using_indirect_summary_function=1; - continue; + if (item->with_sum_func && type != Item::SUM_FUNC_ITEM) + { + /* + Mark that the we have ignored an item that refers to a summary + function. We need to know this if someone is going to use + DISTINCT on the result. + */ + param->using_indirect_summary_function=1; + continue; + } + if (item->const_item()) // We don't have to store this + continue; } - if (item->const_item()) // We don't have to store this - continue; if (type == Item::SUM_FUNC_ITEM && !group && !save_sum_fields) { /* Can't calc group yet */ ((Item_sum*) item)->result_field=0; @@ -3466,7 +3472,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, { Field *new_field= create_tmp_field(table,arg,arg->type(),©_func,tmp_from_field, - group != 0,1); + group != 0,not_all_columns); if (!new_field) goto err; // Should be OOM tmp_from_field++; @@ -3483,7 +3489,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, else { Field *new_field=create_tmp_field(table,item,type,©_func, - tmp_from_field, group != 0,1); + tmp_from_field, group != 0, + not_all_columns); if (!new_field) { if (thd->fatal_error) @@ -3620,7 +3627,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, null_count=(null_count+7) & ~7; // move to next byte } - param->copy_field_count=(uint) (copy - param->copy_field); + param->copy_field_end=copy; param->recinfo=recinfo; store_record(table,2); // Make empty default record @@ -6414,7 +6421,7 @@ setup_copy_fields(TMP_TABLE_PARAM *param,List<Item> &fields) goto err; } } - param->copy_field_count= (uint) (copy - param->copy_field); + param->copy_field_end= copy; DBUG_RETURN(0); err: @@ -6432,17 +6439,16 @@ void copy_fields(TMP_TABLE_PARAM *param) { Copy_field *ptr=param->copy_field; - Copy_field *end=ptr+param->copy_field_count; + Copy_field *end=param->copy_field_end; for ( ; ptr != end; ptr++) (*ptr->do_copy)(ptr); - List_iterator<Item> it(param->copy_funcs); + List_iterator_fast<Item> &it=param->copy_funcs_it; + it.rewind(); Item_copy_string *item; while ((item = (Item_copy_string*) it++)) - { item->copy(); - } } diff --git a/sql/sql_select.h b/sql/sql_select.h index 0ec1854d641..4fcffae31d2 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -118,19 +118,21 @@ typedef struct st_position { /* Used in find_best */ class TMP_TABLE_PARAM { public: List<Item> copy_funcs; - Copy_field *copy_field; + List_iterator_fast<Item> copy_funcs_it; + Copy_field *copy_field, *copy_field_end; byte *group_buff; Item_result_field **funcs; MI_COLUMNDEF *recinfo,*start_recinfo; KEY *keyinfo; ha_rows end_write_records; - uint copy_field_count,field_count,sum_func_count,func_count; + uint field_count,sum_func_count,func_count; uint hidden_field_count; uint group_parts,group_length; uint quick_group; bool using_indirect_summary_function; - TMP_TABLE_PARAM() :copy_field(0), group_parts(0), group_length(0) + TMP_TABLE_PARAM() + :copy_funcs_it(copy_funcs), copy_field(0), group_parts(0), group_length(0) {} ~TMP_TABLE_PARAM() { diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 34e8985007e..2bd6e383eac 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -72,7 +72,7 @@ mysqld_show_dbs(THD *thd,const char *wild) DBUG_RETURN(1); if (mysql_find_files(thd,&files,NullS,mysql_data_home,wild,1)) DBUG_RETURN(1); - List_iterator<char> it(files); + List_iterator_fast<char> it(files); while ((file_name=it++)) { if (!opt_safe_show_db || thd->master_access || @@ -154,7 +154,7 @@ int mysqld_show_tables(THD *thd,const char *db,const char *wild) DBUG_RETURN(1); if (mysql_find_files(thd,&files,db,path,wild,0)) DBUG_RETURN(-1); - List_iterator<char> it(files); + List_iterator_fast<char> it(files); while ((file_name=it++)) { thd->packet.length(0); @@ -284,7 +284,7 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild) if (mysql_find_files(thd,&files,db,path,wild,0)) DBUG_RETURN(-1); - List_iterator<char> it(files); + List_iterator_fast<char> it(files); while ((file_name=it++)) { TABLE_LIST table_list; @@ -1165,6 +1165,6 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables) } #ifdef __GNUC__ -template class List_iterator<char>; +template class List_iterator_fast<char>; template class List<char>; #endif diff --git a/sql/sql_table.cc b/sql/sql_table.cc index e91a9a83e73..8dbb9710cda 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -669,7 +669,7 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, DBUG_ENTER("create_table_from_items"); /* Add selected items to field list */ - List_iterator<Item> it(*items); + List_iterator_fast<Item> it(*items); Item *item; Field *tmp_field; tmp_table.db_create_options=0; diff --git a/sql/sql_union.cc b/sql/sql_union.cc new file mode 100644 index 00000000000..a06c8f87cf0 --- /dev/null +++ b/sql/sql_union.cc @@ -0,0 +1,217 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +/* + UNION of select's + UNION's were introduced by Monty and Sinisa <sinisa@mysql.com> +*/ + + +#include "mysql_priv.h" +#include "sql_select.h" + + +int mysql_union(THD *thd, LEX *lex) +{ + SELECT_LEX *sl, *last_sl; + ORDER *order; + List<Item> item_list; +/* TABLE_LIST *s=(TABLE_LIST*) lex->select_lex.table_list.first; */ + TABLE *table; + TABLE_LIST *first_table, result_table_list; + TMP_TABLE_PARAM tmp_table_param; + select_result *result; + select_union *union_result; + int res; + uint elements; + DBUG_ENTER("mysql_union"); + + /* Find last select part as it's here ORDER BY and GROUP BY is stored */ + elements= lex->select_lex.item_list.elements; + for (last_sl= &lex->select_lex; + last_sl->next; + last_sl=last_sl->next) + { + if (elements != last_sl->next->item_list.elements) + { + my_error(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,MYF(0)); + return -1; + } + } + + order = (ORDER *) last_sl->order_list.first; + { + Item *item; + List_iterator<Item> it(lex->select_lex.item_list); + + /* Create a list of items that will be in the result set */ + first_table= (TABLE_LIST*) lex->select_lex.table_list.first; + while ((item= it++)) + if (item_list.push_back(item)) + DBUG_RETURN(-1); + if (setup_fields(thd,first_table,item_list,0,0,1)) + DBUG_RETURN(-1); + } + bzero((char*) &tmp_table_param,sizeof(tmp_table_param)); + tmp_table_param.field_count=elements; + if (!(table=create_tmp_table(thd, &tmp_table_param, item_list, + (ORDER*) 0, !lex->union_option, + 1, 0, + (lex->select_lex.options | thd->options | + TMP_TABLE_ALL_COLUMNS)))) + DBUG_RETURN(-1); + table->file->extra(HA_EXTRA_WRITE_CACHE); + table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); + bzero((char*) &result_table_list,sizeof(result_table_list)); + result_table_list.db= (char*) ""; + result_table_list.real_name=result_table_list.name=(char*) "union"; + result_table_list.table=table; + + if (!(union_result=new select_union(table))) + { + res= -1; + goto exit; + } + for (sl= &lex->select_lex; sl; sl=sl->next) + { + thd->offset_limit=sl->offset_limit; + thd->select_limit=sl->select_limit+sl->offset_limit; + if (thd->select_limit < sl->select_limit) + thd->select_limit= HA_POS_ERROR; // no limit + if (thd->select_limit == HA_POS_ERROR) + sl->options&= ~OPTION_FOUND_ROWS; + + res=mysql_select(thd,(TABLE_LIST*) sl->table_list.first, + sl->item_list, + sl->where, + sl->ftfunc_list, + (ORDER*) 0, + (ORDER*) sl->group_list.first, + sl->having, + (ORDER*) NULL, + sl->options | thd->options | SELECT_NO_UNLOCK, + union_result); + if (res) + goto exit; + } + if (union_result->flush()) + { + res= 1; // Error is already sent + goto exit; + } + delete union_result; + + /* + Sinisa, we must also be able to handle + CREATE TABLE ... and INSERT ... SELECT with unions + + To do this, it's probably best that we add a new handle_select() function + which takes 'select_result' as parameter and let this internally handle + SELECT with and without unions. + */ + + if (lex->exchange) + { + if (lex->exchange->dumpfile) + result=new select_dump(lex->exchange); + else + result=new select_export(lex->exchange); + } + else + result=new select_send(); + res =-1; + if (result) + { + /* Create a list of fields in the temporary table */ + List_iterator<Item> it(item_list); + Field **field; + List<Item_func_match> ftfunc_list; + ftfunc_list.empty(); + + for (field=table->field ; *field ; field++) + { + (void) it++; + (void) it.replace(new Item_field(*field)); + } + if (!thd->fatal_error) // Check if EOM + res=mysql_select(thd,&result_table_list, + item_list, NULL, ftfunc_list, order, + (ORDER*) NULL, NULL, (ORDER*) NULL, + thd->options, result); + if (res) + result->abort(); + delete result; + } + +exit: + free_tmp_table(thd,table); + DBUG_RETURN(res); +} + + +/*************************************************************************** +** store records in temporary table for UNION +***************************************************************************/ + +select_union::select_union(TABLE *table_par) + :table(table_par) +{ + bzero((char*) &info,sizeof(info)); + /* + We can always use DUP_IGNORE because the temporary table will only + contain a unique key if we are using not using UNION ALL + */ + info.handle_duplicates=DUP_IGNORE; +} + +select_union::~select_union() +{ +} + +int select_union::prepare(List<Item> &list) +{ + return 0; +} + +bool select_union::send_data(List<Item> &values) +{ + if (thd->offset_limit) + { // using limit offset,count + thd->offset_limit--; + return 0; + } + fill_record(table->field,values); + return write_record(table,&info) ? 1 : 0; +} + +bool select_union::send_eof() +{ + return 0; +} + +bool select_union::flush() +{ + int error,error2; + error=table->file->extra(HA_EXTRA_NO_CACHE); + if (error) + { + table->file->print_error(error,MYF(0)); + ::send_error(&thd->net); + return 1; + } + return 0; +} diff --git a/sql/sql_unions.cc b/sql/sql_unions.cc deleted file mode 100644 index b480415619b..00000000000 --- a/sql/sql_unions.cc +++ /dev/null @@ -1,110 +0,0 @@ -/* Copyright (C) 2000 MySQL AB - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - - -/* UNION of select's */ - -/* UNION's were introduced by Monty and Sinisa <sinisa@mysql.com> */ - - -#include "mysql_priv.h" -#include "sql_select.h" - -/* Union of selects */ - - -int mysql_union(THD *thd,LEX *lex,uint no_of_selects) -{ - SELECT_LEX *sl, *for_order=&lex->select_lex; int res=0; - TABLE *table=(TABLE *)NULL; TABLE_LIST *resulting=(TABLE_LIST *)NULL; - for (;for_order->next;for_order=for_order->next); - ORDER *some_order = (ORDER *)for_order->order_list.first; - List<Item> list; - List_iterator<Item> it(lex->select_lex.item_list); - Item *item; - TABLE_LIST *s=(TABLE_LIST*) lex->select_lex.table_list.first; - while ((item= it++)) - if (list.push_back(item)) - return -1; - if (setup_fields(thd,s,list,0,0)) - return -1; - TMP_TABLE_PARAM *tmp_table_param= new TMP_TABLE_PARAM; - count_field_types(tmp_table_param,list,0); - tmp_table_param->end_write_records= HA_POS_ERROR; tmp_table_param->copy_field=0; - tmp_table_param->copy_field_count=tmp_table_param->field_count= - tmp_table_param->sum_func_count= tmp_table_param->func_count=0; - if (!(table=create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, !lex->union_option, - 0, 0, lex->select_lex.options | thd->options))) - return 1; - if (!(resulting = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST)))) - return 1; - resulting->db=s->db ? s->db : thd->db; - resulting->real_name=table->real_name; - resulting->name=table->table_name; - resulting->table=table; - - for (sl=&lex->select_lex;sl;sl=sl->next) - { - TABLE_LIST *tables=(TABLE_LIST*) sl->table_list.first; - select_insert *result; - if ((result=new select_insert(table,&list, DUP_IGNORE, true))) - { - res=mysql_select(thd,tables,sl->item_list, - sl->where, - sl->ftfunc_list, - (ORDER*) some_order, - (ORDER*) sl->group_list.first, - sl->having, - (ORDER*) NULL, - sl->options | thd->options, - result); - delete result; - if (res) - return res; - } - else - return -1; - } - select_result *result; - List<Item_func_match> ftfunc_list; - ftfunc_list.empty(); - if (lex->exchange) - { - if (lex->exchange->dumpfile) - result=new select_dump(lex->exchange); - else - result=new select_export(lex->exchange); - } - else result=new select_send(); - if (result) - { - res=mysql_select(thd,resulting,list, - NULL, - ftfunc_list, - (ORDER*) NULL, - (ORDER*) NULL, - NULL, - (ORDER*) NULL, - thd->options, - result); - if (res) - result->abort(); - delete result; - } - else - res=-1; - return res; -} diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 609c2642bc6..113995b169c 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -90,7 +90,7 @@ int mysql_update(THD *thd, /* Check the fields we are going to modify */ table->grant.want_privilege=want_privilege; - if (setup_fields(thd,table_list,fields,1,0)) + if (setup_fields(thd,table_list,fields,1,0,0)) DBUG_RETURN(-1); /* purecov: inspected */ if (table->timestamp_field) { @@ -103,7 +103,7 @@ int mysql_update(THD *thd, /* Check values */ table->grant.want_privilege=(SELECT_ACL & ~table->grant.privilege); - if (setup_fields(thd,table_list,values,0,0)) + if (setup_fields(thd,table_list,values,0,0,0)) { table->time_stamp=save_time_stamp; // Restore timestamp pointer DBUG_RETURN(-1); /* purecov: inspected */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index e9c5990fe14..c6ae2241603 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1319,7 +1319,7 @@ select: SELECT_SYM { LEX *lex=Lex; - if (lex->sql_command!=SQLCOM_UNION_SELECT) lex->sql_command= SQLCOM_SELECT; + lex->sql_command= SQLCOM_SELECT; lex->lock_option=TL_READ; mysql_init_select(lex); } @@ -3281,7 +3281,7 @@ opt_table: lex->grant = DB_ACLS & ~GRANT_ACL; else if (lex->columns.elements) { - net_printf(&lex->thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); + send_error(&lex->thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); YYABORT; } } @@ -3293,7 +3293,7 @@ opt_table: lex->grant = DB_ACLS & ~GRANT_ACL; else if (lex->columns.elements) { - net_printf(&lex->thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); + send_error(&lex->thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); YYABORT; } } @@ -3305,7 +3305,7 @@ opt_table: lex->grant = GLOBAL_ACLS & ~GRANT_ACL; else if (lex->columns.elements) { - net_printf(&lex->thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); + send_error(&lex->thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); YYABORT; } } @@ -3407,11 +3407,20 @@ union_list: UNION_SYM union_option { LEX *lex=Lex; - if (lex->exchange) YYABORT; /* Only the last SELECT can have INTO...... */ - lex->sql_command=SQLCOM_UNION_SELECT; - mysql_new_select(lex); lex->select->linkage=UNION_TYPE; + if (lex->exchange) + { + /* Only the last SELECT can have INTO...... */ + net_printf(¤t_thd->net, ER_WRONG_USAGE,"UNION","INTO"); + YYABORT; + } + mysql_new_select(lex); + lex->select->linkage=UNION_TYPE; } - select + select + { + Lex->sql_command=SQLCOM_UNION_SELECT; + } + union_option: /* empty */ {} |