summaryrefslogtreecommitdiff
path: root/sql/sql_lex.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_lex.cc')
-rw-r--r--sql/sql_lex.cc993
1 files changed, 647 insertions, 346 deletions
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 16641ad6dd5..2b31abd6a50 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -17,20 +17,19 @@
/* A lexical scanner on a temporary buffer with a yacc interface */
+#define MYSQL_LEX 1
#include "mysql_priv.h"
#include "item_create.h"
#include <m_ctype.h>
#include <hash.h>
-
+#include "sp.h"
+#include "sp_head.h"
/*
- Fake table list object, pointer to which is used as special value for
- st_lex::time_zone_tables_used indicating that we implicitly use time
- zone tables in this statement but real table list was not yet created.
- Pointer to it is also returned by my_tz_get_tables_list() as indication
- of transient error;
+ We are using pointer to this variable for distinguishing between assignment
+ to NEW row field (when parsing trigger definition) and structured variable.
*/
-TABLE_LIST fake_time_zone_tables_list;
+sys_var_long_ptr trg_new_row_fake_var(0, 0);
/* Macros to look like lex */
@@ -42,10 +41,6 @@ TABLE_LIST fake_time_zone_tables_list;
#define yySkip() lex->ptr++
#define yyLength() ((uint) (lex->ptr - lex->tok_start)-1)
-#if MYSQL_VERSION_ID < 32300
-#define FLOAT_NUM REAL_NUM
-#endif
-
pthread_key(LEX*,THR_LEX);
/* Longest standard keyword name */
@@ -56,7 +51,8 @@ pthread_key(LEX*,THR_LEX);
used when comparing keywords
*/
-uchar to_upper_lex[] = {
+static uchar to_upper_lex[]=
+{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
@@ -117,14 +113,23 @@ void lex_free(void)
void lex_start(THD *thd, uchar *buf,uint length)
{
LEX *lex= thd->lex;
+ DBUG_ENTER("lex_start");
+
+ lex->thd= lex->unit.thd= thd;
+ lex->buf= lex->ptr= buf;
+ lex->end_of_query= buf+length;
+
+ lex->context_stack.empty();
lex->unit.init_query();
lex->unit.init_select();
- lex->thd= thd;
- lex->unit.thd= thd;
+ /* 'parent_lex' is used in init_query() so it must be before it. */
+ lex->select_lex.parent_lex= lex;
lex->select_lex.init_query();
lex->value_list.empty();
lex->update_list.empty();
lex->param_list.empty();
+ lex->view_list.empty();
+ lex->prepared_stmt_params.empty();
lex->unit.next= lex->unit.master=
lex->unit.link_next= lex->unit.return_to= 0;
lex->unit.prev= lex->unit.link_prev= 0;
@@ -135,15 +140,23 @@ void lex_start(THD *thd, uchar *buf,uint length)
lex->select_lex.link_next= lex->select_lex.slave= lex->select_lex.next= 0;
lex->select_lex.link_prev= (st_select_lex_node**)&(lex->all_selects_list);
lex->select_lex.options= 0;
+ lex->select_lex.init_order();
+ lex->select_lex.group_list.empty();
lex->describe= 0;
- lex->subqueries= lex->derived_tables= FALSE;
+ lex->subqueries= FALSE;
+ lex->view_prepare_mode= FALSE;
+ lex->stmt_prepare_mode= FALSE;
+ lex->derived_tables= 0;
lex->lock_option= TL_READ;
- lex->found_colon= 0;
+ lex->found_semicolon= 0;
lex->safe_to_cache_query= 1;
lex->time_zone_tables_used= 0;
+ lex->leaf_tables_insert= lex->query_tables= 0;
+ lex->query_tables_last= &lex->query_tables;
+ lex->variables_used= 0;
+ lex->empty_field_list_on_rset= 0;
lex->select_lex.select_number= 1;
lex->next_state=MY_LEX_START;
- lex->end_of_query=(lex->ptr=buf)+length;
lex->yylineno = 1;
lex->in_comment=0;
lex->length=0;
@@ -156,18 +169,28 @@ void lex_start(THD *thd, uchar *buf,uint length)
lex->current_select= &lex->select_lex;
lex->yacc_yyss=lex->yacc_yyvs=0;
lex->ignore_space=test(thd->variables.sql_mode & MODE_IGNORE_SPACE);
- lex->sql_command=SQLCOM_END;
+ lex->sql_command= lex->orig_sql_command= SQLCOM_END;
lex->duplicates= DUP_ERROR;
lex->ignore= 0;
+ lex->sphead= NULL;
+ lex->spcont= NULL;
lex->proc_list.first= 0;
+ lex->query_tables_own_last= 0;
+ lex->escape_used= FALSE;
+
+ if (lex->sroutines.records)
+ my_hash_reset(&lex->sroutines);
+ lex->sroutines_list.empty();
+ lex->sroutines_list_own_last= lex->sroutines_list.next;
+ lex->sroutines_list_own_elements= 0;
+ lex->nest_level=0 ;
+ lex->allow_sum_func= 0;
+ lex->in_sum_func= NULL;
+ DBUG_VOID_RETURN;
}
void lex_end(LEX *lex)
{
- for (SELECT_LEX *sl= lex->all_selects_list;
- sl;
- sl= sl->next_select_in_list())
- sl->expr_list.delete_elements(); // If error when parsing sql-varargs
x_free(lex->yacc_yyss);
x_free(lex->yacc_yyvs);
}
@@ -183,29 +206,16 @@ static int find_keyword(LEX *lex, uint len, bool function)
lex->yylval->symbol.symbol=symbol;
lex->yylval->symbol.str= (char*) tok;
lex->yylval->symbol.length=len;
+
+ if ((symbol->tok == NOT_SYM) &&
+ (lex->thd->variables.sql_mode & MODE_HIGH_NOT_PRECEDENCE))
+ return NOT2_SYM;
+ if ((symbol->tok == OR_OR_SYM) &&
+ !(lex->thd->variables.sql_mode & MODE_PIPES_AS_CONCAT))
+ return OR2_SYM;
+
return symbol->tok;
}
-#ifdef HAVE_DLOPEN
- udf_func *udf;
- if (function && using_udf_functions && (udf=find_udf((char*) tok, len)))
- {
- lex->safe_to_cache_query=0;
- lex->yylval->udf=udf;
- switch (udf->returns) {
- case STRING_RESULT:
- return (udf->type == UDFTYPE_FUNCTION) ? UDF_CHAR_FUNC : UDA_CHAR_SUM;
- case REAL_RESULT:
- return (udf->type == UDFTYPE_FUNCTION) ? UDF_FLOAT_FUNC : UDA_FLOAT_SUM;
- case INT_RESULT:
- return (udf->type == UDFTYPE_FUNCTION) ? UDF_INT_FUNC : UDA_INT_SUM;
- case ROW_RESULT:
- default:
- // This case should never be choosen
- DBUG_ASSERT(0);
- return 0;
- }
- }
-#endif
return 0;
}
@@ -214,7 +224,7 @@ static int find_keyword(LEX *lex, uint len, bool function)
SYNOPSIS
is_keyword()
- name checked name
+ name checked name (must not be empty)
len length of checked name
RETURN VALUES
@@ -224,6 +234,7 @@ static int find_keyword(LEX *lex, uint len, bool function)
bool is_keyword(const char *name, uint len)
{
+ DBUG_ASSERT(len != 0);
return get_hash_symbol(name,len,0)!=0;
}
@@ -290,7 +301,8 @@ static char *get_text(LEX *lex)
continue;
}
#endif
- if (c == '\\')
+ if (c == '\\' &&
+ !(lex->thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES))
{ // Escaped character
found_escape=1;
if (lex->ptr == lex->end_of_query)
@@ -351,7 +363,9 @@ static char *get_text(LEX *lex)
continue;
}
#endif
- if (!found_escape && *str == '\\' && str+1 != end)
+ if (!found_escape &&
+ !(lex->thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) &&
+ *str == '\\' && str+1 != end)
{
switch(*++str) {
case 'n':
@@ -455,12 +469,12 @@ static inline uint int_token(const char *str,uint length)
else if (length < signed_longlong_len)
return LONG_NUM;
else if (length > signed_longlong_len)
- return REAL_NUM;
+ return DECIMAL_NUM;
else
{
cmp=signed_longlong_str+1;
smaller=LONG_NUM; // If <= signed_longlong_str
- bigger=REAL_NUM;
+ bigger=DECIMAL_NUM;
}
}
else
@@ -476,10 +490,10 @@ static inline uint int_token(const char *str,uint length)
else if (length > longlong_len)
{
if (length > unsigned_longlong_len)
- return REAL_NUM;
+ return DECIMAL_NUM;
cmp=unsigned_longlong_str;
smaller=ULONGLONG_NUM;
- bigger=REAL_NUM;
+ bigger=DECIMAL_NUM;
}
else
{
@@ -493,14 +507,14 @@ static inline uint int_token(const char *str,uint length)
}
/*
- yylex remember the following states from the following yylex()
+ MYSQLlex remember the following states from the following MYSQLlex()
- MY_LEX_EOQ Found end of query
- MY_LEX_OPERATOR_OR_IDENT Last state was an ident, text or number
(which can't be followed by a signed number)
*/
-int yylex(void *arg, void *yythd)
+int MYSQLlex(void *arg, void *yythd)
{
reg1 uchar c;
int tokval, result_state;
@@ -513,6 +527,10 @@ int yylex(void *arg, void *yythd)
uchar *ident_map= cs->ident_map;
lex->yylval=yylval; // The global state
+
+ lex->tok_end_prev= lex->tok_end;
+ lex->tok_start_prev= lex->tok_start;
+
lex->tok_start=lex->tok_end=lex->ptr;
state=lex->next_state;
lex->next_state=MY_LEX_OPERATOR_OR_IDENT;
@@ -560,8 +578,7 @@ int yylex(void *arg, void *yythd)
its value in a query for the binlog, the query must stay
grammatically correct.
*/
- else if (c == '?' && ((THD*) yythd)->command == COM_PREPARE &&
- !ident_map[yyPeek()])
+ else if (c == '?' && lex->stmt_prepare_mode && !ident_map[yyPeek()])
return(PARAM_MARKER);
return((int) c);
@@ -591,8 +608,12 @@ int yylex(void *arg, void *yythd)
state= MY_LEX_HEX_NUMBER;
break;
}
- /* Fall through */
- case MY_LEX_IDENT_OR_BIN: // TODO: Add binary string handling
+ case MY_LEX_IDENT_OR_BIN:
+ if (yyPeek() == '\'')
+ { // Found b'bin-number'
+ state= MY_LEX_BIN_NUMBER;
+ break;
+ }
case MY_LEX_IDENT:
uchar *start;
#if defined(USE_MB) && defined(USE_MB_IDENT)
@@ -713,6 +734,20 @@ int yylex(void *arg, void *yythd)
}
yyUnget();
}
+ else if (c == 'b' && (lex->ptr - lex->tok_start) == 2 &&
+ lex->tok_start[0] == '0' )
+ { // b'bin-number'
+ while (my_isxdigit(cs,(c = yyGet()))) ;
+ if ((lex->ptr - lex->tok_start) >= 4 && !ident_map[c])
+ {
+ yylval->lex_str= get_token(lex, yyLength());
+ yylval->lex_str.str+= 2; // Skip 0x
+ yylval->lex_str.length-= 2;
+ lex->yytoklen-= 2;
+ return (BIN_NUM);
+ }
+ yyUnget();
+ }
// fall through
case MY_LEX_IDENT_START: // We come here after '.'
result_state= IDENT;
@@ -808,7 +843,7 @@ int yylex(void *arg, void *yythd)
return(FLOAT_NUM);
}
yylval->lex_str=get_token(lex,yyLength());
- return(REAL_NUM);
+ return(DECIMAL_NUM);
case MY_LEX_HEX_NUMBER: // Found x'hexstring'
yyGet(); // Skip '
@@ -825,6 +860,19 @@ int yylex(void *arg, void *yythd)
lex->yytoklen-=3;
return (HEX_NUM);
+ case MY_LEX_BIN_NUMBER: // Found b'bin-string'
+ yyGet(); // Skip '
+ while ((c= yyGet()) == '0' || c == '1');
+ length= (lex->ptr - lex->tok_start); // Length of bin-num + 3
+ if (c != '\'')
+ return(ABORT_SYM); // Illegal hex constant
+ yyGet(); // get_token makes an unget
+ yylval->lex_str= get_token(lex, length);
+ yylval->lex_str.str+= 2; // Skip b'
+ yylval->lex_str.length-= 3; // Don't count b' and last '
+ lex->yytoklen-= 3;
+ return (BIN_NUM);
+
case MY_LEX_CMP_OP: // Incomplete comparison operator
if (state_map[yyPeek()] == MY_LEX_CMP_OP ||
state_map[yyPeek()] == MY_LEX_LONG_CMP_OP)
@@ -942,16 +990,15 @@ int yylex(void *arg, void *yythd)
{
THD* thd= (THD*)yythd;
if ((thd->client_capabilities & CLIENT_MULTI_STATEMENTS) &&
- (thd->command != COM_PREPARE))
+ !lex->stmt_prepare_mode)
{
- lex->safe_to_cache_query=0;
- lex->found_colon=(char*)lex->ptr;
- thd->server_status |= SERVER_MORE_RESULTS_EXISTS;
- lex->next_state=MY_LEX_END;
- return(END_OF_INPUT);
+ lex->safe_to_cache_query= 0;
+ lex->found_semicolon=(char*) lex->ptr;
+ thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
+ lex->next_state= MY_LEX_END;
+ return (END_OF_INPUT);
}
- else
- state=MY_LEX_CHAR; // Return ';'
+ state= MY_LEX_CHAR; // Return ';'
break;
}
/* fall true */
@@ -1065,32 +1112,52 @@ void st_select_lex_unit::init_query()
cleaned= 0;
item_list.empty();
describe= 0;
+ found_rows_for_union= 0;
}
void st_select_lex::init_query()
{
st_select_lex_node::init_query();
table_list.empty();
+ top_join_list.empty();
+ join_list= &top_join_list;
+ embedding= leaf_tables= 0;
item_list.empty();
join= 0;
- where= 0;
+ having= prep_having= where= prep_where= 0;
olap= UNSPECIFIED_OLAP_TYPE;
having_fix_field= 0;
- resolve_mode= NOMATTER_MODE;
+ context.select_lex= this;
+ context.init();
+ /*
+ Add the name resolution context of the current (sub)query to the
+ stack of contexts for the whole query.
+ TODO:
+ push_context may return an error if there is no memory for a new
+ element in the stack, however this method has no return value,
+ thus push_context should be moved to a place where query
+ initialization is checked for failure.
+ */
+ parent_lex->push_context(&context);
cond_count= with_wild= 0;
+ conds_processed_with_permanent_arena= 0;
ref_pointer_array= 0;
select_n_having_items= 0;
- prep_where= 0;
subquery_in_having= explicit_limit= 0;
- parsing_place= NO_MATTER;
is_item_list_lookup= 0;
+ first_execution= 1;
+ first_cond_optimization= 1;
+ parsing_place= NO_MATTER;
+ exclude_from_table_unique_test= no_wrap_view_item= FALSE;
+ nest_level= 0;
+ link_next= 0;
}
void st_select_lex::init_select()
{
st_select_lex_node::init_select();
group_list.empty();
- type= db= db1= table1= db2= table2= 0;
+ type= db= 0;
having= 0;
use_index_ptr= ignore_index_ptr= 0;
table_join_options= 0;
@@ -1102,13 +1169,15 @@ void st_select_lex::init_select()
interval_list.empty();
use_index.empty();
ftfunc_list_alloc.empty();
+ inner_sum_func_list= 0;
ftfunc_list= &ftfunc_list_alloc;
linkage= UNSPECIFIED_TYPE;
order_list.elements= 0;
order_list.first= 0;
order_list.next= (byte**) &order_list.first;
- select_limit= HA_POS_ERROR;
- offset_limit= 0;
+ /* Set limit and offset to default values */
+ select_limit= 0; /* denotes the default limit = HA_POS_ERROR */
+ offset_limit= 0; /* denotes the default offset = 0 */
with_sum_func= 0;
}
@@ -1333,160 +1402,17 @@ ulong st_select_lex_node::get_table_join_options()
*/
bool st_select_lex::test_limit()
{
- if (select_limit != HA_POS_ERROR)
+ if (select_limit != 0)
{
my_error(ER_NOT_SUPPORTED_YET, MYF(0),
- "LIMIT & IN/ALL/ANY/SOME subquery");
+ "LIMIT & IN/ALL/ANY/SOME subquery");
return(1);
}
- // We need only 1 row to determinate existence
- select_limit= 1;
// no sense in ORDER BY without LIMIT
order_list.empty();
return(0);
}
-/*
- Interface method of table list creation for query
-
- SYNOPSIS
- st_select_lex_unit::create_total_list()
- thd THD pointer
- result pointer on result list of tables pointer
- check_derived force derived table chacking (used for creating
- table list for derived query)
- DESCRIPTION
- This is used for UNION & subselect 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.
-
- RETURN
- 0 - OK
- !0 - error
-*/
-bool st_select_lex_unit::create_total_list(THD *thd_arg, st_lex *lex,
- TABLE_LIST **result_arg)
-{
- *result_arg= 0;
- if (!(res= create_total_list_n_last_return(thd_arg, lex, &result_arg)))
- {
- /*
- If time zone tables were used implicitly in statement we should add
- them to global table list.
- */
- if (lex->time_zone_tables_used)
- {
- /*
- Altough we are modifying lex data, it won't raise any problem in
- case when this lex belongs to some prepared statement or stored
- procedure: such modification does not change any invariants imposed
- by requirement to reuse the same lex for multiple executions.
- */
- if ((lex->time_zone_tables_used= my_tz_get_table_list(thd)) !=
- &fake_time_zone_tables_list)
- {
- *result_arg= lex->time_zone_tables_used;
- }
- else
- {
- send_error(thd, 0);
- res= 1;
- }
- }
- }
- return res;
-}
-
-/*
- Table list creation for query
-
- SYNOPSIS
- st_select_lex_unit::create_total_list()
- thd THD pointer
- lex pointer on LEX stricture
- result pointer on pointer on result list of tables pointer
-
- DESCRIPTION
- This is used for UNION & subselect to create a new table list of all used
- tables.
- The table_list->table_list in all tables of global list are set to point
- to the local SELECT_LEX entries.
-
- RETURN
- 0 - OK
- !0 - error
-*/
-bool st_select_lex_unit::
-create_total_list_n_last_return(THD *thd_arg,
- st_lex *lex,
- TABLE_LIST ***result_arg)
-{
- TABLE_LIST *slave_list_first=0, **slave_list_last= &slave_list_first;
- TABLE_LIST **new_table_list= *result_arg, *aux;
- SELECT_LEX *sl= (SELECT_LEX*)slave;
-
- /*
- iterate all inner selects + fake_select (if exists),
- fake_select->next_select() always is 0
- */
- for (;
- sl;
- sl= (sl->next_select() ?
- sl->next_select() :
- (sl == fake_select_lex ?
- 0 :
- fake_select_lex)))
- {
- // check usage of ORDER BY in union
- if (sl->order_list.first && sl->next_select() && !sl->braces &&
- sl->linkage != GLOBAL_OPTIONS_TYPE)
- {
- net_printf(thd_arg,ER_WRONG_USAGE,"UNION","ORDER BY");
- return 1;
- }
-
- for (SELECT_LEX_UNIT *inner= sl->first_inner_unit();
- inner;
- inner= inner->next_unit())
- {
- if (inner->create_total_list_n_last_return(thd, lex,
- &slave_list_last))
- return 1;
- }
-
- if ((aux= (TABLE_LIST*) sl->table_list.first))
- {
- TABLE_LIST *next_table;
- for (; aux; aux= next_table)
- {
- TABLE_LIST *cursor;
- next_table= aux->next;
- /* Add to the total table list */
- if (!(cursor= (TABLE_LIST *) thd->memdup((char*) aux,
- sizeof(*aux))))
- {
- send_error(thd,0);
- return 1;
- }
- *new_table_list= cursor;
- cursor->table_list= aux;
- new_table_list= &cursor->next;
- *new_table_list= 0; // end result list
- aux->table_list= cursor;
- }
- }
- }
-
- if (slave_list_first)
- {
- *new_table_list= slave_list_first;
- new_table_list= slave_list_last;
- }
- *result_arg= new_table_list;
- return 0;
-}
-
st_select_lex_unit* st_select_lex_unit::master_unit()
{
@@ -1508,7 +1434,9 @@ bool st_select_lex::add_order_to_list(THD *thd, Item *item, bool asc)
bool st_select_lex::add_item_to_list(THD *thd, Item *item)
{
- return item_list.push_back(item);
+ DBUG_ENTER("st_select_lex::add_item_to_list");
+ DBUG_PRINT("info", ("Item: %p", item));
+ DBUG_RETURN(item_list.push_back(item));
}
@@ -1594,95 +1522,27 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num)
We have to create array in prepared statement memory if it is
prepared statement
*/
- Item_arena *arena= thd->current_arena;
- return (ref_pointer_array=
- (Item **)arena->alloc(sizeof(Item*) *
- (item_list.elements +
- select_n_having_items +
- order_group_num)* 5)) == 0;
-}
-
-
-/*
- Find db.table which will be updated in this unit
-
- SYNOPSIS
- st_select_lex_unit::check_updateable()
- db - data base name
- table - real table name
-
- RETURN
- 1 - found
- 0 - OK (table did not found)
-*/
-
-bool st_select_lex_unit::check_updateable(char *db, char *table)
-{
- for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
- if (sl->check_updateable(db, table))
- return 1;
- return 0;
-}
-
-
-/*
- Find db.table which will be updated in this select and
- underlying ones (except derived tables)
-
- SYNOPSIS
- st_select_lex::check_updateable()
- db - data base name
- table - real table name
-
- RETURN
- 1 - found
- 0 - OK (table did not found)
-*/
-
-bool st_select_lex::check_updateable(char *db, char *table)
-{
- if (find_real_table_in_list(get_table_list(), db, table))
- return 1;
-
- return check_updateable_in_subqueries(db, table);
-}
-
-/*
- Find db.table which will be updated in underlying subqueries
-
- SYNOPSIS
- st_select_lex::check_updateable_in_subqueries()
- db - data base name
- table - real table name
-
- RETURN
- 1 - found
- 0 - OK (table did not found)
-*/
-
-bool st_select_lex::check_updateable_in_subqueries(char *db, char *table)
-{
- for (SELECT_LEX_UNIT *un= first_inner_unit();
- un;
- un= un->next_unit())
- {
- if (un->first_select()->linkage != DERIVED_TABLE_TYPE &&
- un->check_updateable(db, table))
- return 1;
- }
- return 0;
+ Query_arena *arena= thd->stmt_arena;
+ return (ref_pointer_array=
+ (Item **)arena->alloc(sizeof(Item*) *
+ (item_list.elements +
+ select_n_having_items +
+ order_group_num)* 5)) == 0;
}
void st_select_lex_unit::print(String *str)
{
+ bool union_all= !union_distinct;
for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
{
if (sl != first_select())
{
- str->append(" union ", 7);
- if (!union_distinct)
- str->append("all ", 4);
+ str->append(STRING_WITH_LEN(" union "));
+ if (union_all)
+ str->append(STRING_WITH_LEN("all "));
+ else if (union_distinct == sl)
+ union_all= TRUE;
}
if (sl->braces)
str->append('(');
@@ -1694,7 +1554,7 @@ void st_select_lex_unit::print(String *str)
{
if (fake_select_lex->order_list.elements)
{
- str->append(" order by ", 10);
+ str->append(STRING_WITH_LEN(" order by "));
fake_select_lex->print_order(str,
(ORDER *) fake_select_lex->
order_list.first);
@@ -1708,9 +1568,16 @@ void st_select_lex::print_order(String *str, ORDER *order)
{
for (; order; order= order->next)
{
- (*order->item)->print(str);
+ if (order->counter_used)
+ {
+ char buffer[20];
+ uint length= my_snprintf(buffer, 20, "%d", order->counter);
+ str->append(buffer, length);
+ }
+ else
+ (*order->item)->print(str);
if (!order->asc)
- str->append(" desc", 5);
+ str->append(STRING_WITH_LEN(" desc"));
if (order->next)
str->append(',');
}
@@ -1719,96 +1586,530 @@ void st_select_lex::print_order(String *str, ORDER *order)
void st_select_lex::print_limit(THD *thd, String *str)
{
+ SELECT_LEX_UNIT *unit= master_unit();
+ Item_subselect *item= unit->item;
+ if (item && unit->global_parameters == this &&
+ (item->substype() == Item_subselect::EXISTS_SUBS ||
+ item->substype() == Item_subselect::IN_SUBS ||
+ item->substype() == Item_subselect::ALL_SUBS))
+ {
+ DBUG_ASSERT(!item->fixed ||
+ select_limit->val_int() == LL(1) && offset_limit == 0);
+ return;
+ }
+
if (explicit_limit)
{
- str->append(" limit ", 7);
- char buff[20];
- // latin1 is good enough for numbers
- String st(buff, sizeof(buff), &my_charset_latin1);
- st.set((ulonglong)select_limit, &my_charset_latin1);
- str->append(st);
+ str->append(STRING_WITH_LEN(" limit "));
if (offset_limit)
{
+ offset_limit->print(str);
str->append(',');
- st.set((ulonglong)select_limit, &my_charset_latin1);
- str->append(st);
}
+ select_limit->print(str);
}
}
+/*
+ Initialize LEX object.
+
+ SYNOPSIS
+ st_lex::st_lex()
+
+ NOTE
+ LEX object initialized with this constructor can be used as part of
+ THD object for which one can safely call open_tables(), lock_tables()
+ and close_thread_tables() functions. But it is not yet ready for
+ statement parsing. On should use lex_start() function to prepare LEX
+ for this.
+*/
+
st_lex::st_lex()
- :result(0)
-{}
+ :result(0), sql_command(SQLCOM_END), query_tables_own_last(0)
+{
+ hash_init(&sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key, 0, 0);
+ sroutines_list.empty();
+ sroutines_list_own_last= sroutines_list.next;
+ sroutines_list_own_elements= 0;
+}
/*
- Unlink first table from global table list and first table from outer select
- list (lex->select_lex)
+ Check whether the merging algorithm can be used on this VIEW
SYNOPSIS
- unlink_first_table()
- tables Global table list
- global_first Save first global table here
- local_first Save first local table here
+ st_lex::can_be_merged()
- NOTES
- This function assumes that outer select list is non-empty.
+ DESCRIPTION
+ We can apply merge algorithm if it is single SELECT view with
+ subqueries only in WHERE clause (we do not count SELECTs of underlying
+ views, and second level subqueries) and we have not grpouping, ordering,
+ HAVING clause, aggregate functions, DISTINCT clause, LIMIT clause and
+ several underlying tables.
+
+ RETURN
+ FALSE - only temporary table algorithm can be used
+ TRUE - merge algorithm can be used
+*/
+
+bool st_lex::can_be_merged()
+{
+ // TODO: do not forget implement case when select_lex.table_list.elements==0
+
+ /* find non VIEW subqueries/unions */
+ bool selects_allow_merge= select_lex.next_select() == 0;
+ if (selects_allow_merge)
+ {
+ for (SELECT_LEX_UNIT *unit= select_lex.first_inner_unit();
+ unit;
+ unit= unit->next_unit())
+ {
+ if (unit->first_select()->parent_lex == this &&
+ (unit->item == 0 || unit->item->place() != IN_WHERE))
+ {
+ selects_allow_merge= 0;
+ break;
+ }
+ }
+ }
+
+ return (selects_allow_merge &&
+ select_lex.order_list.elements == 0 &&
+ select_lex.group_list.elements == 0 &&
+ select_lex.having == 0 &&
+ select_lex.with_sum_func == 0 &&
+ select_lex.table_list.elements >= 1 &&
+ !(select_lex.options & SELECT_DISTINCT) &&
+ select_lex.select_limit == 0);
+}
+
+
+/*
+ check if command can use VIEW with MERGE algorithm (for top VIEWs)
+
+ SYNOPSIS
+ st_lex::can_use_merged()
+
+ DESCRIPTION
+ Only listed here commands can use merge algorithm in top level
+ SELECT_LEX (for subqueries will be used merge algorithm if
+ st_lex::can_not_use_merged() is not TRUE).
RETURN
- global list without first table
+ FALSE - command can't use merged VIEWs
+ TRUE - VIEWs with MERGE algorithms can be used
+*/
+
+bool st_lex::can_use_merged()
+{
+ switch (sql_command)
+ {
+ case SQLCOM_SELECT:
+ case SQLCOM_CREATE_TABLE:
+ case SQLCOM_UPDATE:
+ case SQLCOM_UPDATE_MULTI:
+ case SQLCOM_DELETE:
+ case SQLCOM_DELETE_MULTI:
+ case SQLCOM_INSERT:
+ case SQLCOM_INSERT_SELECT:
+ case SQLCOM_REPLACE:
+ case SQLCOM_REPLACE_SELECT:
+ case SQLCOM_LOAD:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+/*
+ Check if command can't use merged views in any part of command
+
+ SYNOPSIS
+ st_lex::can_not_use_merged()
+
+ DESCRIPTION
+ Temporary table algorithm will be used on all SELECT levels for queries
+ listed here (see also st_lex::can_use_merged()).
+
+ RETURN
+ FALSE - command can't use merged VIEWs
+ TRUE - VIEWs with MERGE algorithms can be used
*/
-TABLE_LIST *st_lex::unlink_first_table(TABLE_LIST *tables,
- TABLE_LIST **global_first,
- TABLE_LIST **local_first)
+
+bool st_lex::can_not_use_merged()
{
- DBUG_ASSERT(select_lex.table_list.first != 0);
+ switch (sql_command)
+ {
+ case SQLCOM_CREATE_VIEW:
+ case SQLCOM_SHOW_CREATE:
/*
- Save pointers to first elements of global table list and list
- of tables used in outer select. It does not harm if these lists
- are the same.
+ SQLCOM_SHOW_FIELDS is necessary to make
+ information schema tables working correctly with views.
+ see get_schema_tables_result function
*/
- *global_first= tables;
- *local_first= (TABLE_LIST*)select_lex.table_list.first;
+ case SQLCOM_SHOW_FIELDS:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+/*
+ Detect that we need only table structure of derived table/view
+
+ SYNOPSIS
+ only_view_structure()
+
+ RETURN
+ TRUE yes, we need only structure
+ FALSE no, we need data
+*/
- /* Exclude first elements from these lists */
- select_lex.table_list.first= (byte*) (*local_first)->next;
- tables= tables->next;
- (*global_first)->next= 0;
- return tables;
+bool st_lex::only_view_structure()
+{
+ switch (sql_command) {
+ case SQLCOM_SHOW_CREATE:
+ case SQLCOM_SHOW_TABLES:
+ case SQLCOM_SHOW_FIELDS:
+ case SQLCOM_REVOKE_ALL:
+ case SQLCOM_REVOKE:
+ case SQLCOM_GRANT:
+ case SQLCOM_CREATE_VIEW:
+ return TRUE;
+ default:
+ return FALSE;
+ }
}
/*
- Link table which was unlinked with unlink_first_table() back.
+ Should Items_ident be printed correctly
+
+ SYNOPSIS
+ need_correct_ident()
+
+ RETURN
+ TRUE yes, we need only structure
+ FALSE no, we need data
+*/
+
+
+bool st_lex::need_correct_ident()
+{
+ switch(sql_command)
+ {
+ case SQLCOM_SHOW_CREATE:
+ case SQLCOM_SHOW_TABLES:
+ case SQLCOM_CREATE_VIEW:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+/*
+ Get effective type of CHECK OPTION for given view
+
+ SYNOPSIS
+ get_effective_with_check()
+ view given view
+
+ NOTE
+ It have not sense to set CHECK OPTION for SELECT satement or subqueries,
+ so we do not.
+
+ RETURN
+ VIEW_CHECK_NONE no need CHECK OPTION
+ VIEW_CHECK_LOCAL CHECK OPTION LOCAL
+ VIEW_CHECK_CASCADED CHECK OPTION CASCADED
+*/
+
+uint8 st_lex::get_effective_with_check(st_table_list *view)
+{
+ if (view->select_lex->master_unit() == &unit &&
+ which_check_option_applicable())
+ return (uint8)view->with_check;
+ return VIEW_CHECK_NONE;
+}
+
+
+/*
+ initialize limit counters
+
+ SYNOPSIS
+ st_select_lex_unit::set_limit()
+ values - SELECT_LEX with initial values for counters
+*/
+
+void st_select_lex_unit::set_limit(SELECT_LEX *sl)
+{
+ ha_rows select_limit_val;
+
+ DBUG_ASSERT(! thd->stmt_arena->is_stmt_prepare());
+ select_limit_val= (ha_rows)(sl->select_limit ? sl->select_limit->val_uint() :
+ HA_POS_ERROR);
+ offset_limit_cnt= (ha_rows)(sl->offset_limit ? sl->offset_limit->val_uint() :
+ ULL(0));
+ select_limit_cnt= select_limit_val + offset_limit_cnt;
+ if (select_limit_cnt < select_limit_val)
+ select_limit_cnt= HA_POS_ERROR; // no limit
+}
+
+
+/*
+ Unlink the first table from the global table list and the first table from
+ outer select (lex->select_lex) local list
+
+ SYNOPSIS
+ unlink_first_table()
+ link_to_local Set to 1 if caller should link this table to local list
+
+ NOTES
+ We assume that first tables in both lists is the same table or the local
+ list is empty.
+
+ RETURN
+ 0 If 'query_tables' == 0
+ unlinked table
+ In this case link_to_local is set.
+
+*/
+TABLE_LIST *st_lex::unlink_first_table(bool *link_to_local)
+{
+ TABLE_LIST *first;
+ if ((first= query_tables))
+ {
+ /*
+ Exclude from global table list
+ */
+ if ((query_tables= query_tables->next_global))
+ query_tables->prev_global= &query_tables;
+ else
+ query_tables_last= &query_tables;
+ first->next_global= 0;
+
+ /*
+ and from local list if it is not empty
+ */
+ if ((*link_to_local= test(select_lex.table_list.first)))
+ {
+ select_lex.context.table_list=
+ select_lex.context.first_name_resolution_table= first->next_local;
+ select_lex.table_list.first= (byte*) (first->next_local);
+ select_lex.table_list.elements--; //safety
+ first->next_local= 0;
+ /*
+ Ensure that the global list has the same first table as the local
+ list.
+ */
+ first_lists_tables_same();
+ }
+ }
+ return first;
+}
+
+
+/*
+ Bring first local table of first most outer select to first place in global
+ table list
+
+ SYNOPSYS
+ st_lex::first_lists_tables_same()
+
+ NOTES
+ In many cases (for example, usual INSERT/DELETE/...) the first table of
+ main SELECT_LEX have special meaning => check that it is the first table
+ in global list and re-link to be first in the global list if it is
+ necessary. We need such re-linking only for queries with sub-queries in
+ the select list, as only in this case tables of sub-queries will go to
+ the global list first.
+*/
+
+void st_lex::first_lists_tables_same()
+{
+ TABLE_LIST *first_table= (TABLE_LIST*) select_lex.table_list.first;
+ if (query_tables != first_table && first_table != 0)
+ {
+ TABLE_LIST *next;
+ if (query_tables_last == &first_table->next_global)
+ query_tables_last= first_table->prev_global;
+
+ if ((next= *first_table->prev_global= first_table->next_global))
+ next->prev_global= first_table->prev_global;
+ /* include in new place */
+ first_table->next_global= query_tables;
+ /*
+ We are sure that query_tables is not 0, because first_table was not
+ first table in the global list => we can use
+ query_tables->prev_global without check of query_tables
+ */
+ query_tables->prev_global= &first_table->next_global;
+ first_table->prev_global= &query_tables;
+ query_tables= first_table;
+ }
+}
+
+
+/*
+ Add implicitly used time zone description tables to global table list
+ (if needed).
+
+ SYNOPSYS
+ st_lex::add_time_zone_tables_to_query_tables()
+ thd - pointer to current thread context
+
+ RETURN VALUE
+ TRUE - error
+ FALSE - success
+*/
+
+bool st_lex::add_time_zone_tables_to_query_tables(THD *thd)
+{
+ /* We should not add these tables twice */
+ if (!time_zone_tables_used)
+ {
+ time_zone_tables_used= my_tz_get_table_list(thd, &query_tables_last);
+ if (time_zone_tables_used == &fake_time_zone_tables_list)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ Link table back that was unlinked with unlink_first_table()
SYNOPSIS
link_first_table_back()
- tables Global table list
- global_first Saved first global table
- local_first Saved first local table
+ link_to_local do we need link this table to local
RETURN
global list
*/
-TABLE_LIST *st_lex::link_first_table_back(TABLE_LIST *tables,
- TABLE_LIST *global_first,
- TABLE_LIST *local_first)
+
+void st_lex::link_first_table_back(TABLE_LIST *first,
+ bool link_to_local)
+{
+ if (first)
+ {
+ if ((first->next_global= query_tables))
+ query_tables->prev_global= &first->next_global;
+ else
+ query_tables_last= &first->next_global;
+ query_tables= first;
+
+ if (link_to_local)
+ {
+ first->next_local= (TABLE_LIST*) select_lex.table_list.first;
+ select_lex.context.table_list= first;
+ select_lex.table_list.first= (byte*) first;
+ select_lex.table_list.elements++; //safety
+ }
+ }
+}
+
+
+
+/*
+ cleanup lex for case when we open table by table for processing
+
+ SYNOPSIS
+ st_lex::cleanup_after_one_table_open()
+*/
+
+void st_lex::cleanup_after_one_table_open()
+{
+ /*
+ thd->lex->derived_tables & additional units may be set if we open
+ a view. It is necessary to clear thd->lex->derived_tables flag
+ to prevent processing of derived tables during next open_and_lock_tables
+ if next table is a real table and cleanup & remove underlying units
+ NOTE: all units will be connected to thd->lex->select_lex, because we
+ have not UNION on most upper level.
+ */
+ if (all_selects_list != &select_lex)
+ {
+ derived_tables= 0;
+ /* cleunup underlying units (units of VIEW) */
+ for (SELECT_LEX_UNIT *un= select_lex.first_inner_unit();
+ un;
+ un= un->next_unit())
+ un->cleanup();
+ /* reduce all selects list to default state */
+ all_selects_list= &select_lex;
+ /* remove underlying units (units of VIEW) subtree */
+ select_lex.cut_subtree();
+ }
+ time_zone_tables_used= 0;
+ if (sroutines.records)
+ my_hash_reset(&sroutines);
+ sroutines_list.empty();
+ sroutines_list_own_last= sroutines_list.next;
+ sroutines_list_own_elements= 0;
+}
+
+
+/*
+ Do end-of-prepare fixup for list of tables and their merge-VIEWed tables
+
+ SYNOPSIS
+ fix_prepare_info_in_table_list()
+ thd Thread handle
+ tbl List of tables to process
+
+ DESCRIPTION
+ Perform end-end-of prepare fixup for list of tables, if any of the tables
+ is a merge-algorithm VIEW, recursively fix up its underlying tables as
+ well.
+
+*/
+
+static void fix_prepare_info_in_table_list(THD *thd, TABLE_LIST *tbl)
+{
+ for (; tbl; tbl= tbl->next_local)
+ {
+ if (tbl->on_expr)
+ {
+ tbl->prep_on_expr= tbl->on_expr;
+ tbl->on_expr= tbl->on_expr->copy_andor_structure(thd);
+ }
+ fix_prepare_info_in_table_list(thd, tbl->merge_underlying_list);
+ }
+}
+
+
+/*
+ fix some structures at the end of preparation
+
+ SYNOPSIS
+ st_select_lex::fix_prepare_information
+ thd thread handler
+ conds pointer on conditions which will be used for execution statement
+*/
+
+void st_select_lex::fix_prepare_information(THD *thd, Item **conds)
{
- global_first->next= tables;
- select_lex.table_list.first= (byte*) local_first;
- return global_first;
+ if (!thd->stmt_arena->is_conventional() && first_execution)
+ {
+ first_execution= 0;
+ if (*conds)
+ {
+ prep_where= *conds;
+ *conds= where= prep_where->copy_andor_structure(thd);
+ }
+ fix_prepare_info_in_table_list(thd, (TABLE_LIST *)table_list.first);
+ }
}
/*
- There are st_select_lex::add_table_to_list &
+ There are st_select_lex::add_table_to_list &
st_select_lex::set_lock_for_tables are in sql_parse.cc
- st_select_lex::print is in sql_select.h
+ st_select_lex::print is in sql_select.cc
st_select_lex_unit::prepare, st_select_lex_unit::exec,
st_select_lex_unit::cleanup, st_select_lex_unit::reinit_exec_mechanism,
st_select_lex_unit::change_result
are in sql_union.cc
*/
+