summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/Makefile.am2
-rw-r--r--sql/mysql_priv.h2
-rw-r--r--sql/sql_lex.h2
-rw-r--r--sql/sql_parse.cc67
-rw-r--r--sql/sql_unions.cc33
-rw-r--r--sql/sql_yacc.yy26
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