diff options
-rw-r--r-- | sql/Makefile.am | 2 | ||||
-rw-r--r-- | sql/mysql_priv.h | 2 | ||||
-rw-r--r-- | sql/sql_lex.h | 2 | ||||
-rw-r--r-- | sql/sql_parse.cc | 67 | ||||
-rw-r--r-- | sql/sql_unions.cc | 33 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 26 |
6 files changed, 127 insertions, 5 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am index bc10bf7ae65..70415be03a4 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 + md5.c stacktrace.c sql_unions.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/mysql_priv.h b/sql/mysql_priv.h index 45249b96125..66a68b52b7f 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -232,6 +232,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list); bool mysql_change_db(THD *thd,const char *name); void mysql_parse(THD *thd,char *inBuf,uint length); void mysql_init_select(LEX *lex); +void mysql_new_select(LEX *lex); void init_max_user_conn(void); void free_max_user_conn(void); pthread_handler_decl(handle_one_connection,arg); @@ -304,6 +305,7 @@ 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); Field *create_tmp_field(TABLE *table,Item *item, Item::Type type, Item_result_field ***copy_func, Field **from_field, bool group,bool modify_item); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index d4483361961..be4a7d1273f 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -55,7 +55,7 @@ enum enum_sql_command { SQLCOM_RESET, SQLCOM_PURGE, SQLCOM_SHOW_BINLOGS, SQLCOM_SHOW_OPEN_TABLES, SQLCOM_LOAD_MASTER_DATA, SQLCOM_HA_OPEN, SQLCOM_HA_CLOSE, SQLCOM_HA_READ, - SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_MULTI_DELETE + SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_MULTI_DELETE, SQLCOM_UNION_SELECT }; enum lex_states { STATE_START, STATE_CHAR, STATE_IDENT, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index cfb22bcb685..20e33516225 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -47,6 +47,7 @@ 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 inline int link_in_large_list_and_check_acl(THD *thd,LEX *lex,SQL_LIST *tables); const char *any_db="*any*"; // Special symbol for check_access @@ -1719,6 +1720,30 @@ mysql_execute_command(void) close_thread_tables(thd); break; } + case SQLCOM_UNION_SELECT: + { + uint total_selects = select_lex->select_number; total_selects++; + SQL_LIST *total=(SQL_LIST *) thd->calloc(sizeof(SQL_LIST)); + if (select_lex->options & SELECT_DESCRIBE) + lex->exchange=0; + res = link_in_large_list_and_check_acl(thd,lex,total); + if (res == -1) + { + res=0; + break; + } + if (res && (res=check_access(thd, lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL, any_db))) + { + res=0; + break; + } + if (!(res=open_and_lock_tables(thd,(TABLE_LIST *)total->first))) + { + res=mysql_union(thd,lex,total_selects); + if (res==-1) res=0; + } + break; + } case SQLCOM_DROP_TABLE: { if (check_table_access(thd,DROP_ACL,tables)) @@ -2405,6 +2430,14 @@ mysql_init_select(LEX *lex) select_lex->next = (SELECT_LEX *)NULL; } +void +mysql_new_select(LEX *lex) +{ + uint select_no=lex->select->select_number; + SELECT_LEX *select_lex = (SELECT_LEX *)sql_calloc(sizeof(SELECT_LEX)); + lex->select->next=select_lex; + lex->select=select_lex; lex->select->select_number = ++select_no; +} void mysql_parse(THD *thd,char *inBuf,uint length) @@ -2838,6 +2871,40 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, DBUG_RETURN(ptr); } +static inline int link_in_large_list_and_check_acl(THD *thd,LEX *lex,SQL_LIST *tables) +{ + SELECT_LEX *sl; const char *current_db=thd->db ? thd->db : ""; + 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)) + { + net_printf(&thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); // correct error message will come here; only last SELECt can have ORDER BY + return -1; + } + if (sl->table_list.first == (byte *)NULL) continue; + TABLE_LIST *cursor,*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) + { + 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; + link_in_list(tables,(byte*)aux,(byte**) &aux->next); + } + } + } + } + return (tables->first) ? 0 : 1; +} + void add_join_on(TABLE_LIST *b,Item *expr) { if (!b->on_expr) diff --git a/sql/sql_unions.cc b/sql/sql_unions.cc new file mode 100644 index 00000000000..06612265f81 --- /dev/null +++ b/sql/sql_unions.cc @@ -0,0 +1,33 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & Monty & Sinisa + + 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 selects */ + +#include "mysql_priv.h" + +/* + Do a union of selects +*/ + + +int mysql_union(THD *thd,LEX *lex,uint no_of_selects) +{ + SELECT_LEX *sl; + for (sl=&lex->select_lex;sl;sl=sl->next) + { + } +} diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index a58186e50ac..60292d5a515 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -543,7 +543,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); opt_mi_check_type opt_to mi_check_types normal_join table_to_table_list table_to_table opt_table_list opt_as handler_rkey_function handler_rkey_mode handler_read_or_scan - single_multi table_multi_delete table_sini_wild + single_multi table_multi_delete table_sini_wild union union_list END_OF_INPUT %type <NONE> @@ -1260,11 +1260,11 @@ select: SELECT_SYM { LEX *lex=Lex; - lex->sql_command= SQLCOM_SELECT; + if (lex->sql_command!=SQLCOM_UNION_SELECT) lex->sql_command= SQLCOM_SELECT; lex->lock_option=TL_READ; mysql_init_select(lex); } - select_options select_item_list select_into select_lock_type + select_options select_item_list select_into select_lock_type union select_into: limit_clause {} @@ -3155,3 +3155,23 @@ commit: rollback: ROLLBACK_SYM { Lex->sql_command = SQLCOM_ROLLBACK;} + + +/* +** UNIONS : glue selects together +*/ + + +union: + /* empty */ {} + | union_list + +union_list: + UNION_SYM + { + 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; + } + select |