From 3eff7d4dd5bba598fe055808697d630622fa249b Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 24 Apr 2007 09:24:21 -0600 Subject: [PATCH 1/5] Bug#25411 (trigger code truncated), PART I The issue found with bug 25411 is due to the function skip_rear_comments() which damages the source code while implementing a work around. The root cause of the problem is in the lexical analyser, which does not process special comments properly. For special comments like : [1] aaa /*!50000 bbb */ ccc since 5.0 is a version older that the current code, the parser is in lining the content of the special comment, so that the query to process is [2] aaa bbb ccc However, the text of the query captured when processing a stored procedure, stored function or trigger (or event in 5.1), can be after rebuilding it: [3] aaa bbb */ ccc which is wrong. To fix bug 25411 properly, the lexical analyser needs to return [2] when in lining special comments. In order to implement this, some preliminary cleanup is required in the code, which is implemented by this patch. Before this change, the structure named LEX (or st_lex) contains attributes that belong to lexical analysis, as well as attributes that represents the abstract syntax tree (AST) of a statement. Creating a new LEX structure for each statements (which makes sense for the AST part) also re-initialized the lexical analysis phase each time, which is conceptually wrong. With this patch, the previous st_lex structure has been split in two: - st_lex represents the Abstract Syntax Tree for a statement. The name "lex" has not been changed to avoid a bigger impact in the code base. - class lex_input_stream represents the internal state of the lexical analyser, which by definition should *not* be reinitialized when parsing multiple statements from the same input stream. This change is a pre-requisite for bug 25411, since the implementation of lex_input_stream will later improve to deal properly with special comments, and this processing can not be done with the current implementation of sp_head::reset_lex and sp_head::restore_lex, which interfere with the lexer. This change set alone does not fix bug 25411. sql/item_func.cc: Refactoring, separate lex_input_stream from st_lex. sql/log_event.cc: Refactoring, separate lex_input_stream from st_lex. sql/mysql_priv.h: Refactoring, separate lex_input_stream from st_lex. sql/slave.cc: Refactoring, separate lex_input_stream from st_lex. sql/sp.cc: Refactoring, separate lex_input_stream from st_lex. sql/sp_head.cc: Refactoring, separate lex_input_stream from st_lex. sql/sp_head.h: Refactoring, separate lex_input_stream from st_lex. sql/sql_class.cc: Refactoring, separate lex_input_stream from st_lex. sql/sql_class.h: Refactoring, separate lex_input_stream from st_lex. sql/sql_lex.cc: Refactoring, separate lex_input_stream from st_lex. sql/sql_lex.h: Refactoring, separate lex_input_stream from st_lex. sql/sql_parse.cc: Refactoring, separate lex_input_stream from st_lex. sql/sql_prepare.cc: Refactoring, separate lex_input_stream from st_lex. sql/sql_trigger.cc: Refactoring, separate lex_input_stream from st_lex. sql/sql_view.cc: Refactoring, separate lex_input_stream from st_lex. sql/sql_yacc.yy: Refactoring, separate lex_input_stream from st_lex. --- mysql-test/r/bdb_notembedded.result | 35 ++++ mysql-test/t/bdb_notembedded.test | 38 ++++ sql/item_func.cc | 2 +- sql/log_event.cc | 9 +- sql/mysql_priv.h | 6 +- sql/slave.cc | 3 +- sql/sp.cc | 9 +- sql/sp_head.cc | 31 +-- sql/sp_head.h | 6 +- sql/sql_class.cc | 2 +- sql/sql_class.h | 10 + sql/sql_lex.cc | 299 +++++++++++++++------------- sql/sql_lex.h | 70 +++++-- sql/sql_parse.cc | 103 +++++++--- sql/sql_prepare.cc | 8 +- sql/sql_trigger.cc | 8 +- sql/sql_view.cc | 14 +- sql/sql_yacc.yy | 171 ++++++++++------ 18 files changed, 530 insertions(+), 294 deletions(-) create mode 100644 mysql-test/r/bdb_notembedded.result create mode 100644 mysql-test/t/bdb_notembedded.test diff --git a/mysql-test/r/bdb_notembedded.result b/mysql-test/r/bdb_notembedded.result new file mode 100644 index 00000000000..14cb5fad915 --- /dev/null +++ b/mysql-test/r/bdb_notembedded.result @@ -0,0 +1,35 @@ +set autocommit=1; +reset master; +create table bug16206 (a int); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +show binlog events; +Log_name Pos Event_type Server_id End_log_pos Info +f n Format_desc 1 n Server ver: VERSION, Binlog ver: 4 +f n Query 1 n use `test`; create table bug16206 (a int) +f n Query 1 n use `test`; insert into bug16206 values(1) +f n Query 1 n use `test`; insert into bug16206 values(2) +drop table bug16206; +reset master; +create table bug16206 (a int) engine= bdb; +insert into bug16206 values(0); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +insert into bug16206 values(3); +show binlog events; +Log_name Pos Event_type Server_id End_log_pos Info +f n Format_desc 1 n Server ver: VERSION, Binlog ver: 4 +f n Query 1 n use `test`; create table bug16206 (a int) engine= bdb +f n Query 1 n use `test`; insert into bug16206 values(0) +f n Query 1 n use `test`; insert into bug16206 values(1) +f n Query 1 n use `test`; BEGIN +f n Query 1 n use `test`; insert into bug16206 values(2) +f n Query 1 n use `test`; COMMIT +f n Query 1 n use `test`; insert into bug16206 values(3) +drop table bug16206; +set autocommit=0; +End of 5.0 tests diff --git a/mysql-test/t/bdb_notembedded.test b/mysql-test/t/bdb_notembedded.test new file mode 100644 index 00000000000..24e64ebbfb2 --- /dev/null +++ b/mysql-test/t/bdb_notembedded.test @@ -0,0 +1,38 @@ +-- source include/not_embedded.inc +-- source include/have_bdb.inc + +# +# Bug #16206: Superfluous COMMIT event in binlog when updating BDB in autocommit mode +# +set autocommit=1; + +let $VERSION=`select version()`; + +reset master; +create table bug16206 (a int); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +--replace_result $VERSION VERSION +--replace_column 1 f 2 n 5 n +show binlog events; +drop table bug16206; + +reset master; +create table bug16206 (a int) engine= bdb; +insert into bug16206 values(0); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +insert into bug16206 values(3); +--replace_result $VERSION VERSION +--replace_column 1 f 2 n 5 n +show binlog events; +drop table bug16206; + +set autocommit=0; + + +--echo End of 5.0 tests diff --git a/sql/item_func.cc b/sql/item_func.cc index d972bde9155..7e4a06a1805 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -4272,7 +4272,7 @@ int get_var_with_binlog(THD *thd, enum_sql_command sql_command, List 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 de567eacbeb..4017457b644 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -697,13 +697,15 @@ bool do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name, char *new_table_alias, bool skip_error); bool mysql_change_db(THD *thd,const char *name,bool no_access_check); -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 ba8c3ff902a..2b07f28b167 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 c8701aa2c87..a17ac9f30aa 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 21225d82188..548a6c903dc 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 a94f903a47b..0d8f2ca6394 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 99803802001..d7091b5a3bd 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 }; @@ -1492,6 +1493,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..6fe4bfbd4f1 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,27 @@ 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) +{ +} + +Lex_input_stream::~Lex_input_stream() +{} + /* This is called before every query that is to be parsed. @@ -115,14 +136,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 +174,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; @@ -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 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, tmp.length); return tmp; } @@ -261,14 +278,15 @@ 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 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 ; + tmp.length=lip->yytoklen=length; + tmp.str=(char*) lip->m_thd->alloc(tmp.length+1); + for (from= (byte*) lip->tok_start, to= (byte*) tmp.str, end= to+length ; to != end ; ) { @@ -285,15 +303,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 +318,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 +344,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 +375,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 +412,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 +525,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 +550,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 +571,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 +595,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 +618,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 +626,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 +640,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,8 +654,8 @@ 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; + length= (uint) (lip->ptr - lip->tok_start)-1; + start= lip->ptr; if (lex->ignore_space) { /* @@ -644,19 +664,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, length); /* Note: "SELECT _bla AS 'alias'" @@ -673,12 +693,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 +718,36 @@ 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, 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=get_token(lip, yyLength()); yylval->lex_str.str+=2; // Skip 0x yylval->lex_str.length-=2; - lex->yytoklen-=2; + lip->yytoklen-=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= get_token(lip, yyLength()); yylval->lex_str.str+= 2; // Skip 0x yylval->lex_str.length-= 2; - lex->yytoklen-= 2; + lip->yytoklen-= 2; return (BIN_NUM); } yyUnget(); @@ -745,10 +765,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 +780,16 @@ 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, 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 ` + lip->tok_start=lip->ptr; // Skip first ` while ((c=yyGet())) { int var_length; @@ -789,23 +809,23 @@ 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, yyLength() - double_quotes, quote_char); else - yylval->lex_str=get_token(lex,yyLength()); + yylval->lex_str=get_token(lip, yyLength()); 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 if (c != '.') { // Found complete integer number. - yylval->lex_str=get_token(lex,yyLength()); + yylval->lex_str=get_token(lip, yyLength()); return int_token(yylval->lex_str.str,yylval->lex_str.length); } // fall through @@ -823,47 +843,47 @@ 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, yyLength()); return(FLOAT_NUM); } - yylval->lex_str=get_token(lex,yyLength()); + yylval->lex_str=get_token(lip, 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=get_token(lip, length); yylval->lex_str.str+=2; // Skip x' yylval->lex_str.length-=3; // Don't count x' and last ' - lex->yytoklen-=3; + lip->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 + 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= get_token(lip, length); yylval->lex_str.str+= 2; // Skip b' yylval->lex_str.length-= 3; // Don't count b' and last ' - lex->yytoklen-= 3; + lip->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) 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 +897,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 +912,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 +953,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 +961,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 +992,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 +1006,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 +1034,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, 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 +1070,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, length); return(result_state); } } @@ -1093,7 +1112,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 de7de0d46e9..8a9bc62a83e 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -469,7 +469,7 @@ public: void set_limit(st_select_lex *values); void set_thd(THD *thd_arg) { thd= thd_arg; } - friend void lex_start(THD *thd, uchar *buf, uint length); + friend void lex_start(THD *thd); friend int subselect_union_engine::exec(); List *get_unit_column_types(); @@ -675,7 +675,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() { @@ -898,30 +898,70 @@ 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; +}; + + /* 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; @@ -990,7 +1030,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; @@ -1101,8 +1140,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(); @@ -1211,7 +1251,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 1b8bfd38fc4..36ae5ca4a31 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1236,6 +1236,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; @@ -1312,7 +1313,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) @@ -1789,17 +1790,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 @@ -1824,7 +1827,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)) @@ -1845,7 +1848,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); @@ -1882,7 +1885,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); @@ -5687,20 +5693,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. @@ -5887,21 +5879,54 @@ 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 the thd and thd->lex structures, which happen to be implemented + by: + - 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_input_stream lip(thd, inBuf, length); + thd->m_lip= &lip; + 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) + + 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 && @@ -5924,8 +5949,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); @@ -5952,6 +5977,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; } @@ -5972,8 +6003,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(); @@ -7032,8 +7068,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 85092f14624..ab26fc4e85d 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 83beec3d1be..5ec32ac3e79 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -761,8 +761,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; @@ -973,10 +973,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..82d148c4a1e 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); } @@ -4444,10 +4455,18 @@ select_item: }; 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 +6311,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 +7358,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 +7402,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 +7585,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 +7617,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 +7711,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 +7727,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 +8323,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 +8339,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 +8348,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 +8377,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 +9652,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 +9665,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 +9681,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 +9765,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 { From c49e378ac4a690220d86c11ef40f2382848c6d30 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 25 Apr 2007 11:38:11 -0600 Subject: [PATCH 2/5] code review fix --- sql/sql_lex.cc | 6 +++--- sql/sql_lex.h | 5 ++++- sql/sql_parse.cc | 9 +++++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 95b9a77d411..ecdbb654ffd 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -122,7 +122,8 @@ Lex_input_stream::Lex_input_stream(THD *thd, tok_start_prev(NULL), buf(buffer), next_state(MY_LEX_START), - found_semicolon(NULL) + found_semicolon(NULL), + ignore_space(test(thd->variables.sql_mode & MODE_IGNORE_SPACE)) { } @@ -192,7 +193,6 @@ void lex_start(THD *thd) 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; @@ -656,7 +656,7 @@ int MYSQLlex(void *arg, void *yythd) } length= (uint) (lip->ptr - lip->tok_start)-1; start= lip->ptr; - if (lex->ignore_space) + if (lip->ignore_space) { /* If we find a space then this can't be an identifier. We notice this diff --git a/sql/sql_lex.h b/sql/sql_lex.h index d580b2016ae..d07de73d5b9 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -951,6 +951,9 @@ public: /** Position of ';' in the stream, to delimit multiple queries. */ const char* found_semicolon; + + /** SQL_MODE = IGNORE_SPACE. */ + bool ignore_space; }; @@ -1069,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. diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 0505f0d3574..22689a3dfa1 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5990,16 +5990,14 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, - 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 the thd and thd->lex structures, which happen to be implemented - by: + 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_input_stream lip(thd, inBuf, length); - thd->m_lip= &lip; lex_start(thd); mysql_reset_thd_for_next_command(thd); @@ -6010,6 +6008,9 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, sp_cache_flush_obsolete(&thd->sp_proc_cache); sp_cache_flush_obsolete(&thd->sp_func_cache); + Lex_input_stream lip(thd, inBuf, length); + thd->m_lip= &lip; + int err= MYSQLparse(thd); *found_semicolon= lip.found_semicolon; From 034c11f3cdac8b2b39dd8593d1fbad5b92f98879 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 25 Apr 2007 21:38:12 -0600 Subject: [PATCH 3/5] manual merge 5.0-runtime -> 5.1->runtime, with 25411 part I --- sql/event_data_objects.cc | 31 ++-- sql/ha_ndbcluster.cc | 2 +- sql/ha_ndbcluster_binlog.cc | 3 +- sql/log_event.cc | 5 +- sql/mysql_priv.h | 6 +- sql/slave.cc | 3 +- sql/sp.cc | 9 +- sql/sp_head.cc | 27 +--- sql/sp_head.h | 4 +- sql/sql_class.h | 11 ++ sql/sql_lex.cc | 284 +++++++++++++++++++----------------- sql/sql_lex.h | 71 +++++++-- sql/sql_parse.cc | 103 ++++++++----- sql/sql_partition.cc | 6 +- sql/sql_prepare.cc | 10 +- sql/sql_trigger.cc | 10 +- sql/sql_view.cc | 10 +- sql/sql_yacc.yy | 184 ++++++++++++++--------- 18 files changed, 479 insertions(+), 300 deletions(-) diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index f5847e603e8..4b788a8f7d8 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -156,10 +156,14 @@ void Event_parse_data::init_body(THD *thd) { DBUG_ENTER("Event_parse_data::init_body"); - DBUG_PRINT("info", ("body: '%s' body_begin: 0x%lx end: 0x%lx", body_begin, - (long) body_begin, (long) thd->lex->ptr)); - body.length= thd->lex->ptr - body_begin; + /* This method is called from within the parser, from sql_yacc.yy */ + DBUG_ASSERT(thd->m_lip != NULL); + + DBUG_PRINT("info", ("body: '%s' body_begin: 0x%lx end: 0x%lx", body_begin, + (long) body_begin, (long) thd->m_lip->ptr)); + + body.length= thd->m_lip->ptr - body_begin; const char *body_end= body_begin + body.length - 1; /* Trim nuls or close-comments ('*'+'/') or spaces at the end */ @@ -1912,15 +1916,20 @@ Event_job_data::execute(THD *thd, bool drop) thd->query= sp_sql.c_ptr_safe(); thd->query_length= sp_sql.length(); - lex_start(thd, thd->query, thd->query_length); - - if (MYSQLparse(thd) || thd->is_fatal_error) { - sql_print_error("Event Scheduler: " - "%serror during compilation of %s.%s", - thd->is_fatal_error ? "fatal " : "", - (const char *) dbname.str, (const char *) name.str); - goto end; + Lex_input_stream lip(thd, thd->query, thd->query_length); + thd->m_lip= &lip; + lex_start(thd); + int err= MYSQLparse(thd); + + if (err || thd->is_fatal_error) + { + sql_print_error("Event Scheduler: " + "%serror during compilation of %s.%s", + thd->is_fatal_error ? "fatal " : "", + (const char *) dbname.str, (const char *) name.str); + goto end; + } } { diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 58d34d99174..d1e6ae56cb2 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -6564,7 +6564,7 @@ int ndb_create_table_from_engine(THD *thd, const char *db, LEX *old_lex= thd->lex, newlex; thd->lex= &newlex; newlex.current_select= NULL; - lex_start(thd, "", 0); + lex_start(thd); int res= ha_create_table_from_engine(thd, db, table_name); thd->lex= old_lex; return res; diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index c65c81c088c..a7c2d1f51e9 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -230,6 +230,7 @@ static void run_query(THD *thd, char *buf, char *end, ulonglong save_thd_options= thd->options; DBUG_ASSERT(sizeof(save_thd_options) == sizeof(thd->options)); NET save_net= thd->net; + const char* found_semicolon= NULL; bzero((char*) &thd->net, sizeof(NET)); thd->query_length= end - buf; @@ -239,7 +240,7 @@ static void run_query(THD *thd, char *buf, char *end, thd->options&= ~OPTION_BIN_LOG; DBUG_PRINT("query", ("%s", thd->query)); - mysql_parse(thd, thd->query, thd->query_length); + mysql_parse(thd, thd->query, thd->query_length, &found_semicolon); if (print_error && thd->query_error) { diff --git a/sql/log_event.cc b/sql/log_event.cc index 048db4ac95d..591a91f2011 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -5863,7 +5863,7 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli) Table_map_log_event::do_apply_event() we don't call mysql_init_query() as that may reset the binlog format. */ - lex_start(thd, NULL, 0); + lex_start(thd); while ((error= lock_tables(thd, rli->tables_to_lock, rli->tables_to_lock_count, &need_reopen))) @@ -6513,7 +6513,8 @@ int Table_map_log_event::do_apply_event(RELAY_LOG_INFO const *rli) initialized, so we should call lex_start(); to be even safer, we call mysql_init_query() which does a more complete set of inits. */ - mysql_init_query(thd, NULL, 0); + lex_start(thd); + mysql_reset_thd_for_next_command(thd); /* Check if the slave is set to use SBR. If so, it should switch to using RBR until the end of the "statement", i.e., next diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 3a01ed27f61..d9386c9f4d7 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -838,13 +838,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, const char *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 f1b9ea8476b..f89a56e2087 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -923,6 +923,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 @@ -974,7 +975,7 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, thd->db = (char*)db; DBUG_ASSERT(thd->db != 0); thd->db_length= strlen(thd->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 49b8b304e76..0a99be3f970 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -384,10 +384,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, 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 04a7b2574a4..89fcd80abcf 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -557,6 +557,7 @@ sp_head::init_strings(THD *thd, LEX *lex) 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) { @@ -565,7 +566,7 @@ 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. @@ -574,8 +575,8 @@ sp_head::init_strings(THD *thd, LEX *lex) m_body.length= endp - m_body_begin; m_body.str= strmake_root(root, m_body_begin, m_body.length); - m_defstr.length= endp - lex->buf; - m_defstr.str= strmake_root(root, lex->buf, m_defstr.length); + m_defstr.length= endp - lip->buf; + m_defstr.str= strmake_root(root, lip->buf, m_defstr.length); DBUG_VOID_RETURN; } @@ -1815,25 +1816,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->tok_end= oldlex->tok_end; - sublex->yylineno= oldlex->yylineno; /* And keep the SP stuff too */ sublex->sphead= oldlex->sphead; sublex->spcont= oldlex->spcont; @@ -1866,10 +1855,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->tok_end= sublex->tok_end; - 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 551707fa7bd..51ca529bed0 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -176,7 +176,9 @@ public: */ HASH m_sroutines; // Pointers set during parsing - const char *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.h b/sql/sql_class.h index 48a88087fd3..fd88df4bcc8 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -31,6 +31,7 @@ class Load_log_event; class Slave_log_event; class sp_rcontext; class sp_cache; +class Lex_input_stream; class Rows_log_event; enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE }; @@ -1423,6 +1424,16 @@ 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; + #ifdef WITH_PARTITION_STORAGE_ENGINE partition_info *work_part_info; #endif diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 4a162478388..24c4f42b32a 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -33,13 +33,13 @@ sys_var *trg_new_row_fake_var= (sys_var*) 0x01; /* Macros to look like lex */ -#define yyGet() ((uchar) *(lex->ptr++)) -#define yyGetLast() ((uchar) lex->ptr[-1]) -#define yyPeek() ((uchar) lex->ptr[0]) -#define yyPeek2() ((uchar) lex->ptr[1]) -#define yyUnget() lex->ptr-- -#define yySkip() lex->ptr++ -#define yyLength() ((uint) (lex->ptr - lex->tok_start)-1) +#define yyGet() ((uchar) *(lip->ptr++)) +#define yyGetLast() ((uchar) lip->ptr[-1]) +#define yyPeek() ((uchar) lip->ptr[0]) +#define yyPeek2() ((uchar) 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 @@ -120,6 +120,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. @@ -127,14 +149,12 @@ st_parsing_options::reset() (We already do too much here) */ -void lex_start(THD *thd, const char *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(); @@ -167,14 +187,11 @@ void lex_start(THD *thd, const char *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->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->part_info= 0; @@ -184,7 +201,6 @@ void lex_start(THD *thd, const char *buf, uint length) lex->select_lex.ftfunc_list= &lex->select_lex.ftfunc_list_alloc; lex->select_lex.group_list.empty(); lex->select_lex.order_list.empty(); - lex->ignore_space=test(thd->variables.sql_mode & MODE_IGNORE_SPACE); lex->sql_command= SQLCOM_END; lex->duplicates= DUP_ERROR; lex->ignore= 0; @@ -236,24 +252,24 @@ 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) { - const char *tok= lex->tok_start; + const char *tok= lip->tok_start; 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; } return 0; @@ -286,12 +302,12 @@ bool is_lex_native_function(const LEX_STRING *name) /* 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 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, tmp.length); return tmp; } @@ -302,15 +318,16 @@ 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 length, char quote) { LEX_STRING tmp; const char *from, *end; char *to; yyUnget(); // ptr points now after last token char - tmp.length=lex->yytoklen=length; - tmp.str=(char*) lex->thd->alloc(tmp.length+1); - for (from= lex->tok_start, to= tmp.str, end= to+length ; + tmp.length= lip->yytoklen=length; + tmp.str=(char*) lip->m_thd->alloc(tmp.length+1); + for (from= lip->tok_start, to= tmp.str, end= to+length ; to != end ; ) { @@ -327,31 +344,31 @@ 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 - while (lex->ptr != lex->end_of_query) + while (lip->ptr != lip->end_of_query) { c = yyGet(); #ifdef USE_MB { int l; if (use_mb(cs) && - (l = my_ismbchar(cs, lex->ptr-1, lex->end_of_query))) { - lex->ptr += l-1; + (l = my_ismbchar(cs, 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(); } @@ -369,15 +386,15 @@ static char *get_text(LEX *lex) const char *str, *end; char *start; - str=lex->tok_start+1; - end=lex->ptr-1; - if (!(start= (char*) 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 { @@ -395,7 +412,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) { @@ -432,7 +449,7 @@ static char *get_text(LEX *lex) *to++ = *str; } *to=0; - lex->yytoklen=(uint) (to-start); + lip->yytoklen=(uint) (to-start); } return start; } @@ -545,19 +562,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_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 (;;) { @@ -568,9 +587,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: @@ -589,13 +608,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 @@ -613,14 +632,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: @@ -643,21 +662,21 @@ int MYSQLlex(void *arg, void *yythd) result_state= IDENT_QUOTED; if (my_mbcharlen(cs, yyGetLast()) > 1) { - int l = my_ismbchar(cs, lex->ptr-1, lex->end_of_query); + int l = my_ismbchar(cs, 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()]) { if (my_mbcharlen(cs, c) > 1) { int l; - if ((l = my_ismbchar(cs, lex->ptr-1, lex->end_of_query)) == 0) + if ((l = my_ismbchar(cs, lip->ptr-1, lip->end_of_query)) == 0) break; - lex->ptr += l-1; + lip->ptr += l-1; } } } @@ -668,9 +687,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 @@ -678,19 +697,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, length); /* Note: "SELECT _bla AS 'alias'" @@ -707,12 +726,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 @@ -732,36 +751,36 @@ 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, 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=get_token(lip, yyLength()); yylval->lex_str.str+=2; // Skip 0x yylval->lex_str.length-=2; - lex->yytoklen-=2; + lip->yytoklen-=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= get_token(lip, yyLength()); yylval->lex_str.str+= 2; // Skip 0x yylval->lex_str.length-= 2; - lex->yytoklen-= 2; + lip->yytoklen-= 2; return (BIN_NUM); } yyUnget(); @@ -778,9 +797,9 @@ int MYSQLlex(void *arg, void *yythd) if (my_mbcharlen(cs, c) > 1) { int l; - if ((l = my_ismbchar(cs, lex->ptr-1, lex->end_of_query)) == 0) + if ((l = my_ismbchar(cs, lip->ptr-1, lip->end_of_query)) == 0) break; - lex->ptr += l-1; + lip->ptr += l-1; } } } @@ -792,16 +811,16 @@ 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, 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 ` + lip->tok_start=lip->ptr; // Skip first ` while ((c=yyGet())) { int var_length; @@ -819,23 +838,23 @@ 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, yyLength() - double_quotes, quote_char); else - yylval->lex_str=get_token(lex,yyLength()); + yylval->lex_str=get_token(lip, yyLength()); 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 if (c != '.') { // Found complete integer number. - yylval->lex_str=get_token(lex,yyLength()); + yylval->lex_str=get_token(lip, yyLength()); return int_token(yylval->lex_str.str,yylval->lex_str.length); } // fall through @@ -853,47 +872,47 @@ 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, yyLength()); return(FLOAT_NUM); } - yylval->lex_str=get_token(lex,yyLength()); + yylval->lex_str=get_token(lip, 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=get_token(lip, length); yylval->lex_str.str+=2; // Skip x' yylval->lex_str.length-=3; // Don't count x' and last ' - lex->yytoklen-=3; + lip->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 + 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= get_token(lip, length); yylval->lex_str.str+= 2; // Skip b' yylval->lex_str.length-= 3; // Don't count b' and last ' - lex->yytoklen-= 3; + lip->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) 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 @@ -907,9 +926,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 @@ -922,24 +941,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 @@ -963,7 +982,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) { @@ -971,13 +990,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; @@ -1002,14 +1021,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 ';' @@ -1017,15 +1035,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 */ @@ -1045,26 +1063,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, 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); @@ -1081,16 +1099,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, length); return(result_state); } } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 1175776ffd3..278a59fe1ac 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -543,7 +543,7 @@ public: void set_thd(THD *thd_arg) { thd= thd_arg; } inline bool is_union (); - friend void lex_start(THD *thd, const char *buf, uint length); + friend void lex_start(THD *thd); friend int subselect_union_engine::exec(); List *get_unit_column_types(); @@ -744,7 +744,7 @@ public: void cut_subtree() { slave= 0; } bool test_limit(); - friend void lex_start(THD *thd, const char *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() { @@ -992,23 +992,67 @@ 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; - const char *buf; /* The beginning of string, used by SPs */ - const char *ptr,*tok_start,*tok_end,*end_of_query; - - /* The value of tok_start as they were one call of MYSQLlex before */ - const char *tok_start_prev; char *length,*dec,*change; LEX_STRING name; @@ -1017,7 +1061,6 @@ typedef struct st_lex : public Query_tables_list 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; @@ -1101,7 +1144,6 @@ typedef struct st_lex : public Query_tables_list 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; @@ -1133,7 +1175,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. @@ -1210,7 +1252,8 @@ 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). */ - const char *fname_start, *fname_end; + const char *fname_start; + const char *fname_end; /* Reference to a struct that contains information in various commands @@ -1327,7 +1370,7 @@ struct st_lex_local: public st_lex extern void lex_init(void); extern void lex_free(void); -extern void lex_start(THD *thd, const char *buf, uint length); +extern void lex_start(THD *thd); extern void lex_end(LEX *lex); extern int MYSQLlex(void *arg, void *yythd); extern const char *skip_rear_comments(const char *ubegin, const char *uend); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index df2ac4d72fa..d2f9d31a495 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -300,6 +300,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; @@ -375,7 +376,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) @@ -889,17 +890,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; + general_log_print(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 @@ -924,7 +927,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)) @@ -946,7 +949,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, uint dummy; /* used as fields initializator */ - lex_start(thd, 0, 0); + lex_start(thd); statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS], &LOCK_status); @@ -985,7 +988,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, "", 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); @@ -5038,20 +5044,6 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize) } -/**************************************************************************** - Initialize global thd variables needed for query -****************************************************************************/ - -void -mysql_init_query(THD *thd, const char *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. @@ -5263,22 +5255,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, inBuf, length); + /* + 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, inBuf, length) <= 0) + 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 && @@ -5301,8 +5326,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); @@ -5329,6 +5354,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; } @@ -5349,8 +5380,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, 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(); @@ -6401,8 +6437,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_partition.cc b/sql/sql_partition.cc index d445c8bfbe0..47260b945fc 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -3718,7 +3718,11 @@ bool mysql_unpack_partition(THD *thd, thd->lex= &lex; thd->variables.character_set_client= system_charset_info; - lex_start(thd, part_buf, part_info_len); + + Lex_input_stream lip(thd, part_buf, part_info_len); + thd->m_lip= &lip; + + lex_start(thd); /* We need to use the current SELECT_LEX since I need to keep the Name_resolution_context object which is referenced from the diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 31a6c7af04a..ca1b29ea57d 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2850,10 +2850,14 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) old_stmt_arena= thd->stmt_arena; thd->stmt_arena= this; - lex_start(thd, thd->query, thd->query_length); - lex->stmt_prepare_mode= TRUE; - error= MYSQLparse((void *)thd) || thd->is_fatal_error || + Lex_input_stream lip(thd, thd->query, thd->query_length); + thd->m_lip= &lip; + lex_start(thd); + lex->stmt_prepare_mode= TRUE; + int err= MYSQLparse((void *)thd); + + 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 7b4deba527a..89ac444efa9 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -976,10 +976,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, trg_create_str->str, trg_create_str->length); - thd->spcont= 0; - if (MYSQLparse((void *)thd) || thd->is_fatal_error) + Lex_input_stream lip(thd, trg_create_str->str, trg_create_str->length); + thd->m_lip= &lip; + lex_start(thd); + thd->spcont= 0; + 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 453cbbc091e..5eaac8acc77 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -985,10 +985,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, 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 0c7d2fc2187..61d78ae831a 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -104,12 +104,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); } /** @@ -1837,7 +1838,9 @@ opt_ev_comment: /* empty */ { $$= 0; } ev_sql_stmt: { - LEX *lex= Lex; + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; /* This stops the following : @@ -1874,22 +1877,23 @@ ev_sql_stmt: 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; - Lex->event_parse_data->body_begin= lex->ptr; + lex->event_parse_data->body_begin= lip->ptr; } ev_sql_stmt_inner { - LEX *lex=Lex; + THD *thd= YYTHD; + LEX *lex= thd->lex; /* return back to the original memory root ASAP */ - lex->sphead->init_strings(YYTHD, lex); - lex->sphead->restore_thd_mem_root(YYTHD); + lex->sphead->init_strings(thd, lex); + lex->sphead->restore_thd_mem_root(thd); lex->sp_chistics.suid= SP_IS_SUID; //always the definer! - Lex->event_parse_data->init_body(YYTHD); + lex->event_parse_data->init_body(thd); } ; @@ -1983,7 +1987,9 @@ create_function_tail: } | '(' { - LEX *lex= Lex; + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; sp_head *sp; /* @@ -2003,9 +2009,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; @@ -2014,15 +2020,17 @@ create_function_tail: stored procedure, otherwise yylex will chop it into pieces at each ';'. */ - $$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; - YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; - lex->sphead->m_param_begin= lex->tok_start+1; + $$= 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 { @@ -2057,10 +2065,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 { @@ -2666,14 +2676,18 @@ sp_proc_stmt_if: sp_proc_stmt_statement: { - 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); @@ -2700,15 +2714,15 @@ sp_proc_stmt_statement: 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, + 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); } ; @@ -6212,10 +6226,18 @@ select_item: }; 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.* */ @@ -7983,12 +8005,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()); } ; @@ -9078,13 +9102,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 {} @@ -9118,8 +9145,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 { @@ -9300,14 +9329,15 @@ param_marker: PARAM_MARKER { THD *thd= YYTHD; - LEX *lex= thd->lex; + 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 - 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)); @@ -9330,8 +9360,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);} @@ -9421,8 +9454,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))) { @@ -9435,7 +9470,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) @@ -10085,7 +10120,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. @@ -10097,9 +10136,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; @@ -10107,12 +10145,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) { @@ -10134,15 +10174,15 @@ 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), sp->m_tmp_query, @@ -10151,7 +10191,7 @@ option_type_value: i->m_query= qbuff; sp->add_instr(i); } - lex->sphead->restore_lex(YYTHD); + lex->sphead->restore_lex(thd); } }; @@ -11400,7 +11440,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) @@ -11411,9 +11453,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; lex->ident.length= $10 - $7; @@ -11426,12 +11468,12 @@ trigger_tail: stored procedure, otherwise yylex will chop it into pieces at each ';'. */ - $$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; - YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; + $$= 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; } @@ -11510,24 +11552,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 { From 1700feaa0296250b10882bd421f555776614120c Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 27 Apr 2007 17:14:25 -0600 Subject: [PATCH 4/5] Bug#21513 (SP having body starting with quoted label rendered unusable) Before this fix, the parser would sometime change where a token starts by altering Lex_input_string::tok_start, which later confused the code in sql_yacc.yy that needs to capture the source code of a SQL statement, like to represent the body of a stored procedure. This line of code in sql_lex.cc : case MY_LEX_USER_VARIABLE_DELIMITER: lip->tok_start= lip->ptr; // Skip first ` would ... and cause the bug reported. In general, the responsibility of sql_lex.cc is to *find* where token are in the SQL text, but is *not* to make up fake or incomplete tokens. With a quoted label like `my_label`, the token starts on the first quote. Extracting the token value should not change that (it did). With this fix, the lexical analysis has been cleaned up to not change lip->tok_start (in the case found for this bug). The functions get_token() and get_quoted_token() now have an extra parameters, used when some characters from the beginning of the token need to be skipped when extracting a token value, like when extracting 'AB' from '0xAB', for example, for a HEX_NUM token. This exposed a bad assumption in Item_hex_string and Item_bin_string, which has been fixed: The assumption was that the string given, 'AB', was in fact preceded in memory by '0x', which might be false (it can be preceded by "x'" and followed by "'" -- or not be preceded by valid memory at all) If a name is needed for Item_hex_string or Item_bin_string, the name is taken from the original and true source code ('0xAB'), and assigned in the select_item rule, instead of relying on assumptions related to how memory is used. mysql-test/r/sp.result: Lex_input_stream::tok_start must point at the real start of a token. mysql-test/t/sp.test: Lex_input_stream::tok_start must point at the real start of a token. sql/item.cc: Lex_input_stream::tok_start must point at the real start of a token. sql/sql_lex.cc: Lex_input_stream::tok_start must point at the real start of a token. sql/sql_yacc.yy: Lex_input_stream::tok_start must point at the real start of a token. --- mysql-test/r/sp.result | 7 +++++ mysql-test/t/sp.test | 11 ++++++++ sql/item.cc | 2 -- sql/sql_lex.cc | 64 ++++++++++++++++++++---------------------- sql/sql_yacc.yy | 14 +++++---- 5 files changed, 56 insertions(+), 42 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 64876ae0584..97677838d48 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 ed5bd67d096..a2a7d46edc3 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -4602,7 +4602,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) @@ -4713,7 +4712,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/sql_lex.cc b/sql/sql_lex.cc index ecdbb654ffd..fabd9a0e003 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -262,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_input_stream *lip, 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=lip->yytoklen=length; - tmp.str= lip->m_thd->strmake(lip->tok_start, tmp.length); + tmp.str= lip->m_thd->strmake(lip->tok_start + skip, tmp.length); return tmp; } @@ -279,6 +279,7 @@ static LEX_STRING get_token(Lex_input_stream *lip, uint length) */ static LEX_STRING get_quoted_token(Lex_input_stream *lip, + uint skip, uint length, char quote) { LEX_STRING tmp; @@ -286,9 +287,10 @@ static LEX_STRING get_quoted_token(Lex_input_stream *lip, yyUnget(); // ptr points now after last token char tmp.length=lip->yytoklen=length; tmp.str=(char*) lip->m_thd->alloc(tmp.length+1); - for (from= (byte*) lip->tok_start, to= (byte*) tmp.str, end= to+length ; - to != end ; - ) + from= (byte*) lip->tok_start + skip; + to= (byte*) tmp.str; + end= to+length; + for ( ; to != end; ) { if ((*to++= *from++) == quote) from++; // Skip double quotes @@ -676,7 +678,7 @@ int MYSQLlex(void *arg, void *yythd) } yySkip(); // next state does a unget } - yylval->lex_str=get_token(lip, length); + yylval->lex_str=get_token(lip, 0, length); /* Note: "SELECT _bla AS 'alias'" @@ -718,7 +720,7 @@ int MYSQLlex(void *arg, void *yythd) { yySkip(); while (my_isdigit(cs,yyGet())) ; - yylval->lex_str=get_token(lip, yyLength()); + yylval->lex_str=get_token(lip, 0, yyLength()); return(FLOAT_NUM); } } @@ -730,10 +732,8 @@ int MYSQLlex(void *arg, void *yythd) while (my_isxdigit(cs,(c = yyGet()))) ; if ((lip->ptr - lip->tok_start) >= 4 && !ident_map[c]) { - yylval->lex_str=get_token(lip, yyLength()); - yylval->lex_str.str+=2; // Skip 0x - yylval->lex_str.length-=2; - lip->yytoklen-=2; + /* skip '0x' */ + yylval->lex_str=get_token(lip, 2, yyLength()-2); return (HEX_NUM); } yyUnget(); @@ -744,10 +744,8 @@ int MYSQLlex(void *arg, void *yythd) while (my_isxdigit(cs,(c = yyGet()))) ; if ((lip->ptr - lip->tok_start) >= 4 && !ident_map[c]) { - yylval->lex_str= get_token(lip, yyLength()); - yylval->lex_str.str+= 2; // Skip 0x - yylval->lex_str.length-= 2; - lip->yytoklen-= 2; + /* Skip '0b' */ + yylval->lex_str= get_token(lip, 2, yyLength()-2); return (BIN_NUM); } yyUnget(); @@ -782,14 +780,13 @@ int MYSQLlex(void *arg, void *yythd) if (c == '.' && ident_map[yyPeek()]) lip->next_state=MY_LEX_IDENT_SEP;// Next is '.' - yylval->lex_str= get_token(lip, 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 - lip->tok_start=lip->ptr; // Skip first ` while ((c=yyGet())) { int var_length; @@ -813,19 +810,20 @@ int MYSQLlex(void *arg, void *yythd) #endif } if (double_quotes) - yylval->lex_str=get_quoted_token(lip, yyLength() - double_quotes, + yylval->lex_str=get_quoted_token(lip, 1, + yyLength() - double_quotes -1, quote_char); else - yylval->lex_str=get_token(lip, yyLength()); + yylval->lex_str=get_token(lip, 1, yyLength() -1); if (c == quote_char) yySkip(); // Skip end ` 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(lip, yyLength()); + yylval->lex_str=get_token(lip, 0, yyLength()); return int_token(yylval->lex_str.str,yylval->lex_str.length); } // fall through @@ -843,10 +841,10 @@ int MYSQLlex(void *arg, void *yythd) break; } while (my_isdigit(cs,yyGet())) ; - yylval->lex_str=get_token(lip, yyLength()); + yylval->lex_str=get_token(lip, 0, yyLength()); return(FLOAT_NUM); } - yylval->lex_str=get_token(lip, yyLength()); + yylval->lex_str=get_token(lip, 0, yyLength()); return(DECIMAL_NUM); case MY_LEX_HEX_NUMBER: // Found x'hexstring' @@ -858,10 +856,9 @@ int MYSQLlex(void *arg, void *yythd) return(ABORT_SYM); // Illegal hex constant } yyGet(); // get_token makes an unget - yylval->lex_str=get_token(lip, length); - yylval->lex_str.str+=2; // Skip x' - yylval->lex_str.length-=3; // Don't count x' and last ' - lip->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' @@ -871,11 +868,10 @@ int MYSQLlex(void *arg, void *yythd) if (c != '\'') return(ABORT_SYM); // Illegal hex constant yyGet(); // get_token makes an unget - yylval->lex_str= get_token(lip, length); - yylval->lex_str.str+= 2; // Skip b' - yylval->lex_str.length-= 3; // Don't count b' and last ' - lip->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 || @@ -1047,7 +1043,7 @@ int MYSQLlex(void *arg, void *yythd) for (c=yyGet() ; my_isalnum(cs,c) || c == '.' || c == '_' || c == '$'; c= yyGet()) ; - yylval->lex_str=get_token(lip, yyLength()); + yylval->lex_str=get_token(lip, 0, yyLength()); return(LEX_HOSTNAME); case MY_LEX_SYSTEM_VAR: yylval->lex_str.str=(char*) lip->ptr; @@ -1079,7 +1075,7 @@ int MYSQLlex(void *arg, void *yythd) yyUnget(); // Put back 'c' return(tokval); // Was keyword } - yylval->lex_str=get_token(lip, length); + yylval->lex_str=get_token(lip, 0, length); return(result_state); } } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 82d148c4a1e..390a991c7b6 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -4439,21 +4439,23 @@ 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: { THD *thd= YYTHD; From d2e8f9010fef50bd4cc2c339aaa46f88528e954c Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 30 Apr 2007 15:36:29 -0600 Subject: [PATCH 5/5] fix merge issues mysql-test/r/sp.result: manual merge sql/sql_lex.cc: merge typo --- mysql-test/r/sp.result | 7 +++++++ sql/sql_lex.cc | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index e3406e4bbde..ac394a9df3d 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -6210,6 +6210,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/sql/sql_lex.cc b/sql/sql_lex.cc index d1056285d8d..c0fa16c0811 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -328,7 +328,7 @@ static LEX_STRING get_quoted_token(Lex_input_stream *lip, yyUnget(); // ptr points now after last token char tmp.length= lip->yytoklen=length; tmp.str=(char*) lip->m_thd->alloc(tmp.length+1); - from= lip->tok_start + skip + from= lip->tok_start + skip; to= tmp.str; end= to+length; for ( ; to != end; )