diff options
-rw-r--r-- | mysql-test/r/sp.result | 7 | ||||
-rw-r--r-- | mysql-test/t/sp.test | 11 | ||||
-rw-r--r-- | sql/item.cc | 2 | ||||
-rw-r--r-- | sql/item_func.cc | 2 | ||||
-rw-r--r-- | sql/log_event.cc | 9 | ||||
-rw-r--r-- | sql/mysql_priv.h | 6 | ||||
-rw-r--r-- | sql/slave.cc | 3 | ||||
-rw-r--r-- | sql/sp.cc | 9 | ||||
-rw-r--r-- | sql/sp_head.cc | 31 | ||||
-rw-r--r-- | sql/sp_head.h | 6 | ||||
-rw-r--r-- | sql/sql_class.cc | 2 | ||||
-rw-r--r-- | sql/sql_class.h | 10 | ||||
-rw-r--r-- | sql/sql_lex.cc | 323 | ||||
-rw-r--r-- | sql/sql_lex.h | 75 | ||||
-rw-r--r-- | sql/sql_parse.cc | 104 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 8 | ||||
-rw-r--r-- | sql/sql_trigger.cc | 8 | ||||
-rw-r--r-- | sql/sql_view.cc | 14 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 185 |
19 files changed, 498 insertions, 317 deletions
diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 9ba6a356db2..b5b79af031e 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -6118,6 +6118,13 @@ Warning 1265 Data truncated for column 'bug5274_f1' at row 1 Warning 1265 Data truncated for column 'bug5274_f1' at row 1 DROP FUNCTION bug5274_f1| DROP FUNCTION bug5274_f2| +drop procedure if exists proc_21513| +create procedure proc_21513()`my_label`:BEGIN END| +show create procedure proc_21513| +Procedure sql_mode Create Procedure +proc_21513 CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_21513`() +`my_label`:BEGIN END +drop procedure proc_21513| End of 5.0 tests. drop table t1,t2; CREATE TABLE t1 (a int auto_increment primary key) engine=MyISAM; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index c94a526e10c..ff203a85ef7 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -7054,6 +7054,17 @@ SELECT bug5274_f2()| DROP FUNCTION bug5274_f1| DROP FUNCTION bug5274_f2| +# +# Bug#21513 (SP having body starting with quoted label rendered unusable) +# +--disable_warnings +drop procedure if exists proc_21513| +--enable_warnings + +create procedure proc_21513()`my_label`:BEGIN END| +show create procedure proc_21513| + +drop procedure proc_21513| ### --echo End of 5.0 tests. diff --git a/sql/item.cc b/sql/item.cc index 28729e9936f..4a2a38caf51 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -4621,7 +4621,6 @@ inline uint char_val(char X) Item_hex_string::Item_hex_string(const char *str, uint str_length) { - name=(char*) str-2; // Lex makes this start with 0x max_length=(str_length+1)/2; char *ptr=(char*) sql_alloc(max_length+1); if (!ptr) @@ -4732,7 +4731,6 @@ Item_bin_string::Item_bin_string(const char *str, uint str_length) uchar bits= 0; uint power= 1; - name= (char*) str - 2; max_length= (str_length + 7) >> 3; char *ptr= (char*) sql_alloc(max_length + 1); if (!ptr) diff --git a/sql/item_func.cc b/sql/item_func.cc index 14a4c4dcf4b..4028127db77 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -4315,7 +4315,7 @@ int get_var_with_binlog(THD *thd, enum_sql_command sql_command, List<set_var_base> tmp_var_list; LEX *sav_lex= thd->lex, lex_tmp; thd->lex= &lex_tmp; - lex_start(thd, NULL, 0); + lex_start(thd); tmp_var_list.push_back(new set_var_user(new Item_func_set_user_var(name, new Item_null()))); /* Create the variable */ diff --git a/sql/log_event.cc b/sql/log_event.cc index 8bb63e72bde..173ca6232ee 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1879,7 +1879,8 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli, thd->variables.collation_database= thd->db_charset; /* Execute the query (note that we bypass dispatch_command()) */ - mysql_parse(thd, thd->query, thd->query_length); + const char* found_semicolon= NULL; + mysql_parse(thd, thd->query, thd->query_length, &found_semicolon); } else @@ -2987,10 +2988,12 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, thd->query_error= 0; clear_all_errors(thd, rli); /* - Usually mysql_init_query() is called by mysql_parse(), but we need it here + Usually lex_start() is called by mysql_parse(), but we need it here as the present method does not call mysql_parse(). */ - mysql_init_query(thd, 0, 0); + lex_start(thd); + mysql_reset_thd_for_next_command(thd); + if (!use_rli_only_for_errors) { /* Saved for InnoDB, see comment in Query_log_event::exec_event() */ diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 7ab683134e0..868f3584448 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -694,13 +694,15 @@ bool do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, bool skip_error); bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch); -void mysql_parse(THD *thd,char *inBuf,uint length); + +void mysql_parse(THD *thd, const char *inBuf, uint length, + const char ** semicolon); + bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length); bool is_update_query(enum enum_sql_command command); bool alloc_query(THD *thd, const char *packet, uint packet_length); void mysql_init_select(LEX *lex); void mysql_reset_thd_for_next_command(THD *thd); -void mysql_init_query(THD *thd, uchar *buf, uint length); bool mysql_new_select(LEX *lex, bool move_down); void create_select_for_variable(const char *var_name); void mysql_init_multi_delete(LEX *lex); diff --git a/sql/slave.cc b/sql/slave.cc index 97615165803..84f409d7f34 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1517,6 +1517,7 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, handler *file; ulonglong save_options; NET *net= &mysql->net; + const char *found_semicolon= NULL; DBUG_ENTER("create_table_from_dump"); packet_len= my_net_read(net); // read create table statement @@ -1567,7 +1568,7 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, save_db_length= thd->db_length; DBUG_ASSERT(db != 0); thd->reset_db((char*)db, strlen(db)); - mysql_parse(thd, thd->query, packet_len); // run create table + mysql_parse(thd, thd->query, packet_len, &found_semicolon); // run create table thd->db = save_db; // leave things the way the were before thd->db_length= save_db_length; thd->options = save_options; diff --git a/sql/sp.cc b/sql/sp.cc index 4fc9ea1f602..976bb1911b2 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -434,10 +434,15 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, if ((ret= sp_use_new_db(thd, name->m_db, &old_db, 1, &dbchanged))) goto end; - lex_start(thd, (uchar*)defstr.c_ptr(), defstr.length()); + { + Lex_input_stream lip(thd, defstr.c_ptr(), defstr.length()); + thd->m_lip= &lip; + lex_start(thd); + ret= MYSQLparse(thd); + } thd->spcont= 0; - if (MYSQLparse(thd) || thd->is_fatal_error || newlex.sphead == NULL) + if (ret || thd->is_fatal_error || newlex.sphead == NULL) { sp_head *sp= newlex.sphead; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 338b2eb84bb..1ebef4f8bee 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -519,9 +519,10 @@ void sp_head::init_strings(THD *thd, LEX *lex) { DBUG_ENTER("sp_head::init_strings"); - uchar *endp; /* Used to trim the end */ + const char *endp; /* Used to trim the end */ /* During parsing, we must use thd->mem_root */ MEM_ROOT *root= thd->mem_root; + Lex_input_stream *lip=thd->m_lip; if (m_param_begin && m_param_end) { @@ -531,17 +532,17 @@ sp_head::init_strings(THD *thd, LEX *lex) } /* If ptr has overrun end_of_query then end_of_query is the end */ - endp= (lex->ptr > lex->end_of_query ? lex->end_of_query : lex->ptr); + endp= (lip->ptr > lip->end_of_query ? lip->end_of_query : lip->ptr); /* Trim "garbage" at the end. This is sometimes needed with the "/ * ! VERSION... * /" wrapper in dump files. */ - endp= skip_rear_comments(m_body_begin, endp); + endp= skip_rear_comments((char*) m_body_begin, (char*) endp); m_body.length= endp - m_body_begin; - m_body.str= strmake_root(root, (char *)m_body_begin, m_body.length); - m_defstr.length= endp - lex->buf; - m_defstr.str= strmake_root(root, (char *)lex->buf, m_defstr.length); + m_body.str= strmake_root(root, m_body_begin, m_body.length); + m_defstr.length= endp - lip->buf; + m_defstr.str= strmake_root(root, lip->buf, m_defstr.length); DBUG_VOID_RETURN; } @@ -1756,24 +1757,13 @@ sp_head::reset_lex(THD *thd) DBUG_ENTER("sp_head::reset_lex"); LEX *sublex; LEX *oldlex= thd->lex; - my_lex_states org_next_state= oldlex->next_state; (void)m_lex.push_front(oldlex); thd->lex= sublex= new st_lex; - /* Reset most stuff. The length arguments doesn't matter here. */ - lex_start(thd, oldlex->buf, (ulong) (oldlex->end_of_query - oldlex->ptr)); + /* Reset most stuff. */ + lex_start(thd); - /* - next_state is normally the same (0), but it happens that we swap lex in - "mid-sentence", so we must restore it. - */ - sublex->next_state= org_next_state; - /* We must reset ptr and end_of_query again */ - sublex->ptr= oldlex->ptr; - sublex->end_of_query= oldlex->end_of_query; - sublex->tok_start= oldlex->tok_start; - sublex->yylineno= oldlex->yylineno; /* And keep the SP stuff too */ sublex->sphead= oldlex->sphead; sublex->spcont= oldlex->spcont; @@ -1806,9 +1796,6 @@ sp_head::restore_lex(THD *thd) if (! oldlex) return; // Nothing to restore - // Update some state in the old one first - oldlex->ptr= sublex->ptr; - oldlex->next_state= sublex->next_state; oldlex->trg_table_fields.push_back(&sublex->trg_table_fields); /* diff --git a/sql/sp_head.h b/sql/sp_head.h index cce400d6a14..4632f6808fd 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -125,7 +125,7 @@ public: create_field m_return_field_def; /* This is used for FUNCTIONs only. */ - uchar *m_tmp_query; // Temporary pointer to sub query string + const char *m_tmp_query; // Temporary pointer to sub query string uint m_old_cmq; // Old CLIENT_MULTI_QUERIES value st_sp_chistics *m_chistics; ulong m_sql_mode; // For SHOW CREATE and execution @@ -174,7 +174,9 @@ public: */ HASH m_sroutines; // Pointers set during parsing - uchar *m_param_begin, *m_param_end, *m_body_begin; + const char *m_param_begin; + const char *m_param_end; + const char *m_body_begin; /* Security context for stored routine which should be run under diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 6ec14426162..010dc101e0d 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -176,7 +176,7 @@ THD::THD() rand_used(0), time_zone_used(0), last_insert_id_used(0), last_insert_id_used_bin_log(0), insert_id_used(0), clear_next_insert_id(0), in_lock_tables(0), bootstrap(0), - derived_tables_processing(FALSE), spcont(NULL) + derived_tables_processing(FALSE), spcont(NULL), m_lip(NULL) { ulong tmp; diff --git a/sql/sql_class.h b/sql/sql_class.h index 12d7cb2368f..7f195d5a048 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -28,6 +28,7 @@ class Slave_log_event; class Format_description_log_event; class sp_rcontext; class sp_cache; +class Lex_input_stream; enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE }; enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME }; @@ -1496,6 +1497,15 @@ public: query_id_t first_query_id; } binlog_evt_union; + /** + Character input stream consumed by the lexical analyser, + used during parsing. + Note that since the parser is not re-entrant, we keep only one input + stream here. This member is valid only when executing code during parsing, + and may point to invalid memory after that. + */ + Lex_input_stream *m_lip; + THD(); ~THD(); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 3be844b6761..6a44a883b51 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -32,13 +32,13 @@ sys_var_long_ptr trg_new_row_fake_var(0, 0); /* Macros to look like lex */ -#define yyGet() *(lex->ptr++) -#define yyGetLast() lex->ptr[-1] -#define yyPeek() lex->ptr[0] -#define yyPeek2() lex->ptr[1] -#define yyUnget() lex->ptr-- -#define yySkip() lex->ptr++ -#define yyLength() ((uint) (lex->ptr - lex->tok_start)-1) +#define yyGet() *(lip->ptr++) +#define yyGetLast() lip->ptr[-1] +#define yyPeek() lip->ptr[0] +#define yyPeek2() lip->ptr[1] +#define yyUnget() lip->ptr-- +#define yySkip() lip->ptr++ +#define yyLength() ((uint) (lip->ptr - lip->tok_start)-1) /* Longest standard keyword name */ #define TOCK_NAME_LENGTH 24 @@ -108,6 +108,28 @@ st_parsing_options::reset() allows_derived= TRUE; } +Lex_input_stream::Lex_input_stream(THD *thd, + const char* buffer, + unsigned int length) +: m_thd(thd), + yylineno(1), + yytoklen(0), + yylval(NULL), + ptr(buffer), + tok_start(NULL), + tok_end(NULL), + end_of_query(buffer + length), + tok_start_prev(NULL), + buf(buffer), + next_state(MY_LEX_START), + found_semicolon(NULL), + ignore_space(test(thd->variables.sql_mode & MODE_IGNORE_SPACE)) +{ +} + +Lex_input_stream::~Lex_input_stream() +{} + /* This is called before every query that is to be parsed. @@ -115,14 +137,12 @@ st_parsing_options::reset() (We already do too much here) */ -void lex_start(THD *thd, uchar *buf,uint length) +void lex_start(THD *thd) { 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(); @@ -155,15 +175,13 @@ void lex_start(THD *thd, uchar *buf,uint length) lex->stmt_prepare_mode= FALSE; lex->derived_tables= 0; lex->lock_option= TL_READ; - lex->found_semicolon= 0; lex->safe_to_cache_query= 1; lex->time_zone_tables_used= 0; lex->leaf_tables_insert= 0; lex->parsing_options.reset(); lex->empty_field_list_on_rset= 0; lex->select_lex.select_number= 1; - lex->next_state=MY_LEX_START; - lex->yylineno = 1; + lex->in_comment=0; lex->length=0; lex->select_lex.in_sum_expr=0; @@ -175,7 +193,6 @@ void lex_start(THD *thd, uchar *buf,uint length) lex->select_lex.udf_list.empty(); 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= lex->orig_sql_command= SQLCOM_END; lex->duplicates= DUP_ERROR; lex->ignore= 0; @@ -201,22 +218,22 @@ void lex_end(LEX *lex) } -static int find_keyword(LEX *lex, uint len, bool function) +static int find_keyword(Lex_input_stream *lip, uint len, bool function) { - uchar *tok=lex->tok_start; + const char *tok= lip->tok_start; - SYMBOL *symbol = get_hash_symbol((const char *)tok,len,function); + SYMBOL *symbol= get_hash_symbol(tok, len, function); if (symbol) { - lex->yylval->symbol.symbol=symbol; - lex->yylval->symbol.str= (char*) tok; - lex->yylval->symbol.length=len; + lip->yylval->symbol.symbol=symbol; + lip->yylval->symbol.str= (char*) tok; + lip->yylval->symbol.length=len; if ((symbol->tok == NOT_SYM) && - (lex->thd->variables.sql_mode & MODE_HIGH_NOT_PRECEDENCE)) + (lip->m_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)) + !(lip->m_thd->variables.sql_mode & MODE_PIPES_AS_CONCAT)) return OR2_SYM; return symbol->tok; @@ -245,12 +262,12 @@ bool is_keyword(const char *name, uint len) /* make a copy of token before ptr and set yytoklen */ -static LEX_STRING get_token(LEX *lex,uint length) +static LEX_STRING get_token(Lex_input_stream *lip, uint skip, uint length) { LEX_STRING tmp; yyUnget(); // ptr points now after last token char - tmp.length=lex->yytoklen=length; - tmp.str=(char*) lex->thd->strmake((char*) lex->tok_start,tmp.length); + tmp.length=lip->yytoklen=length; + tmp.str= lip->m_thd->strmake(lip->tok_start + skip, tmp.length); return tmp; } @@ -261,16 +278,19 @@ static LEX_STRING get_token(LEX *lex,uint length) future to operate multichar strings (like ucs2) */ -static LEX_STRING get_quoted_token(LEX *lex,uint length, char quote) +static LEX_STRING get_quoted_token(Lex_input_stream *lip, + uint skip, + uint length, char quote) { LEX_STRING tmp; byte *from, *to, *end; yyUnget(); // ptr points now after last token char - tmp.length=lex->yytoklen=length; - tmp.str=(char*) lex->thd->alloc(tmp.length+1); - for (from= (byte*) lex->tok_start, to= (byte*) tmp.str, end= to+length ; - to != end ; - ) + tmp.length=lip->yytoklen=length; + tmp.str=(char*) lip->m_thd->alloc(tmp.length+1); + from= (byte*) lip->tok_start + skip; + to= (byte*) tmp.str; + end= to+length; + for ( ; to != end; ) { if ((*to++= *from++) == quote) from++; // Skip double quotes @@ -285,15 +305,14 @@ static LEX_STRING get_quoted_token(LEX *lex,uint length, char quote) Fix sometimes to do only one scan of the string */ -static char *get_text(LEX *lex) +static char *get_text(Lex_input_stream *lip) { reg1 uchar c,sep; uint found_escape=0; - CHARSET_INFO *cs= lex->thd->charset(); + CHARSET_INFO *cs= lip->m_thd->charset(); sep= yyGetLast(); // String should end with this - //lex->tok_start=lex->ptr-1; // Remember ' - while (lex->ptr != lex->end_of_query) + while (lip->ptr != lip->end_of_query) { c = yyGet(); #ifdef USE_MB @@ -301,18 +320,18 @@ static char *get_text(LEX *lex) int l; if (use_mb(cs) && (l = my_ismbchar(cs, - (const char *)lex->ptr-1, - (const char *)lex->end_of_query))) { - lex->ptr += l-1; + lip->ptr-1, + lip->end_of_query))) { + lip->ptr += l-1; continue; } } #endif if (c == '\\' && - !(lex->thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES)) + !(lip->m_thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES)) { // Escaped character found_escape=1; - if (lex->ptr == lex->end_of_query) + if (lip->ptr == lip->end_of_query) return 0; yySkip(); } @@ -327,21 +346,23 @@ static char *get_text(LEX *lex) yyUnget(); /* Found end. Unescape and return string */ - uchar *str,*end,*start; + const char *str; + const char *end; + char *start; - str=lex->tok_start+1; - end=lex->ptr-1; - if (!(start=(uchar*) lex->thd->alloc((uint) (end-str)+1))) + str=lip->tok_start+1; + end=lip->ptr-1; + if (!(start=(char*) lip->m_thd->alloc((uint) (end-str)+1))) return (char*) ""; // Sql_alloc has set error flag if (!found_escape) { - lex->yytoklen=(uint) (end-str); - memcpy(start,str,lex->yytoklen); - start[lex->yytoklen]=0; + lip->yytoklen=(uint) (end-str); + memcpy(start,str,lip->yytoklen); + start[lip->yytoklen]=0; } else { - uchar *to; + char *to; for (to=start ; str != end ; str++) { @@ -356,7 +377,7 @@ static char *get_text(LEX *lex) continue; } #endif - if (!(lex->thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) && + if (!(lip->m_thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) && *str == '\\' && str+1 != end) { switch(*++str) { @@ -393,7 +414,7 @@ static char *get_text(LEX *lex) *to++ = *str; } *to=0; - lex->yytoklen=(uint) (to-start); + lip->yytoklen=(uint) (to-start); } return (char*) start; } @@ -506,20 +527,21 @@ int MYSQLlex(void *arg, void *yythd) int tokval, result_state; uint length; enum my_lex_states state; - LEX *lex= ((THD *)yythd)->lex; + THD *thd= (THD *)yythd; + Lex_input_stream *lip= thd->m_lip; + LEX *lex= thd->lex; YYSTYPE *yylval=(YYSTYPE*) arg; - CHARSET_INFO *cs= ((THD *) yythd)->charset(); + CHARSET_INFO *cs= thd->charset(); uchar *state_map= cs->state_map; uchar *ident_map= cs->ident_map; - lex->yylval=yylval; // The global state + lip->yylval=yylval; // The global state - lex->tok_end_prev= lex->tok_end; - lex->tok_start_prev= lex->tok_start; + lip->tok_start_prev= lip->tok_start; - lex->tok_start=lex->tok_end=lex->ptr; - state=lex->next_state; - lex->next_state=MY_LEX_OPERATOR_OR_IDENT; + lip->tok_start=lip->tok_end=lip->ptr; + state=lip->next_state; + lip->next_state=MY_LEX_OPERATOR_OR_IDENT; LINT_INIT(c); for (;;) { @@ -530,9 +552,9 @@ int MYSQLlex(void *arg, void *yythd) for (c=yyGet() ; (state_map[c] == MY_LEX_SKIP) ; c= yyGet()) { if (c == '\n') - lex->yylineno++; + lip->yylineno++; } - lex->tok_start=lex->ptr-1; // Start of real token + lip->tok_start=lip->ptr-1; // Start of real token state= (enum my_lex_states) state_map[c]; break; case MY_LEX_ESCAPE: @@ -551,13 +573,13 @@ int MYSQLlex(void *arg, void *yythd) state=MY_LEX_COMMENT; break; } - yylval->lex_str.str=(char*) (lex->ptr=lex->tok_start);// Set to first chr + yylval->lex_str.str=(char*) (lip->ptr=lip->tok_start);// Set to first chr yylval->lex_str.length=1; c=yyGet(); if (c != ')') - lex->next_state= MY_LEX_START; // Allow signed numbers + lip->next_state= MY_LEX_START; // Allow signed numbers if (c == ',') - lex->tok_start=lex->ptr; // Let tok_start point at next item + lip->tok_start=lip->ptr; // Let tok_start point at next item /* Check for a placeholder: it should not precede a possible identifier because of binlogging: when a placeholder is replaced with @@ -575,14 +597,14 @@ int MYSQLlex(void *arg, void *yythd) break; } /* Found N'string' */ - lex->tok_start++; // Skip N + lip->tok_start++; // Skip N yySkip(); // Skip ' - if (!(yylval->lex_str.str = get_text(lex))) + if (!(yylval->lex_str.str = get_text(lip))) { state= MY_LEX_CHAR; // Read char by char break; } - yylval->lex_str.length= lex->yytoklen; + yylval->lex_str.length= lip->yytoklen; return(NCHAR_STRING); case MY_LEX_IDENT_OR_HEX: @@ -598,7 +620,7 @@ int MYSQLlex(void *arg, void *yythd) break; } case MY_LEX_IDENT: - uchar *start; + const char *start; #if defined(USE_MB) && defined(USE_MB_IDENT) if (use_mb(cs)) { @@ -606,13 +628,13 @@ int MYSQLlex(void *arg, void *yythd) if (my_mbcharlen(cs, yyGetLast()) > 1) { int l = my_ismbchar(cs, - (const char *)lex->ptr-1, - (const char *)lex->end_of_query); + lip->ptr-1, + lip->end_of_query); if (l == 0) { state = MY_LEX_CHAR; continue; } - lex->ptr += l - 1; + lip->ptr += l - 1; } while (ident_map[c=yyGet()]) { @@ -620,10 +642,10 @@ int MYSQLlex(void *arg, void *yythd) { int l; if ((l = my_ismbchar(cs, - (const char *)lex->ptr-1, - (const char *)lex->end_of_query)) == 0) + lip->ptr-1, + lip->end_of_query)) == 0) break; - lex->ptr += l-1; + lip->ptr += l-1; } } } @@ -634,9 +656,9 @@ int MYSQLlex(void *arg, void *yythd) /* If there were non-ASCII characters, mark that we must convert */ result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT; } - length= (uint) (lex->ptr - lex->tok_start)-1; - start= lex->ptr; - if (lex->ignore_space) + length= (uint) (lip->ptr - lip->tok_start)-1; + start= lip->ptr; + if (lip->ignore_space) { /* If we find a space then this can't be an identifier. We notice this @@ -644,19 +666,19 @@ int MYSQLlex(void *arg, void *yythd) */ for (; state_map[c] == MY_LEX_SKIP ; c= yyGet()); } - if (start == lex->ptr && c == '.' && ident_map[yyPeek()]) - lex->next_state=MY_LEX_IDENT_SEP; + if (start == lip->ptr && c == '.' && ident_map[yyPeek()]) + lip->next_state=MY_LEX_IDENT_SEP; else { // '(' must follow directly if function yyUnget(); - if ((tokval = find_keyword(lex,length,c == '('))) + if ((tokval = find_keyword(lip, length, c == '('))) { - lex->next_state= MY_LEX_START; // Allow signed numbers + lip->next_state= MY_LEX_START; // Allow signed numbers return(tokval); // Was keyword } yySkip(); // next state does a unget } - yylval->lex_str=get_token(lex,length); + yylval->lex_str=get_token(lip, 0, length); /* Note: "SELECT _bla AS 'alias'" @@ -673,12 +695,12 @@ int MYSQLlex(void *arg, void *yythd) return(result_state); // IDENT or IDENT_QUOTED case MY_LEX_IDENT_SEP: // Found ident and now '.' - yylval->lex_str.str=(char*) lex->ptr; + yylval->lex_str.str=(char*) lip->ptr; yylval->lex_str.length=1; c=yyGet(); // should be '.' - lex->next_state= MY_LEX_IDENT_START;// Next is an ident (not a keyword) + lip->next_state= MY_LEX_IDENT_START;// Next is an ident (not a keyword) if (!ident_map[yyPeek()]) // Probably ` or " - lex->next_state= MY_LEX_START; + lip->next_state= MY_LEX_START; return((int) c); case MY_LEX_NUMBER_IDENT: // number or ident which num-start @@ -698,36 +720,32 @@ int MYSQLlex(void *arg, void *yythd) { yySkip(); while (my_isdigit(cs,yyGet())) ; - yylval->lex_str=get_token(lex,yyLength()); + yylval->lex_str=get_token(lip, 0, yyLength()); return(FLOAT_NUM); } } yyUnget(); /* purecov: inspected */ } - else if (c == 'x' && (lex->ptr - lex->tok_start) == 2 && - lex->tok_start[0] == '0' ) + else if (c == 'x' && (lip->ptr - lip->tok_start) == 2 && + lip->tok_start[0] == '0' ) { // Varbinary while (my_isxdigit(cs,(c = yyGet()))) ; - if ((lex->ptr - lex->tok_start) >= 4 && !ident_map[c]) + if ((lip->ptr - lip->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; + /* skip '0x' */ + yylval->lex_str=get_token(lip, 2, yyLength()-2); return (HEX_NUM); } yyUnget(); } - else if (c == 'b' && (lex->ptr - lex->tok_start) == 2 && - lex->tok_start[0] == '0' ) + else if (c == 'b' && (lip->ptr - lip->tok_start) == 2 && + lip->tok_start[0] == '0' ) { // b'bin-number' while (my_isxdigit(cs,(c = yyGet()))) ; - if ((lex->ptr - lex->tok_start) >= 4 && !ident_map[c]) + if ((lip->ptr - lip->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; + /* Skip '0b' */ + yylval->lex_str= get_token(lip, 2, yyLength()-2); return (BIN_NUM); } yyUnget(); @@ -745,10 +763,10 @@ int MYSQLlex(void *arg, void *yythd) { int l; if ((l = my_ismbchar(cs, - (const char *)lex->ptr-1, - (const char *)lex->end_of_query)) == 0) + lip->ptr-1, + lip->end_of_query)) == 0) break; - lex->ptr += l-1; + lip->ptr += l-1; } } } @@ -760,16 +778,15 @@ int MYSQLlex(void *arg, void *yythd) result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT; } if (c == '.' && ident_map[yyPeek()]) - lex->next_state=MY_LEX_IDENT_SEP;// Next is '.' + lip->next_state=MY_LEX_IDENT_SEP;// Next is '.' - yylval->lex_str= get_token(lex,yyLength()); + yylval->lex_str= get_token(lip, 0, yyLength()); return(result_state); case MY_LEX_USER_VARIABLE_DELIMITER: // Found quote char { uint double_quotes= 0; char quote_char= c; // Used char - lex->tok_start=lex->ptr; // Skip first ` while ((c=yyGet())) { int var_length; @@ -789,23 +806,24 @@ int MYSQLlex(void *arg, void *yythd) #ifdef USE_MB else if (var_length < 1) break; // Error - lex->ptr+= var_length-1; + lip->ptr+= var_length-1; #endif } if (double_quotes) - yylval->lex_str=get_quoted_token(lex,yyLength() - double_quotes, + yylval->lex_str=get_quoted_token(lip, 1, + yyLength() - double_quotes -1, quote_char); else - yylval->lex_str=get_token(lex,yyLength()); + yylval->lex_str=get_token(lip, 1, yyLength() -1); if (c == quote_char) yySkip(); // Skip end ` - lex->next_state= MY_LEX_START; + lip->next_state= MY_LEX_START; return(IDENT_QUOTED); } - case MY_LEX_INT_OR_REAL: // Compleat int or incompleat real + case MY_LEX_INT_OR_REAL: // Complete int or incomplete real if (c != '.') { // Found complete integer number. - yylval->lex_str=get_token(lex,yyLength()); + yylval->lex_str=get_token(lip, 0, yyLength()); return int_token(yylval->lex_str.str,yylval->lex_str.length); } // fall through @@ -823,47 +841,45 @@ int MYSQLlex(void *arg, void *yythd) break; } while (my_isdigit(cs,yyGet())) ; - yylval->lex_str=get_token(lex,yyLength()); + yylval->lex_str=get_token(lip, 0, yyLength()); return(FLOAT_NUM); } - yylval->lex_str=get_token(lex,yyLength()); + yylval->lex_str=get_token(lip, 0, yyLength()); return(DECIMAL_NUM); case MY_LEX_HEX_NUMBER: // Found x'hexstring' yyGet(); // Skip ' while (my_isxdigit(cs,(c = yyGet()))) ; - length=(lex->ptr - lex->tok_start); // Length of hexnum+3 + length=(lip->ptr - lip->tok_start); // Length of hexnum+3 if (!(length & 1) || 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 x' - yylval->lex_str.length-=3; // Don't count x' and last ' - lex->yytoklen-=3; + yylval->lex_str=get_token(lip, + 2, // skip x' + length-3); // don't count x' and last ' 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 + length= (lip->ptr - lip->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); + yylval->lex_str= get_token(lip, + 2, // skip b' + length-3); // don't count b' and last ' + 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) yySkip(); - if ((tokval = find_keyword(lex,(uint) (lex->ptr - lex->tok_start),0))) + if ((tokval = find_keyword(lip,(uint) (lip->ptr - lip->tok_start),0))) { - lex->next_state= MY_LEX_START; // Allow signed numbers + lip->next_state= MY_LEX_START; // Allow signed numbers return(tokval); } state = MY_LEX_CHAR; // Something fishy found @@ -877,9 +893,9 @@ int MYSQLlex(void *arg, void *yythd) if (state_map[yyPeek()] == MY_LEX_CMP_OP) yySkip(); } - if ((tokval = find_keyword(lex,(uint) (lex->ptr - lex->tok_start),0))) + if ((tokval = find_keyword(lip,(uint) (lip->ptr - lip->tok_start),0))) { - lex->next_state= MY_LEX_START; // Found long op + lip->next_state= MY_LEX_START; // Found long op return(tokval); } state = MY_LEX_CHAR; // Something fishy found @@ -892,24 +908,24 @@ int MYSQLlex(void *arg, void *yythd) break; } yySkip(); - tokval = find_keyword(lex,2,0); // Is a bool operator - lex->next_state= MY_LEX_START; // Allow signed numbers + tokval = find_keyword(lip,2,0); // Is a bool operator + lip->next_state= MY_LEX_START; // Allow signed numbers return(tokval); case MY_LEX_STRING_OR_DELIMITER: - if (((THD *) yythd)->variables.sql_mode & MODE_ANSI_QUOTES) + if (thd->variables.sql_mode & MODE_ANSI_QUOTES) { state= MY_LEX_USER_VARIABLE_DELIMITER; break; } /* " used for strings */ case MY_LEX_STRING: // Incomplete text string - if (!(yylval->lex_str.str = get_text(lex))) + if (!(yylval->lex_str.str = get_text(lip))) { state= MY_LEX_CHAR; // Read char by char break; } - yylval->lex_str.length=lex->yytoklen; + yylval->lex_str.length=lip->yytoklen; return(TEXT_STRING); case MY_LEX_COMMENT: // Comment @@ -933,7 +949,7 @@ int MYSQLlex(void *arg, void *yythd) state=MY_LEX_START; if (my_isdigit(cs,yyPeek())) { // Version number - version=strtol((char*) lex->ptr,(char**) &lex->ptr,10); + version=strtol((char*) lip->ptr,(char**) &lip->ptr,10); } if (version <= MYSQL_VERSION_ID) { @@ -941,13 +957,13 @@ int MYSQLlex(void *arg, void *yythd) break; } } - while (lex->ptr != lex->end_of_query && + while (lip->ptr != lip->end_of_query && ((c=yyGet()) != '*' || yyPeek() != '/')) { if (c == '\n') - lex->yylineno++; + lip->yylineno++; } - if (lex->ptr != lex->end_of_query) + if (lip->ptr != lip->end_of_query) yySkip(); // remove last '/' state = MY_LEX_START; // Try again break; @@ -972,14 +988,13 @@ int MYSQLlex(void *arg, void *yythd) case MY_LEX_SEMICOLON: // optional line terminator if (yyPeek()) { - THD* thd= (THD*)yythd; if ((thd->client_capabilities & CLIENT_MULTI_STATEMENTS) && !lex->stmt_prepare_mode) { lex->safe_to_cache_query= 0; - lex->found_semicolon=(char*) lex->ptr; + lip->found_semicolon= lip->ptr; thd->server_status|= SERVER_MORE_RESULTS_EXISTS; - lex->next_state= MY_LEX_END; + lip->next_state= MY_LEX_END; return (END_OF_INPUT); } state= MY_LEX_CHAR; // Return ';' @@ -987,15 +1002,15 @@ int MYSQLlex(void *arg, void *yythd) } /* fall true */ case MY_LEX_EOL: - if (lex->ptr >= lex->end_of_query) + if (lip->ptr >= lip->end_of_query) { - lex->next_state=MY_LEX_END; // Mark for next loop + lip->next_state=MY_LEX_END; // Mark for next loop return(END_OF_INPUT); } state=MY_LEX_CHAR; break; case MY_LEX_END: - lex->next_state=MY_LEX_END; + lip->next_state=MY_LEX_END; return(0); // We found end of input last time /* Actually real shouldn't start with . but allow them anyhow */ @@ -1015,26 +1030,26 @@ int MYSQLlex(void *arg, void *yythd) case MY_LEX_STRING_OR_DELIMITER: break; case MY_LEX_USER_END: - lex->next_state=MY_LEX_SYSTEM_VAR; + lip->next_state=MY_LEX_SYSTEM_VAR; break; default: - lex->next_state=MY_LEX_HOSTNAME; + lip->next_state=MY_LEX_HOSTNAME; break; } - yylval->lex_str.str=(char*) lex->ptr; + yylval->lex_str.str=(char*) lip->ptr; yylval->lex_str.length=1; return((int) '@'); case MY_LEX_HOSTNAME: // end '@' of user@hostname for (c=yyGet() ; my_isalnum(cs,c) || c == '.' || c == '_' || c == '$'; c= yyGet()) ; - yylval->lex_str=get_token(lex,yyLength()); + yylval->lex_str=get_token(lip, 0, yyLength()); return(LEX_HOSTNAME); case MY_LEX_SYSTEM_VAR: - yylval->lex_str.str=(char*) lex->ptr; + yylval->lex_str.str=(char*) lip->ptr; yylval->lex_str.length=1; yySkip(); // Skip '@' - lex->next_state= (state_map[yyPeek()] == + lip->next_state= (state_map[yyPeek()] == MY_LEX_USER_VARIABLE_DELIMITER ? MY_LEX_OPERATOR_OR_IDENT : MY_LEX_IDENT_OR_KEYWORD); @@ -1051,16 +1066,16 @@ int MYSQLlex(void *arg, void *yythd) result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT; if (c == '.') - lex->next_state=MY_LEX_IDENT_SEP; - length= (uint) (lex->ptr - lex->tok_start)-1; + lip->next_state=MY_LEX_IDENT_SEP; + length= (uint) (lip->ptr - lip->tok_start)-1; if (length == 0) return(ABORT_SYM); // Names must be nonempty. - if ((tokval= find_keyword(lex,length,0))) + if ((tokval= find_keyword(lip, length,0))) { yyUnget(); // Put back 'c' return(tokval); // Was keyword } - yylval->lex_str=get_token(lex,length); + yylval->lex_str=get_token(lip, 0, length); return(result_state); } } @@ -1093,7 +1108,7 @@ Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root) Pointer to the last non-comment symbol of the statement. */ -uchar *skip_rear_comments(uchar *begin, uchar *end) +char *skip_rear_comments(char *begin, char *end) { while (begin < end && (end[-1] <= ' ' || end[-1] == '*' || end[-1] == '/' || end[-1] == ';')) diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 2ff29d684d1..d07de73d5b9 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -470,7 +470,7 @@ public: void set_thd(THD *thd_arg) { thd= thd_arg; } inline bool is_union (); - friend void lex_start(THD *thd, uchar *buf, uint length); + friend void lex_start(THD *thd); friend int subselect_union_engine::exec(); List<Item> *get_unit_column_types(); @@ -676,7 +676,7 @@ public: void cut_subtree() { slave= 0; } bool test_limit(); - friend void lex_start(THD *thd, uchar *buf, uint length); + friend void lex_start(THD *thd); st_select_lex() : n_sum_items(0), n_child_sum_items(0) {} void make_empty_select() { @@ -906,30 +906,73 @@ struct st_parsing_options }; +/** + This class represents the character input stream consumed during + lexical analysis. +*/ +class Lex_input_stream +{ +public: + Lex_input_stream(THD *thd, const char* buff, unsigned int length); + ~Lex_input_stream(); + + /** Current thread. */ + THD *m_thd; + + /** Current line number. */ + uint yylineno; + + /** Length of the last token parsed. */ + uint yytoklen; + + /** Interface with bison, value of the last token parsed. */ + LEX_YYSTYPE yylval; + + /** Pointer to the current position in the input stream. */ + const char* ptr; + + /** Starting position of the last token parsed. */ + const char* tok_start; + + /** Ending position of the last token parsed. */ + const char* tok_end; + + /** End of the query text in the input stream. */ + const char* end_of_query; + + /** Starting position of the previous token parsed. */ + const char* tok_start_prev; + + /** Begining of the query text in the input stream. */ + const char* buf; + + /** Current state of the lexical analyser. */ + enum my_lex_states next_state; + + /** Position of ';' in the stream, to delimit multiple queries. */ + const char* found_semicolon; + + /** SQL_MODE = IGNORE_SPACE. */ + bool ignore_space; +}; + + /* The state of the lex parsing. This is saved in the THD struct */ typedef struct st_lex : public Query_tables_list { - uint yylineno,yytoklen; /* Simulate lex */ - LEX_YYSTYPE yylval; SELECT_LEX_UNIT unit; /* most upper unit */ SELECT_LEX select_lex; /* first SELECT_LEX */ /* current SELECT_LEX in parsing */ SELECT_LEX *current_select; /* list of all SELECT_LEX */ SELECT_LEX *all_selects_list; - uchar *buf; /* The beginning of string, used by SPs */ - uchar *ptr,*tok_start,*tok_end,*end_of_query; - - /* The values of tok_start/tok_end as they were one call of MYSQLlex before */ - uchar *tok_start_prev, *tok_end_prev; char *length,*dec,*change,*name; char *help_arg; char *backup_dir; /* For RESTORE/BACKUP */ char* to_log; /* For PURGE MASTER LOGS TO */ char* x509_subject,*x509_issuer,*ssl_cipher; - char* found_semicolon; /* For multi queries - next query */ String *wild; sql_exchange *exchange; select_result *result; @@ -998,7 +1041,6 @@ typedef struct st_lex : public Query_tables_list enum_sql_command sql_command, orig_sql_command; thr_lock_type lock_option; enum SSL_type ssl_type; /* defined in violite.h */ - enum my_lex_states next_state; enum enum_duplicates duplicates; enum enum_tx_isolation tx_isolation; enum enum_ha_read_modes ha_read_mode; @@ -1030,7 +1072,7 @@ typedef struct st_lex : public Query_tables_list uint8 create_view_algorithm; uint8 create_view_check; bool drop_if_exists, drop_temporary, local_file, one_shot_set; - bool in_comment, ignore_space, verbose, no_write_to_binlog; + bool in_comment, verbose, no_write_to_binlog; bool tx_chain, tx_release; /* Special JOIN::prepare mode: changing of query is prohibited. @@ -1109,8 +1151,9 @@ typedef struct st_lex : public Query_tables_list Pointers to part of LOAD DATA statement that should be rewritten during replication ("LOCAL 'filename' REPLACE INTO" part). */ - uchar *fname_start, *fname_end; - + const char *fname_start; + const char *fname_end; + bool escape_used; st_lex(); @@ -1219,7 +1262,7 @@ struct st_lex_local: public st_lex extern void lex_init(void); extern void lex_free(void); -extern void lex_start(THD *thd, uchar *buf,uint length); +extern void lex_start(THD *thd); extern void lex_end(LEX *lex); extern int MYSQLlex(void *arg, void *yythd); -extern uchar *skip_rear_comments(uchar *begin, uchar *end); +extern char *skip_rear_comments(char *begin, char *end); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 35089bbb251..30abb7c2c0a 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1238,6 +1238,7 @@ pthread_handler_t handle_bootstrap(void *arg) THD *thd=(THD*) arg; FILE *file=bootstrap_file; char *buff; + const char* found_semicolon= NULL; /* The following must be called before DBUG_ENTER */ thd->thread_stack= (char*) &thd; @@ -1314,7 +1315,7 @@ pthread_handler_t handle_bootstrap(void *arg) */ thd->query_id=next_query_id(); thd->set_time(); - mysql_parse(thd,thd->query,length); + mysql_parse(thd, thd->query, length, & found_semicolon); close_thread_tables(thd); // Free tables if (thd->is_fatal_error) @@ -1793,17 +1794,19 @@ bool dispatch_command(enum enum_server_command command, THD *thd, char *packet_end= thd->query + thd->query_length; /* 'b' stands for 'buffer' parameter', special for 'my_snprintf' */ const char *format= "%.*b"; + const char* found_semicolon= NULL; + mysql_log.write(thd,command, format, thd->query_length, thd->query); DBUG_PRINT("query",("%-.4096s",thd->query)); if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),QUERY_PRIOR); - mysql_parse(thd,thd->query, thd->query_length); + mysql_parse(thd, thd->query, thd->query_length, & found_semicolon); - while (!thd->killed && thd->lex->found_semicolon && !thd->net.report_error) + while (!thd->killed && found_semicolon && !thd->net.report_error) { - char *next_packet= thd->lex->found_semicolon; + char *next_packet= (char*) found_semicolon; net->no_send_error= 0; /* Multiple queries exits, execute them individually @@ -1828,7 +1831,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->set_time(); /* Reset the query start time. */ /* TODO: set thd->lex->sql_command to SQLCOM_END here */ VOID(pthread_mutex_unlock(&LOCK_thread_count)); - mysql_parse(thd, next_packet, length); + mysql_parse(thd, next_packet, length, & found_semicolon); } if (!(specialflag & SPECIAL_NO_PRIOR)) @@ -1849,7 +1852,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, LEX_STRING conv_name; /* used as fields initializator */ - lex_start(thd, 0, 0); + lex_start(thd); statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS], &LOCK_status); @@ -1886,7 +1889,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; /* init structures for VIEW processing */ table_list.select_lex= &(thd->lex->select_lex); - mysql_init_query(thd, (uchar*)"", 0); + + lex_start(thd); + mysql_reset_thd_for_next_command(thd); + thd->lex-> select_lex.table_list.link_in_list((byte*) &table_list, (byte**) &table_list.next_local); @@ -5774,20 +5780,6 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize) } -/**************************************************************************** - Initialize global thd variables needed for query -****************************************************************************/ - -void -mysql_init_query(THD *thd, uchar *buf, uint length) -{ - DBUG_ENTER("mysql_init_query"); - lex_start(thd, buf, length); - mysql_reset_thd_for_next_command(thd); - DBUG_VOID_RETURN; -} - - /* Reset THD part responsible for command processing state. @@ -5974,21 +5966,55 @@ void mysql_init_multi_delete(LEX *lex) mysql_test_parse_for_slave() in this same file. */ -void mysql_parse(THD *thd, char *inBuf, uint length) +/** + Parse a query. + @param thd Current thread + @param inBuf Begining of the query text + @param length Length of the query text + @param [out] semicolon For multi queries, position of the character of + the next query in the query text. +*/ + +void mysql_parse(THD *thd, const char *inBuf, uint length, + const char ** found_semicolon) { DBUG_ENTER("mysql_parse"); DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on();); - mysql_init_query(thd, (uchar*) inBuf, length); - if (query_cache_send_result_to_client(thd, inBuf, length) <= 0) + /* + Warning. + The purpose of query_cache_send_result_to_client() is to lookup the + query in the query cache first, to avoid parsing and executing it. + So, the natural implementation would be to: + - first, call query_cache_send_result_to_client, + - second, if caching failed, initialise the lexical and syntactic parser. + The problem is that the query cache depends on a clean initialization + of (among others) lex->safe_to_cache_query and thd->server_status, + which are reset respectively in + - lex_start() + - mysql_reset_thd_for_next_command() + So, initializing the lexical analyser *before* using the query cache + is required for the cache to work properly. + FIXME: cleanup the dependencies in the code to simplify this. + */ + lex_start(thd); + mysql_reset_thd_for_next_command(thd); + + if (query_cache_send_result_to_client(thd, (char*) inBuf, length) <= 0) { LEX *lex= thd->lex; - + sp_cache_flush_obsolete(&thd->sp_proc_cache); sp_cache_flush_obsolete(&thd->sp_func_cache); - - if (!MYSQLparse((void *)thd) && ! thd->is_fatal_error) + + Lex_input_stream lip(thd, inBuf, length); + thd->m_lip= &lip; + + int err= MYSQLparse(thd); + *found_semicolon= lip.found_semicolon; + + if (!err && ! thd->is_fatal_error) { #ifndef NO_EMBEDDED_ACCESS_CHECKS if (mqh_used && thd->user_connect && @@ -6011,8 +6037,8 @@ void mysql_parse(THD *thd, char *inBuf, uint length) PROCESSLIST. Note that we don't need LOCK_thread_count to modify query_length. */ - if (lex->found_semicolon && - (thd->query_length= (ulong)(lex->found_semicolon - thd->query))) + if (lip.found_semicolon && + (thd->query_length= (ulong)(lip.found_semicolon - thd->query))) thd->query_length--; /* Actually execute the query */ mysql_execute_command(thd); @@ -6039,6 +6065,12 @@ void mysql_parse(THD *thd, char *inBuf, uint length) thd->cleanup_after_query(); DBUG_ASSERT(thd->change_list.is_empty()); } + else + { + /* There are no multi queries in the cache. */ + *found_semicolon= NULL; + } + DBUG_VOID_RETURN; } @@ -6059,8 +6091,13 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) bool error= 0; DBUG_ENTER("mysql_test_parse_for_slave"); - mysql_init_query(thd, (uchar*) inBuf, length); - if (!MYSQLparse((void*) thd) && ! thd->is_fatal_error && + Lex_input_stream lip(thd, inBuf, length); + thd->m_lip= &lip; + lex_start(thd); + mysql_reset_thd_for_next_command(thd); + int err= MYSQLparse((void*) thd); + + if (!err && ! thd->is_fatal_error && all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first)) error= 1; /* Ignore question */ thd->end_statement(); @@ -7119,8 +7156,9 @@ bool check_simple_select() if (lex->current_select != &lex->select_lex) { char command[80]; - strmake(command, lex->yylval->symbol.str, - min(lex->yylval->symbol.length, sizeof(command)-1)); + Lex_input_stream *lip= thd->m_lip; + strmake(command, lip->yylval->symbol.str, + min(lip->yylval->symbol.length, sizeof(command)-1)); my_error(ER_CANT_USE_OPTION_HERE, MYF(0), command); return 1; } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 1ec65743b0f..d3cc3613b4a 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2799,11 +2799,15 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) old_stmt_arena= thd->stmt_arena; thd->stmt_arena= this; - lex_start(thd, (uchar*) thd->query, thd->query_length); + + Lex_input_stream lip(thd, thd->query, thd->query_length); + thd->m_lip= &lip; + lex_start(thd); lex->safe_to_cache_query= FALSE; lex->stmt_prepare_mode= TRUE; + int err= MYSQLparse((void *)thd); - error= MYSQLparse((void *)thd) || thd->is_fatal_error || + error= err || thd->is_fatal_error || thd->net.report_error || init_param_array(this); /* diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 55d51ad07b7..26b557a8247 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -978,10 +978,14 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, LEX_STRING *trg_definer= it_definer++; thd->variables.sql_mode= (ulong)*trg_sql_mode; - lex_start(thd, (uchar*)trg_create_str->str, trg_create_str->length); + Lex_input_stream lip(thd, trg_create_str->str, trg_create_str->length); + thd->m_lip= &lip; + lex_start(thd); thd->spcont= 0; - if (MYSQLparse((void *)thd) || thd->is_fatal_error) + int err= MYSQLparse((void *)thd); + + if (err || thd->is_fatal_error) { /* Currently sphead is always deleted in case of a parse error */ DBUG_ASSERT(lex.sphead == 0); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index d717aea9a3e..ba367040b36 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -772,8 +772,8 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, view->query.str= (char*)str.ptr(); view->query.length= str.length()-1; // we do not need last \0 view->source.str= thd->query + thd->lex->create_view_select_start; - view->source.length= (char *)skip_rear_comments((uchar *)view->source.str, - (uchar *)thd->query + + view->source.length= (char *)skip_rear_comments((char *)view->source.str, + (char *)thd->query + thd->query_length) - view->source.str; view->file_version= 1; @@ -984,10 +984,14 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, now Lex placed in statement memory */ table->view= lex= thd->lex= (LEX*) new(thd->mem_root) st_lex_local; - lex_start(thd, (uchar*)table->query.str, table->query.length); - view_select= &lex->select_lex; - view_select->select_number= ++thd->select_number; + { + Lex_input_stream lip(thd, table->query.str, table->query.length); + thd->m_lip= &lip; + lex_start(thd); + view_select= &lex->select_lex; + view_select->select_number= ++thd->select_number; + ulong save_mode= thd->variables.sql_mode; /* switch off modes which can prevent normal parsing of VIEW - MODE_REAL_AS_FLOAT affect only CREATE TABLE parsing diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4a50a602121..390a991c7b6 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -86,12 +86,13 @@ const LEX_STRING null_lex_str={0,0}; void my_parse_error(const char *s) { THD *thd= current_thd; + Lex_input_stream *lip= thd->m_lip; - char *yytext= (char*) thd->lex->tok_start; + const char *yytext= lip->tok_start; /* Push an error into the error stack */ my_printf_error(ER_PARSE_ERROR, ER(ER_PARSE_ERROR), MYF(0), s, - (yytext ? (char*) yytext : ""), - thd->lex->yylineno); + (yytext ? yytext : ""), + lip->yylineno); } /** @@ -1619,7 +1620,9 @@ create_function_tail: } | '(' { - LEX *lex= Lex; + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; sp_head *sp; /* @@ -1639,9 +1642,9 @@ create_function_tail: } /* Order is important here: new - reset - init */ sp= new sp_head(); - sp->reset_thd_mem_root(YYTHD); + sp->reset_thd_mem_root(thd); sp->init(lex); - sp->init_sp_name(YYTHD, lex->spname); + sp->init_sp_name(thd, lex->spname); sp->m_type= TYPE_ENUM_FUNCTION; lex->sphead= sp; @@ -1650,15 +1653,17 @@ create_function_tail: * stored procedure, otherwise yylex will chop it into pieces * at each ';'. */ - sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; - YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; - lex->sphead->m_param_begin= lex->tok_start+1; + sp->m_old_cmq= thd->client_capabilities & CLIENT_MULTI_QUERIES; + thd->client_capabilities &= ~CLIENT_MULTI_QUERIES; + lex->sphead->m_param_begin= lip->tok_start+1; } sp_fdparam_list ')' { - LEX *lex= Lex; + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; - lex->sphead->m_param_end= lex->tok_start; + lex->sphead->m_param_end= lip->tok_start; } RETURNS_SYM { @@ -1682,10 +1687,12 @@ create_function_tail: } sp_c_chistics { - LEX *lex= Lex; + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->m_body_begin= lex->tok_start; + lex->sphead->m_body_begin= lip->tok_start; } sp_proc_stmt { @@ -2233,14 +2240,18 @@ sp_opt_default: sp_proc_stmt: { - LEX *lex= Lex; + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; - lex->sphead->reset_lex(YYTHD); - lex->sphead->m_tmp_query= lex->tok_start; + lex->sphead->reset_lex(thd); + lex->sphead->m_tmp_query= lip->tok_start; } statement { - LEX *lex= Lex; + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; sp_head *sp= lex->sphead; sp->m_flags|= sp_get_flags_for_command(lex); @@ -2267,15 +2278,15 @@ sp_proc_stmt: lex->tok_end otherwise. */ if (yychar == YYEMPTY) - i->m_query.length= lex->ptr - sp->m_tmp_query; + i->m_query.length= lip->ptr - sp->m_tmp_query; else - i->m_query.length= lex->tok_end - sp->m_tmp_query; - i->m_query.str= strmake_root(YYTHD->mem_root, - (char *)sp->m_tmp_query, + i->m_query.length= lip->tok_end - sp->m_tmp_query; + i->m_query.str= strmake_root(thd->mem_root, + sp->m_tmp_query, i->m_query.length); sp->add_instr(i); } - sp->restore_lex(YYTHD); + sp->restore_lex(thd); } | RETURN_SYM { Lex->sphead->reset_lex(YYTHD); } @@ -4428,26 +4439,36 @@ select_item_list: select_item: remember_name select_item2 remember_end select_alias { - if (add_item_to_list(YYTHD, $2)) + THD *thd= YYTHD; + DBUG_ASSERT($1 < $3); + + if (add_item_to_list(thd, $2)) MYSQL_YYABORT; if ($4.str) { $2->is_autogenerated_name= FALSE; $2->set_name($4.str, $4.length, system_charset_info); } - else if (!$2->name) { - char *str = $1; - if (str[-1] == '`') - str--; - $2->set_name(str,(uint) ($3 - str), YYTHD->charset()); + else if (!$2->name) + { + $2->set_name($1, (uint) ($3 - $1), thd->charset()); } }; + remember_name: - { $$=(char*) Lex->tok_start; }; + { + THD *thd= YYTHD; + Lex_input_stream *lip= thd->m_lip; + $$= (char*) lip->tok_start; + }; remember_end: - { $$=(char*) Lex->tok_end; }; + { + THD *thd= YYTHD; + Lex_input_stream *lip= thd->m_lip; + $$=(char*) lip->tok_end; + }; select_item2: table_wild { $$=$1; } /* table.* */ @@ -6292,12 +6313,14 @@ procedure_list2: procedure_item: remember_name expr { - LEX *lex= Lex; - if (add_proc_to_list(lex->thd, $2)) + THD *thd= YYTHD; + Lex_input_stream *lip= thd->m_lip; + + if (add_proc_to_list(thd, $2)) MYSQL_YYABORT; if (!$2->name) - $2->set_name($1,(uint) ((char*) lex->tok_end - $1), - YYTHD->charset()); + $2->set_name($1,(uint) ((char*) lip->tok_end - $1), + thd->charset()); } ; @@ -7337,13 +7360,16 @@ use: USE_SYM ident load: LOAD DATA_SYM { - LEX *lex=Lex; + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; + if (lex->sphead) { my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD DATA"); MYSQL_YYABORT; } - lex->fname_start= lex->ptr; + lex->fname_start= lip->ptr; } load_data {} @@ -7378,8 +7404,10 @@ load_data: } opt_duplicate INTO { - LEX *lex=Lex; - lex->fname_end= lex->ptr; + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; + lex->fname_end= lip->ptr; } TABLE_SYM table_ident { @@ -7559,15 +7587,16 @@ text_string: param_marker: PARAM_MARKER { - THD *thd=YYTHD; + THD *thd= YYTHD; LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; Item_param *item; if (! lex->parsing_options.allows_variable) { my_error(ER_VIEW_SELECT_VARIABLE, MYF(0)); MYSQL_YYABORT; } - item= new Item_param((uint) (lex->tok_start - (uchar *) thd->query)); + item= new Item_param((uint) (lip->tok_start - thd->query)); if (!($$= item) || lex->param_list.push_back(item)) { my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); @@ -7590,8 +7619,11 @@ signed_literal: literal: text_literal { $$ = $1; } | NUM_literal { $$ = $1; } - | NULL_SYM { $$ = new Item_null(); - Lex->next_state=MY_LEX_OPERATOR_OR_IDENT;} + | NULL_SYM + { + $$ = new Item_null(); + YYTHD->m_lip->next_state=MY_LEX_OPERATOR_OR_IDENT; + } | FALSE_SYM { $$= new Item_int((char*) "FALSE",0,1); } | TRUE_SYM { $$= new Item_int((char*) "TRUE",1,1); } | HEX_NUM { $$ = new Item_hex_string($1.str, $1.length);} @@ -7681,8 +7713,10 @@ order_ident: simple_ident: ident { + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; sp_variable_t *spv; - LEX *lex = Lex; sp_pcontext *spc = lex->spcont; if (spc && (spv = spc->find_variable(&$1))) { @@ -7695,7 +7729,7 @@ simple_ident: Item_splocal *splocal; splocal= new Item_splocal($1, spv->offset, spv->type, - lex->tok_start_prev - + lip->tok_start_prev - lex->sphead->m_tmp_query); #ifndef DBUG_OFF if (splocal) @@ -8291,7 +8325,11 @@ option_value_list: option_type_value: { - if (Lex->sphead) + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; + + if (lex->sphead) { /* If we are in SP we want have own LEX for each assignment. @@ -8303,9 +8341,8 @@ option_type_value: QQ: May be we should simply prohibit group assignments in SP? */ - LEX *lex; - Lex->sphead->reset_lex(YYTHD); - lex= Lex; + Lex->sphead->reset_lex(thd); + lex= thd->lex; /* Set new LEX as if we at start of set rule. */ lex->sql_command= SQLCOM_SET_OPTION; @@ -8313,12 +8350,14 @@ option_type_value: lex->option_type=OPT_SESSION; lex->var_list.empty(); lex->one_shot_set= 0; - lex->sphead->m_tmp_query= lex->tok_start; + lex->sphead->m_tmp_query= lip->tok_start; } } ext_option_value { - LEX *lex= Lex; + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; if (lex->sphead) { @@ -8340,24 +8379,24 @@ option_type_value: /* Extract the query statement from the tokenizer. The - end is either lex->ptr, if there was no lookahead, - lex->tok_end otherwise. + end is either lip->ptr, if there was no lookahead, + lip->tok_end otherwise. */ if (yychar == YYEMPTY) - qbuff.length= lex->ptr - sp->m_tmp_query; + qbuff.length= lip->ptr - sp->m_tmp_query; else - qbuff.length= lex->tok_end - sp->m_tmp_query; + qbuff.length= lip->tok_end - sp->m_tmp_query; - if (!(qbuff.str= alloc_root(YYTHD->mem_root, qbuff.length + 5))) + if (!(qbuff.str= alloc_root(thd->mem_root, qbuff.length + 5))) MYSQL_YYABORT; - strmake(strmake(qbuff.str, "SET ", 4), (char *)sp->m_tmp_query, + strmake(strmake(qbuff.str, "SET ", 4), sp->m_tmp_query, qbuff.length); qbuff.length+= 4; i->m_query= qbuff; sp->add_instr(i); } - lex->sphead->restore_lex(YYTHD); + lex->sphead->restore_lex(thd); } }; @@ -9615,7 +9654,9 @@ trigger_tail: TRIGGER_SYM remember_name sp_name trg_action_time trg_event ON remember_name table_ident FOR_SYM remember_name EACH_SYM ROW_SYM { - LEX *lex= Lex; + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; sp_head *sp; if (lex->sphead) @@ -9626,9 +9667,9 @@ trigger_tail: if (!(sp= new sp_head())) MYSQL_YYABORT; - sp->reset_thd_mem_root(YYTHD); + sp->reset_thd_mem_root(thd); sp->init(lex); - sp->init_sp_name(YYTHD, $3); + sp->init_sp_name(thd, $3); lex->stmt_definition_begin= $2; lex->ident.str= $7; @@ -9642,12 +9683,12 @@ trigger_tail: stored procedure, otherwise yylex will chop it into pieces at each ';'. */ - sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; - YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; + sp->m_old_cmq= thd->client_capabilities & CLIENT_MULTI_QUERIES; + thd->client_capabilities &= ~CLIENT_MULTI_QUERIES; bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->m_body_begin= lex->ptr; + lex->sphead->m_body_begin= lip->ptr; while (my_isspace(system_charset_info, lex->sphead->m_body_begin[0])) ++lex->sphead->m_body_begin; } @@ -9726,24 +9767,30 @@ sp_tail: } '(' { - LEX *lex= Lex; + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; - lex->sphead->m_param_begin= lex->tok_start+1; + lex->sphead->m_param_begin= lip->tok_start+1; } sp_pdparam_list ')' { - LEX *lex= Lex; + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; - lex->sphead->m_param_end= lex->tok_start; + lex->sphead->m_param_end= lip->tok_start; bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); } sp_c_chistics { - LEX *lex= Lex; + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->m_body_begin= lex->tok_start; + lex->sphead->m_body_begin= lip->tok_start; } sp_proc_stmt { |