diff options
Diffstat (limited to 'sql/sql_lex.cc')
-rw-r--r-- | sql/sql_lex.cc | 1465 |
1 files changed, 1186 insertions, 279 deletions
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 9fb35e3f914..47de2ff36c7 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -22,7 +22,15 @@ #include <m_ctype.h> #include <hash.h> -LEX_STRING tmp_table_alias= {(char*) "tmp-table",8}; + +/* + Fake table list object, pointer to which is used as special value for + st_lex::time_zone_tables_used indicating that we implicitly use time + zone tables in this statement but real table list was not yet created. + Pointer to it is also returned by my_tz_get_tables_list() as indication + of transient error; +*/ +TABLE_LIST fake_time_zone_tables_list; /* Macros to look like lex */ @@ -40,10 +48,11 @@ LEX_STRING tmp_table_alias= {(char*) "tmp-table",8}; pthread_key(LEX*,THR_LEX); +/* Longest standard keyword name */ #define TOCK_NAME_LENGTH 24 /* - The following is based on the latin1 character set, and is only + The following data is based on the latin1 character set, and is only used when comparing keywords */ @@ -66,6 +75,7 @@ uchar to_upper_lex[] = { 208,209,210,211,212,213,214,247,216,217,218,219,220,221,222,255 }; + inline int lex_casecmp(const char *s, const char *t, uint len) { while (len-- != 0 && @@ -75,8 +85,6 @@ inline int lex_casecmp(const char *s, const char *t, uint len) #include "lex_hash.h" -static uchar state_map[256]; - void lex_init(void) { @@ -89,42 +97,6 @@ void lex_init(void) VOID(pthread_key_create(&THR_LEX,NULL)); - /* Fill state_map with states to get a faster parser */ - for (i=0; i < 256 ; i++) - { - if (isalpha(i)) - state_map[i]=(uchar) STATE_IDENT; - else if (isdigit(i)) - state_map[i]=(uchar) STATE_NUMBER_IDENT; -#if defined(USE_MB) && defined(USE_MB_IDENT) - else if (use_mb(default_charset_info) && my_ismbhead(default_charset_info, i)) - state_map[i]=(uchar) STATE_IDENT; -#endif - else if (!isgraph(i)) - state_map[i]=(uchar) STATE_SKIP; - else - state_map[i]=(uchar) STATE_CHAR; - } - state_map[(uchar)'_']=state_map[(uchar)'$']=(uchar) STATE_IDENT; - state_map[(uchar)'\'']=state_map[(uchar)'"']=(uchar) STATE_STRING; - state_map[(uchar)'-']=state_map[(uchar)'+']=(uchar) STATE_SIGNED_NUMBER; - state_map[(uchar)'.']=(uchar) STATE_REAL_OR_POINT; - state_map[(uchar)'>']=state_map[(uchar)'=']=state_map[(uchar)'!']= (uchar) STATE_CMP_OP; - state_map[(uchar)'<']= (uchar) STATE_LONG_CMP_OP; - state_map[(uchar)'&']=state_map[(uchar)'|']=(uchar) STATE_BOOL; - state_map[(uchar)'#']=(uchar) STATE_COMMENT; - state_map[(uchar)';']=(uchar) STATE_COLON; - state_map[(uchar)':']=(uchar) STATE_SET_VAR; - state_map[0]=(uchar) STATE_EOL; - state_map[(uchar)'\\']= (uchar) STATE_ESCAPE; - state_map[(uchar)'/']= (uchar) STATE_LONG_COMMENT; - state_map[(uchar)'*']= (uchar) STATE_END_LONG_COMMENT; - state_map[(uchar)'@']= (uchar) STATE_USER_END; - state_map[(uchar) '`']= (uchar) STATE_USER_VARIABLE_DELIMITER; - if (opt_sql_mode & MODE_ANSI_QUOTES) - { - state_map[(uchar) '"'] = STATE_USER_VARIABLE_DELIMITER; - } DBUG_VOID_RETURN; } @@ -136,31 +108,67 @@ void lex_free(void) } -LEX *lex_start(THD *thd, uchar *buf,uint length) +/* + This is called before every query that is to be parsed. + Because of this, it's critical to not do too much things here. + (We already do too much here) +*/ + +void lex_start(THD *thd, uchar *buf,uint length) { - LEX *lex= &thd->lex; - lex->next_state=STATE_START; + LEX *lex= thd->lex; + lex->unit.init_query(); + lex->unit.init_select(); + lex->thd= thd; + lex->unit.thd= thd; + lex->select_lex.init_query(); + lex->value_list.empty(); + lex->update_list.empty(); + lex->param_list.empty(); + lex->unit.next= lex->unit.master= + lex->unit.link_next= lex->unit.return_to= 0; + lex->unit.prev= lex->unit.link_prev= 0; + lex->unit.slave= lex->unit.global_parameters= lex->current_select= + lex->all_selects_list= &lex->select_lex; + lex->select_lex.master= &lex->unit; + lex->select_lex.prev= &lex->unit.slave; + lex->select_lex.link_next= lex->select_lex.slave= lex->select_lex.next= 0; + lex->select_lex.link_prev= (st_select_lex_node**)&(lex->all_selects_list); + lex->select_lex.options= 0; + lex->describe= 0; + lex->subqueries= lex->derived_tables= FALSE; + lex->lock_option= TL_READ; + lex->found_colon= 0; + lex->safe_to_cache_query= 1; + lex->time_zone_tables_used= 0; + lex->select_lex.select_number= 1; + lex->next_state=MY_LEX_START; lex->end_of_query=(lex->ptr=buf)+length; lex->yylineno = 1; - lex->select->create_refs=lex->in_comment=0; + lex->in_comment=0; lex->length=0; - lex->select->in_sum_expr=0; - lex->select->expr_list.empty(); - lex->select->ftfunc_list.empty(); - lex->lock_option=TL_READ; - lex->convert_set=(lex->thd=thd)->variables.convert_set; + lex->select_lex.in_sum_expr=0; + lex->select_lex.expr_list.empty(); + lex->select_lex.ftfunc_list_alloc.empty(); + lex->select_lex.ftfunc_list= &lex->select_lex.ftfunc_list_alloc; + lex->select_lex.group_list.empty(); + lex->select_lex.order_list.empty(); + lex->current_select= &lex->select_lex; lex->yacc_yyss=lex->yacc_yyvs=0; - lex->ignore_space=test(thd->sql_mode & MODE_IGNORE_SPACE); - lex->slave_thd_opt=0; + lex->ignore_space=test(thd->variables.sql_mode & MODE_IGNORE_SPACE); lex->sql_command=SQLCOM_END; - bzero((char *)&lex->mi,sizeof(lex->mi)); + lex->duplicates= DUP_ERROR; + lex->ignore= 0; + lex->proc_list.first= 0; lex->select_lex.is_item_list_lookup= 0; - return lex; } void lex_end(LEX *lex) { - lex->select->expr_list.delete_elements(); // If error when parsing sql-varargs + for (SELECT_LEX *sl= lex->all_selects_list; + sl; + sl= sl->next_select_in_list()) + sl->expr_list.delete_elements(); // If error when parsing sql-varargs x_free(lex->yacc_yyss); x_free(lex->yacc_yyvs); } @@ -182,7 +190,7 @@ static int find_keyword(LEX *lex, uint len, bool function) udf_func *udf; if (function && using_udf_functions && (udf=find_udf((char*) tok, len))) { - lex->thd->safe_to_cache_query=0; + lex->safe_to_cache_query=0; lex->yylval->udf=udf; switch (udf->returns) { case STRING_RESULT: @@ -191,16 +199,38 @@ static int find_keyword(LEX *lex, uint len, bool function) return (udf->type == UDFTYPE_FUNCTION) ? UDF_FLOAT_FUNC : UDA_FLOAT_SUM; case INT_RESULT: return (udf->type == UDFTYPE_FUNCTION) ? UDF_INT_FUNC : UDA_INT_SUM; + case ROW_RESULT: + default: + // This case should never be choosen + DBUG_ASSERT(0); + return 0; } } #endif return 0; } +/* + Check if name is a keyword + + SYNOPSIS + is_keyword() + name checked name + len length of checked name + + RETURN VALUES + 0 name is a keyword + 1 name isn't a keyword +*/ + +bool is_keyword(const char *name, uint len) +{ + return get_hash_symbol(name,len,0)!=0; +} /* make a copy of token before ptr and set yytoklen */ -LEX_STRING get_token(LEX *lex,uint length) +static LEX_STRING get_token(LEX *lex,uint length) { LEX_STRING tmp; yyUnget(); // ptr points now after last token char @@ -209,13 +239,42 @@ LEX_STRING get_token(LEX *lex,uint length) return tmp; } -/* Return an unescaped text literal without quotes */ -/* Fix sometimes to do only one scan of the string */ +/* + todo: + There are no dangerous charsets in mysql for function + get_quoted_token yet. But it should be fixed in the + future to operate multichar strings (like ucs2) +*/ + +static LEX_STRING get_quoted_token(LEX *lex,uint length, char quote) +{ + LEX_STRING tmp; + byte *from, *to, *end; + yyUnget(); // ptr points now after last token char + tmp.length=lex->yytoklen=length; + tmp.str=(char*) lex->thd->alloc(tmp.length+1); + for (from= (byte*) lex->tok_start, to= (byte*) tmp.str, end= to+length ; + to != end ; + ) + { + if ((*to++= *from++) == quote) + from++; // Skip double quotes + } + *to= 0; // End null for safety + return tmp; +} + + +/* + Return an unescaped text literal without quotes + Fix sometimes to do only one scan of the string +*/ static char *get_text(LEX *lex) { reg1 uchar c,sep; uint found_escape=0; + CHARSET_INFO *cs= lex->thd->charset(); sep= yyGetLast(); // String should end with this //lex->tok_start=lex->ptr-1; // Remember ' @@ -224,8 +283,8 @@ static char *get_text(LEX *lex) c = yyGet(); #ifdef USE_MB int l; - if (use_mb(default_charset_info) && - (l = my_ismbchar(default_charset_info, + if (use_mb(cs) && + (l = my_ismbchar(cs, (const char *)lex->ptr-1, (const char *)lex->end_of_query))) { lex->ptr += l-1; @@ -237,7 +296,18 @@ static char *get_text(LEX *lex) found_escape=1; if (lex->ptr == lex->end_of_query) return 0; - yySkip(); +#ifdef USE_MB + int l; + if (use_mb(cs) && + (l = my_ismbchar(cs, + (const char *)lex->ptr, + (const char *)lex->end_of_query))) { + lex->ptr += l; + continue; + } + else +#endif + yySkip(); } else if (c == sep) { @@ -265,12 +335,16 @@ static char *get_text(LEX *lex) else { uchar *to; + + /* Re-use found_escape for tracking state of escapes */ + found_escape= 0; + for (to=start ; str != end ; str++) { #ifdef USE_MB int l; - if (use_mb(default_charset_info) && - (l = my_ismbchar(default_charset_info, + if (use_mb(cs) && + (l = my_ismbchar(cs, (const char *)str, (const char *)end))) { while (l--) *to++ = *str++; @@ -278,7 +352,7 @@ static char *get_text(LEX *lex) continue; } #endif - if (*str == '\\' && str+1 != end) + if (!found_escape && *str == '\\' && str+1 != end) { switch(*++str) { case 'n': @@ -304,21 +378,24 @@ static char *get_text(LEX *lex) *to++= '\\'; // remember prefix for wildcard /* Fall through */ default: - *to++ = *str; + found_escape= 1; + str--; break; } } - else if (*str == sep) - *to++= *str++; // Two ' or " + else if (!found_escape && *str == sep) + { + found_escape= 1; + } else + { *to++ = *str; - + found_escape= 0; + } } *to=0; lex->yytoklen=(uint) (to-start); } - if (lex->convert_set) - lex->convert_set->convert((char*) start,lex->yytoklen); return (char*) start; } } @@ -344,7 +421,7 @@ static const uint signed_longlong_len=19; static const char *unsigned_longlong_str="18446744073709551615"; static const uint unsigned_longlong_len=20; -inline static uint int_token(const char *str,uint length) +static inline uint int_token(const char *str,uint length) { if (length < long_len) // quick normal case return NUM; @@ -416,85 +493,130 @@ inline static uint int_token(const char *str,uint length) return ((uchar) str[-1] <= (uchar) cmp[-1]) ? smaller : bigger; } +/* + yylex remember the following states from the following yylex() -// yylex remember the following states from the following yylex() -// STATE_EOQ ; found end of query -// STATE_OPERATOR_OR_IDENT ; last state was an ident, text or number -// (which can't be followed by a signed number) + - MY_LEX_EOQ Found end of query + - MY_LEX_OPERATOR_OR_IDENT Last state was an ident, text or number + (which can't be followed by a signed number) +*/ -int yylex(void *arg) +int yylex(void *arg, void *yythd) { reg1 uchar c; - int tokval; + int tokval, result_state; uint length; - enum lex_states state,prev_state; - LEX *lex=current_lex; + enum my_lex_states state; + LEX *lex= ((THD *)yythd)->lex; YYSTYPE *yylval=(YYSTYPE*) arg; + CHARSET_INFO *cs= ((THD *) yythd)->charset(); + uchar *state_map= cs->state_map; + uchar *ident_map= cs->ident_map; lex->yylval=yylval; // The global state lex->tok_start=lex->tok_end=lex->ptr; - prev_state=state=lex->next_state; - lex->next_state=STATE_OPERATOR_OR_IDENT; + state=lex->next_state; + lex->next_state=MY_LEX_OPERATOR_OR_IDENT; LINT_INIT(c); for (;;) { - switch(state) { - case STATE_OPERATOR_OR_IDENT: // Next is operator or keyword - case STATE_START: // Start of token + switch (state) { + case MY_LEX_OPERATOR_OR_IDENT: // Next is operator or keyword + case MY_LEX_START: // Start of token // Skip startspace - for (c=yyGet() ; (state_map[c] == STATE_SKIP) ; c= yyGet()) + for (c=yyGet() ; (state_map[c] == MY_LEX_SKIP) ; c= yyGet()) { if (c == '\n') lex->yylineno++; } lex->tok_start=lex->ptr-1; // Start of real token - state= (enum lex_states) state_map[c]; + state= (enum my_lex_states) state_map[c]; break; - case STATE_ESCAPE: + case MY_LEX_ESCAPE: if (yyGet() == 'N') { // Allow \N as shortcut for NULL yylval->lex_str.str=(char*) "\\N"; yylval->lex_str.length=2; return NULL_SYM; } - case STATE_CHAR: // Unknown or single char token - case STATE_SKIP: // This should not happen - yylval->lex_str.str=(char*) (lex->ptr=lex->tok_start);// Set to first char + case MY_LEX_CHAR: // Unknown or single char token + case MY_LEX_SKIP: // This should not happen + if (c == '-' && yyPeek() == '-' && + (my_isspace(cs,yyPeek2()) || + my_iscntrl(cs,yyPeek2()))) + { + state=MY_LEX_COMMENT; + break; + } + yylval->lex_str.str=(char*) (lex->ptr=lex->tok_start);// Set to first chr yylval->lex_str.length=1; c=yyGet(); if (c != ')') - lex->next_state= STATE_START; // Allow signed numbers + lex->next_state= MY_LEX_START; // Allow signed numbers if (c == ',') lex->tok_start=lex->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 + its value in a query for the binlog, the query must stay + grammatically correct. + */ + else if (c == '?' && ((THD*) yythd)->command == COM_PREPARE && + !ident_map[yyPeek()]) + return(PARAM_MARKER); return((int) c); - case STATE_IDENT: // Incomplete keyword or ident - if ((c == 'x' || c == 'X') && yyPeek() == '\'') + case MY_LEX_IDENT_OR_NCHAR: + if (yyPeek() != '\'') + { // Found x'hex-number' + state= MY_LEX_IDENT; + break; + } + yyGet(); // Skip ' + while ((c = yyGet()) && (c !='\'')) ; + length=(lex->ptr - lex->tok_start); // Length of hexnum+3 + if (c != '\'') + { + return(ABORT_SYM); // Illegal hex constant + } + yyGet(); // get_token makes an unget + yylval->lex_str=get_token(lex,length); + yylval->lex_str.str+=2; // Skip x' + yylval->lex_str.length-=3; // Don't count x' and last ' + lex->yytoklen-=3; + return (NCHAR_STRING); + + case MY_LEX_IDENT_OR_HEX: + if (yyPeek() == '\'') { // Found x'hex-number' - state=STATE_HEX_NUMBER; + state= MY_LEX_HEX_NUMBER; break; } + /* Fall through */ + case MY_LEX_IDENT_OR_BIN: // TODO: Add binary string handling + case MY_LEX_IDENT: + uchar *start; #if defined(USE_MB) && defined(USE_MB_IDENT) - if (use_mb(default_charset_info)) + if (use_mb(cs)) { - if (my_ismbhead(default_charset_info, yyGetLast())) + result_state= IDENT_QUOTED; + if (my_mbcharlen(cs, yyGetLast()) > 1) { - int l = my_ismbchar(default_charset_info, + int l = my_ismbchar(cs, (const char *)lex->ptr-1, (const char *)lex->end_of_query); if (l == 0) { - state = STATE_CHAR; + state = MY_LEX_CHAR; continue; } lex->ptr += l - 1; } - while (state_map[c=yyGet()] == STATE_IDENT || - state_map[c] == STATE_NUMBER_IDENT) + while (ident_map[c=yyGet()]) { - if (my_ismbhead(default_charset_info, c)) + if (my_mbcharlen(cs, c) > 1) { int l; - if ((l = my_ismbchar(default_charset_info, + if ((l = my_ismbchar(cs, (const char *)lex->ptr-1, (const char *)lex->end_of_query)) == 0) break; @@ -504,54 +626,74 @@ int yylex(void *arg) } else #endif - while (state_map[c=yyGet()] == STATE_IDENT || - state_map[c] == STATE_NUMBER_IDENT) ; + { + for (result_state= c; ident_map[c= yyGet()]; result_state|= c); + /* 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) { - for (; state_map[c] == STATE_SKIP ; c= yyGet()); + /* + If we find a space then this can't be an identifier. We notice this + below by checking start != lex->ptr. + */ + for (; state_map[c] == MY_LEX_SKIP ; c= yyGet()); } - if (c == '.' && (state_map[yyPeek()] == STATE_IDENT || - state_map[yyPeek()] == STATE_NUMBER_IDENT)) - lex->next_state=STATE_IDENT_SEP; + if (start == lex->ptr && c == '.' && ident_map[yyPeek()]) + lex->next_state=MY_LEX_IDENT_SEP; else { // '(' must follow directly if function yyUnget(); if ((tokval = find_keyword(lex,length,c == '('))) { - lex->next_state= STATE_START; // Allow signed numbers + lex->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); - if (lex->convert_set) - lex->convert_set->convert((char*) yylval->lex_str.str,lex->yytoklen); - return(IDENT); - case STATE_IDENT_SEP: // Found ident and now '.' - lex->next_state=STATE_IDENT_START;// Next is an ident (not a keyword) + /* + Note: "SELECT _bla AS 'alias'" + _bla should be considered as a IDENT if charset haven't been found. + So we don't use MYF(MY_WME) with get_charset_by_csname to avoid + producing an error. + */ + + if ((yylval->lex_str.str[0]=='_') && + (lex->charset=get_charset_by_csname(yylval->lex_str.str+1, + MY_CS_PRIMARY,MYF(0)))) + return(UNDERSCORE_CHARSET); + 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.length=1; c=yyGet(); // should be '.' + lex->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; return((int) c); - case STATE_NUMBER_IDENT: // number or ident which num-start - while (isdigit((c = yyGet()))) ; - if (state_map[c] != STATE_IDENT) + case MY_LEX_NUMBER_IDENT: // number or ident which num-start + while (my_isdigit(cs,(c = yyGet()))) ; + if (!ident_map[c]) { // Can't be identifier - state=STATE_INT_OR_REAL; + state=MY_LEX_INT_OR_REAL; break; } if (c == 'e' || c == 'E') { // The following test is written this way to allow numbers of type 1e1 - if (isdigit(yyPeek()) || (c=(yyGet())) == '+' || c == '-') + if (my_isdigit(cs,yyPeek()) || + (c=(yyGet())) == '+' || c == '-') { // Allow 1E+10 - if (isdigit(yyPeek())) // Number must have digit after sign + if (my_isdigit(cs,yyPeek())) // Number must have digit after sign { yySkip(); - while (isdigit(yyGet())) ; + while (my_isdigit(cs,yyGet())) ; yylval->lex_str=get_token(lex,yyLength()); return(FLOAT_NUM); } @@ -561,8 +703,8 @@ int yylex(void *arg) else if (c == 'x' && (lex->ptr - lex->tok_start) == 2 && lex->tok_start[0] == '0' ) { // Varbinary - while (isxdigit((c = yyGet()))) ; - if ((lex->ptr - lex->tok_start) >= 4 && state_map[c] != STATE_IDENT) + while (my_isxdigit(cs,(c = yyGet()))) ; + if ((lex->ptr - lex->tok_start) >= 4 && !ident_map[c]) { yylval->lex_str=get_token(lex,yyLength()); yylval->lex_str.str+=2; // Skip 0x @@ -573,29 +715,18 @@ int yylex(void *arg) yyUnget(); } // fall through - case STATE_IDENT_START: // Incomplete ident + case MY_LEX_IDENT_START: // We come here after '.' + result_state= IDENT; #if defined(USE_MB) && defined(USE_MB_IDENT) - if (use_mb(default_charset_info)) + if (use_mb(cs)) { - if (my_ismbhead(default_charset_info, yyGetLast())) - { - int l = my_ismbchar(default_charset_info, - (const char *)lex->ptr-1, - (const char *)lex->end_of_query); - if (l == 0) - { - state = STATE_CHAR; - continue; - } - lex->ptr += l - 1; - } - while (state_map[c=yyGet()] == STATE_IDENT || - state_map[c] == STATE_NUMBER_IDENT) + result_state= IDENT_QUOTED; + while (ident_map[c=yyGet()]) { - if (my_ismbhead(default_charset_info, c)) + if (my_mbcharlen(cs, c) > 1) { int l; - if ((l = my_ismbchar(default_charset_info, + if ((l = my_ismbchar(cs, (const char *)lex->ptr-1, (const char *)lex->end_of_query)) == 0) break; @@ -605,112 +736,84 @@ int yylex(void *arg) } else #endif - while (state_map[c = yyGet()] == STATE_IDENT || - state_map[c] == STATE_NUMBER_IDENT) ; - - if (c == '.' && (state_map[yyPeek()] == STATE_IDENT || - state_map[yyPeek()] == STATE_NUMBER_IDENT)) - lex->next_state=STATE_IDENT_SEP;// Next is '.' - // fall through + { + for (result_state=0; ident_map[c= yyGet()]; result_state|= c); + /* If there were non-ASCII characters, mark that we must convert */ + result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT; + } + if (c == '.' && ident_map[yyPeek()]) + lex->next_state=MY_LEX_IDENT_SEP;// Next is '.' - case STATE_FOUND_IDENT: // Complete ident - yylval->lex_str=get_token(lex,yyLength()); - if (lex->convert_set) - lex->convert_set->convert((char*) yylval->lex_str.str,lex->yytoklen); - return(IDENT); + yylval->lex_str= get_token(lex,yyLength()); + return(result_state); - case STATE_USER_VARIABLE_DELIMITER: + 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 ` -#ifdef USE_MB - if (use_mb(default_charset_info)) + while ((c=yyGet())) { - while ((c=yyGet()) && state_map[c] != STATE_USER_VARIABLE_DELIMITER && - c != (uchar) NAMES_SEP_CHAR) + int length; + if ((length= my_mbcharlen(cs, c)) == 1) { - if (my_ismbhead(default_charset_info, c)) - { - int l; - if ((l = my_ismbchar(default_charset_info, - (const char *)lex->ptr-1, - (const char *)lex->end_of_query)) == 0) - break; - lex->ptr += l-1; - } - } - } - else + if (c == (uchar) NAMES_SEP_CHAR) + break; /* Old .frm format can't handle this char */ + if (c == quote_char) + { + if (yyPeek() != quote_char) + break; + c=yyGet(); + double_quotes++; + continue; + } + } +#ifdef USE_MB + else if (length < 1) + break; // Error + lex->ptr+= length-1; #endif - { - while ((c=yyGet()) && state_map[c] != STATE_USER_VARIABLE_DELIMITER && - c != (uchar) NAMES_SEP_CHAR) ; } - yylval->lex_str=get_token(lex,yyLength()); - if (lex->convert_set) - lex->convert_set->convert((char*) yylval->lex_str.str,lex->yytoklen); - if (state_map[c] == STATE_USER_VARIABLE_DELIMITER) + if (double_quotes) + yylval->lex_str=get_quoted_token(lex,yyLength() - double_quotes, + quote_char); + else + yylval->lex_str=get_token(lex,yyLength()); + if (c == quote_char) yySkip(); // Skip end ` - return(IDENT); - - case STATE_SIGNED_NUMBER: // Incomplete signed number - if (prev_state == STATE_OPERATOR_OR_IDENT) - { - if (c == '-' && yyPeek() == '-' && - (isspace(yyPeek2()) || iscntrl(yyPeek2()))) - state=STATE_COMMENT; - else - state= STATE_CHAR; // Must be operator - break; - } - if (!isdigit(c=yyGet()) || yyPeek() == 'x') - { - if (c != '.') - { - if (c == '-' && isspace(yyPeek())) - state=STATE_COMMENT; - else - state = STATE_CHAR; // Return sign as single char - break; - } - yyUnget(); // Fix for next loop - } - while (isdigit(c=yyGet())) ; // Incomplete real or int number - if ((c == 'e' || c == 'E') && - (yyPeek() == '+' || yyPeek() == '-' || isdigit(yyPeek()))) - { // Real number - yyUnget(); - c= '.'; // Fool next test - } - // fall through - case STATE_INT_OR_REAL: // Compleat int or incompleat real + lex->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()); return int_token(yylval->lex_str.str,yylval->lex_str.length); } // fall through - case STATE_REAL: // Incomplete real number - while (isdigit(c = yyGet())) ; + case MY_LEX_REAL: // Incomplete real number + while (my_isdigit(cs,c = yyGet())) ; if (c == 'e' || c == 'E') { c = yyGet(); if (c == '-' || c == '+') c = yyGet(); // Skip sign - if (!isdigit(c)) + if (!my_isdigit(cs,c)) { // No digit after sign - state= STATE_CHAR; + state= MY_LEX_CHAR; break; } - while (isdigit(yyGet())) ; + while (my_isdigit(cs,yyGet())) ; yylval->lex_str=get_token(lex,yyLength()); return(FLOAT_NUM); } yylval->lex_str=get_token(lex,yyLength()); return(REAL_NUM); - case STATE_HEX_NUMBER: // Found x'hexstring' + case MY_LEX_HEX_NUMBER: // Found x'hexstring' yyGet(); // Skip ' - while (isxdigit((c = yyGet()))) ; + while (my_isxdigit(cs,(c = yyGet()))) ; length=(lex->ptr - lex->tok_start); // Length of hexnum+3 if (!(length & 1) || c != '\'') { @@ -723,64 +826,71 @@ int yylex(void *arg) lex->yytoklen-=3; return (HEX_NUM); - case STATE_CMP_OP: // Incomplete comparison operator - if (state_map[yyPeek()] == STATE_CMP_OP || - state_map[yyPeek()] == STATE_LONG_CMP_OP) + 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))) { - lex->next_state= STATE_START; // Allow signed numbers + lex->next_state= MY_LEX_START; // Allow signed numbers return(tokval); } - state = STATE_CHAR; // Something fishy found + state = MY_LEX_CHAR; // Something fishy found break; - case STATE_LONG_CMP_OP: // Incomplete comparison operator - if (state_map[yyPeek()] == STATE_CMP_OP || - state_map[yyPeek()] == STATE_LONG_CMP_OP) + case MY_LEX_LONG_CMP_OP: // Incomplete comparison operator + if (state_map[yyPeek()] == MY_LEX_CMP_OP || + state_map[yyPeek()] == MY_LEX_LONG_CMP_OP) { yySkip(); - if (state_map[yyPeek()] == STATE_CMP_OP) + if (state_map[yyPeek()] == MY_LEX_CMP_OP) yySkip(); } if ((tokval = find_keyword(lex,(uint) (lex->ptr - lex->tok_start),0))) { - lex->next_state= STATE_START; // Found long op + lex->next_state= MY_LEX_START; // Found long op return(tokval); } - state = STATE_CHAR; // Something fishy found + state = MY_LEX_CHAR; // Something fishy found break; - case STATE_BOOL: + case MY_LEX_BOOL: if (c != yyPeek()) { - state=STATE_CHAR; + state=MY_LEX_CHAR; break; } yySkip(); tokval = find_keyword(lex,2,0); // Is a bool operator - lex->next_state= STATE_START; // Allow signed numbers + lex->next_state= MY_LEX_START; // Allow signed numbers return(tokval); - case STATE_STRING: // Incomplete text string + case MY_LEX_STRING_OR_DELIMITER: + if (((THD *) yythd)->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))) { - state= STATE_CHAR; // Read char by char + state= MY_LEX_CHAR; // Read char by char break; } yylval->lex_str.length=lex->yytoklen; return(TEXT_STRING); - case STATE_COMMENT: // Comment + case MY_LEX_COMMENT: // Comment lex->select_lex.options|= OPTION_FOUND_COMMENT; while ((c = yyGet()) != '\n' && c) ; yyUnget(); // Safety against eof - state = STATE_START; // Try again + state = MY_LEX_START; // Try again break; - case STATE_LONG_COMMENT: /* Long C comment? */ + case MY_LEX_LONG_COMMENT: /* Long C comment? */ if (yyPeek() != '*') { - state=STATE_CHAR; // Probable division + state=MY_LEX_CHAR; // Probable division break; } yySkip(); // Skip '*' @@ -789,8 +899,8 @@ int yylex(void *arg) { ulong version=MYSQL_VERSION_ID; yySkip(); - state=STATE_START; - if (isdigit(yyPeek())) + state=MY_LEX_START; + if (my_isdigit(cs,yyPeek())) { // Version number version=strtol((char*) lex->ptr,(char**) &lex->ptr,10); } @@ -808,89 +918,110 @@ int yylex(void *arg) } if (lex->ptr != lex->end_of_query) yySkip(); // remove last '/' - state = STATE_START; // Try again + state = MY_LEX_START; // Try again break; - case STATE_END_LONG_COMMENT: + case MY_LEX_END_LONG_COMMENT: if (lex->in_comment && yyPeek() == '/') { yySkip(); lex->in_comment=0; - state=STATE_START; + state=MY_LEX_START; } else - state=STATE_CHAR; // Return '*' + state=MY_LEX_CHAR; // Return '*' break; - case STATE_SET_VAR: // Check if ':=' + case MY_LEX_SET_VAR: // Check if ':=' if (yyPeek() != '=') { - state=STATE_CHAR; // Return ':' + state=MY_LEX_CHAR; // Return ':' break; } yySkip(); return (SET_VAR); - case STATE_COLON: // optional line terminator + case MY_LEX_SEMICOLON: // optional line terminator if (yyPeek()) { - state=STATE_CHAR; // Return ';' + THD* thd= (THD*)yythd; + if ((thd->client_capabilities & CLIENT_MULTI_STATEMENTS) && + (thd->command != COM_PREPARE)) + { + lex->safe_to_cache_query=0; + lex->found_colon=(char*)lex->ptr; + thd->server_status |= SERVER_MORE_RESULTS_EXISTS; + lex->next_state=MY_LEX_END; + return(END_OF_INPUT); + } + else + state=MY_LEX_CHAR; // Return ';' break; } /* fall true */ - case STATE_EOL: - lex->next_state=STATE_END; // Mark for next loop - return(END_OF_INPUT); - case STATE_END: - lex->next_state=STATE_END; + case MY_LEX_EOL: + if (lex->ptr >= lex->end_of_query) + { + lex->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; return(0); // We found end of input last time - - // Actually real shouldn't start - // with . but allow them anyhow - case STATE_REAL_OR_POINT: - if (isdigit(yyPeek())) - state = STATE_REAL; // Real + + /* Actually real shouldn't start with . but allow them anyhow */ + case MY_LEX_REAL_OR_POINT: + if (my_isdigit(cs,yyPeek())) + state = MY_LEX_REAL; // Real else { - state = STATE_CHAR; // return '.' - lex->next_state=STATE_IDENT_START;// Next is an ident (not a keyword) + state= MY_LEX_IDENT_SEP; // return '.' + yyUnget(); // Put back '.' } break; - case STATE_USER_END: // end '@' of user@hostname + case MY_LEX_USER_END: // end '@' of user@hostname switch (state_map[yyPeek()]) { - case STATE_STRING: - case STATE_USER_VARIABLE_DELIMITER: + case MY_LEX_STRING: + case MY_LEX_USER_VARIABLE_DELIMITER: + case MY_LEX_STRING_OR_DELIMITER: break; - case STATE_USER_END: - lex->next_state=STATE_SYSTEM_VAR; + case MY_LEX_USER_END: + lex->next_state=MY_LEX_SYSTEM_VAR; break; default: - lex->next_state=STATE_HOSTNAME; + lex->next_state=MY_LEX_HOSTNAME; break; } yylval->lex_str.str=(char*) lex->ptr; yylval->lex_str.length=1; return((int) '@'); - case STATE_HOSTNAME: // end '@' of user@hostname - for (c=yyGet() ; - isalnum(c) || c == '.' || c == '_' || c == '$'; + 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()); return(LEX_HOSTNAME); - case STATE_SYSTEM_VAR: + case MY_LEX_SYSTEM_VAR: yylval->lex_str.str=(char*) lex->ptr; yylval->lex_str.length=1; - lex->next_state=STATE_IDENT_OR_KEYWORD; yySkip(); // Skip '@' + lex->next_state= (state_map[yyPeek()] == + MY_LEX_USER_VARIABLE_DELIMITER ? + MY_LEX_OPERATOR_OR_IDENT : + MY_LEX_IDENT_OR_KEYWORD); return((int) '@'); - case STATE_IDENT_OR_KEYWORD: + case MY_LEX_IDENT_OR_KEYWORD: /* We come here when we have found two '@' in a row. We should now be able to handle: [(global | local | session) .]variable_name */ - - while (state_map[c=yyGet()] == STATE_IDENT || - state_map[c] == STATE_NUMBER_IDENT) ; + + for (result_state= 0; ident_map[c= yyGet()]; result_state|= c); + /* If there were non-ASCII characters, mark that we must convert */ + result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT; + if (c == '.') - lex->next_state=STATE_IDENT_SEP; + lex->next_state=MY_LEX_IDENT_SEP; length= (uint) (lex->ptr - lex->tok_start)-1; if ((tokval= find_keyword(lex,length,0))) { @@ -898,9 +1029,785 @@ int yylex(void *arg) return(tokval); // Was keyword } yylval->lex_str=get_token(lex,length); - if (lex->convert_set) - lex->convert_set->convert((char*) yylval->lex_str.str,lex->yytoklen); - return(IDENT); + return(result_state); + } + } +} + +/* + st_select_lex structures initialisations +*/ + +void st_select_lex_node::init_query() +{ + options= 0; + linkage= UNSPECIFIED_TYPE; + no_error= no_table_names_allowed= 0; + uncacheable= 0; +} + +void st_select_lex_node::init_select() +{ +} + +void st_select_lex_unit::init_query() +{ + st_select_lex_node::init_query(); + linkage= GLOBAL_OPTIONS_TYPE; + global_parameters= first_select(); + select_limit_cnt= HA_POS_ERROR; + offset_limit_cnt= 0; + union_distinct= 0; + prepared= optimized= executed= 0; + item= 0; + union_result= 0; + table= 0; + fake_select_lex= 0; + cleaned= 0; + item_list.empty(); + describe= 0; +} + +void st_select_lex::init_query() +{ + st_select_lex_node::init_query(); + table_list.empty(); + item_list.empty(); + join= 0; + where= 0; + olap= UNSPECIFIED_OLAP_TYPE; + having_fix_field= 0; + resolve_mode= NOMATTER_MODE; + cond_count= with_wild= 0; + ref_pointer_array= 0; + select_n_having_items= 0; + prep_where= 0; + subquery_in_having= explicit_limit= 0; + parsing_place= NO_MATTER; +} + +void st_select_lex::init_select() +{ + st_select_lex_node::init_select(); + group_list.empty(); + type= db= db1= table1= db2= table2= 0; + having= 0; + use_index_ptr= ignore_index_ptr= 0; + table_join_options= 0; + in_sum_expr= with_wild= 0; + options= 0; + braces= 0; + when_list.empty(); + expr_list.empty(); + interval_list.empty(); + use_index.empty(); + ftfunc_list_alloc.empty(); + ftfunc_list= &ftfunc_list_alloc; + linkage= UNSPECIFIED_TYPE; + order_list.elements= 0; + order_list.first= 0; + order_list.next= (byte**) &order_list.first; + select_limit= HA_POS_ERROR; + offset_limit= 0; + with_sum_func= 0; +} + +/* + st_select_lex structures linking +*/ + +/* include on level down */ +void st_select_lex_node::include_down(st_select_lex_node *upper) +{ + if ((next= upper->slave)) + next->prev= &next; + prev= &upper->slave; + upper->slave= this; + master= upper; + slave= 0; +} + +/* + include on level down (but do not link) + + SYNOPSYS + st_select_lex_node::include_standalone() + upper - reference on node underr which this node should be included + ref - references on reference on this node +*/ +void st_select_lex_node::include_standalone(st_select_lex_node *upper, + st_select_lex_node **ref) +{ + next= 0; + prev= ref; + master= upper; + slave= 0; +} + +/* include neighbour (on same level) */ +void st_select_lex_node::include_neighbour(st_select_lex_node *before) +{ + if ((next= before->next)) + next->prev= &next; + prev= &before->next; + before->next= this; + master= before->master; + slave= 0; +} + +/* including in global SELECT_LEX list */ +void st_select_lex_node::include_global(st_select_lex_node **plink) +{ + if ((link_next= *plink)) + link_next->link_prev= &link_next; + link_prev= plink; + *plink= this; +} + +//excluding from global list (internal function) +void st_select_lex_node::fast_exclude() +{ + if (link_prev) + { + if ((*link_prev= link_next)) + link_next->link_prev= link_prev; + } + // Remove slave structure + for (; slave; slave= slave->next) + slave->fast_exclude(); + +} + +/* + excluding select_lex structure (except first (first select can't be + deleted, because it is most upper select)) +*/ +void st_select_lex_node::exclude() +{ + //exclude from global list + fast_exclude(); + //exclude from other structures + if ((*prev= next)) + next->prev= prev; + /* + We do not need following statements, because prev pointer of first + list element point to master->slave + if (master->slave == this) + master->slave= next; + */ +} + + +/* + Exclude level of current unit from tree of SELECTs + + SYNOPSYS + st_select_lex_unit::exclude_level() + + NOTE: units which belong to current will be brought up on level of + currernt unit +*/ +void st_select_lex_unit::exclude_level() +{ + SELECT_LEX_UNIT *units= 0, **units_last= &units; + for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select()) + { + // unlink current level from global SELECTs list + if (sl->link_prev && (*sl->link_prev= sl->link_next)) + sl->link_next->link_prev= sl->link_prev; + + // bring up underlay levels + SELECT_LEX_UNIT **last= 0; + for (SELECT_LEX_UNIT *u= sl->first_inner_unit(); u; u= u->next_unit()) + { + u->master= master; + last= (SELECT_LEX_UNIT**)&(u->next); + } + if (last) + { + (*units_last)= sl->first_inner_unit(); + units_last= last; + } + } + if (units) + { + // include brought up levels in place of current + (*prev)= units; + (*units_last)= (SELECT_LEX_UNIT*)next; + if (next) + next->prev= (SELECT_LEX_NODE**)units_last; + units->prev= prev; + } + else + { + // exclude currect unit from list of nodes + (*prev)= next; + if (next) + next->prev= prev; + } +} + + +/* + Exclude subtree of current unit from tree of SELECTs + + SYNOPSYS + st_select_lex_unit::exclude_tree() +*/ +void st_select_lex_unit::exclude_tree() +{ + for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select()) + { + // unlink current level from global SELECTs list + if (sl->link_prev && (*sl->link_prev= sl->link_next)) + sl->link_next->link_prev= sl->link_prev; + + // unlink underlay levels + for (SELECT_LEX_UNIT *u= sl->first_inner_unit(); u; u= u->next_unit()) + { + u->exclude_level(); + } + } + // exclude currect unit from list of nodes + (*prev)= next; + if (next) + next->prev= prev; +} + + +/* + st_select_lex_node::mark_as_dependent mark all st_select_lex struct from + this to 'last' as dependent + + SYNOPSIS + last - pointer to last st_select_lex struct, before wich all + st_select_lex have to be marked as dependent + + NOTE + 'last' should be reachable from this st_select_lex_node +*/ + +void st_select_lex::mark_as_dependent(SELECT_LEX *last) +{ + /* + Mark all selects from resolved to 1 before select where was + found table as depended (of select where was found table) + */ + for (SELECT_LEX *s= this; + s && s != last; + s= s->outer_select()) + if (!(s->uncacheable & UNCACHEABLE_DEPENDENT)) + { + // Select is dependent of outer select + s->uncacheable|= UNCACHEABLE_DEPENDENT; + SELECT_LEX_UNIT *munit= s->master_unit(); + munit->uncacheable|= UNCACHEABLE_DEPENDENT; + } +} + +bool st_select_lex_node::set_braces(bool value) { return 1; } +bool st_select_lex_node::inc_in_sum_expr() { return 1; } +uint st_select_lex_node::get_in_sum_expr() { return 0; } +TABLE_LIST* st_select_lex_node::get_table_list() { return 0; } +List<Item>* st_select_lex_node::get_item_list() { return 0; } +List<String>* st_select_lex_node::get_use_index() { return 0; } +List<String>* st_select_lex_node::get_ignore_index() { return 0; } +TABLE_LIST *st_select_lex_node::add_table_to_list(THD *thd, Table_ident *table, + LEX_STRING *alias, + ulong table_join_options, + thr_lock_type flags, + List<String> *use_index, + List<String> *ignore_index, + LEX_STRING *option) +{ + return 0; +} +ulong st_select_lex_node::get_table_join_options() +{ + return 0; +} + +/* + prohibit using LIMIT clause +*/ +bool st_select_lex::test_limit() +{ + if (select_limit != HA_POS_ERROR) + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), + "LIMIT & IN/ALL/ANY/SOME subquery"); + return(1); + } + // We need only 1 row to determinate existence + select_limit= 1; + // no sense in ORDER BY without LIMIT + order_list.empty(); + return(0); +} + +/* + Interface method of table list creation for query + + SYNOPSIS + st_select_lex_unit::create_total_list() + thd THD pointer + result pointer on result list of tables pointer + check_derived force derived table chacking (used for creating + table list for derived query) + DESCRIPTION + This is used for UNION & subselect to create a new table list of all used + tables. + The table_list->table entry in all used tables are set to point + to the entries in this list. + + RETURN + 0 - OK + !0 - error +*/ +bool st_select_lex_unit::create_total_list(THD *thd_arg, st_lex *lex, + TABLE_LIST **result_arg) +{ + *result_arg= 0; + if (!(res= create_total_list_n_last_return(thd_arg, lex, &result_arg))) + { + /* + If time zone tables were used implicitly in statement we should add + them to global table list. + */ + if (lex->time_zone_tables_used) + { + /* + Altough we are modifying lex data, it won't raise any problem in + case when this lex belongs to some prepared statement or stored + procedure: such modification does not change any invariants imposed + by requirement to reuse the same lex for multiple executions. + */ + if ((lex->time_zone_tables_used= my_tz_get_table_list(thd)) != + &fake_time_zone_tables_list) + { + *result_arg= lex->time_zone_tables_used; + } + else + { + send_error(thd, 0); + res= 1; + } + } + } + return res; +} + +/* + Table list creation for query + + SYNOPSIS + st_select_lex_unit::create_total_list() + thd THD pointer + lex pointer on LEX stricture + result pointer on pointer on result list of tables pointer + + DESCRIPTION + This is used for UNION & subselect to create a new table list of all used + tables. + The table_list->table_list in all tables of global list are set to point + to the local SELECT_LEX entries. + + RETURN + 0 - OK + !0 - error +*/ +bool st_select_lex_unit:: +create_total_list_n_last_return(THD *thd_arg, + st_lex *lex, + TABLE_LIST ***result_arg) +{ + TABLE_LIST *slave_list_first=0, **slave_list_last= &slave_list_first; + TABLE_LIST **new_table_list= *result_arg, *aux; + SELECT_LEX *sl= (SELECT_LEX*)slave; + + /* + iterate all inner selects + fake_select (if exists), + fake_select->next_select() always is 0 + */ + for (; + sl; + sl= (sl->next_select() ? + sl->next_select() : + (sl == fake_select_lex ? + 0 : + fake_select_lex))) + { + // check usage of ORDER BY in union + if (sl->order_list.first && sl->next_select() && !sl->braces && + sl->linkage != GLOBAL_OPTIONS_TYPE) + { + net_printf(thd_arg,ER_WRONG_USAGE,"UNION","ORDER BY"); + return 1; + } + + for (SELECT_LEX_UNIT *inner= sl->first_inner_unit(); + inner; + inner= inner->next_unit()) + { + if (inner->create_total_list_n_last_return(thd, lex, + &slave_list_last)) + return 1; + } + + if ((aux= (TABLE_LIST*) sl->table_list.first)) + { + TABLE_LIST *next_table; + for (; aux; aux= next_table) + { + TABLE_LIST *cursor; + next_table= aux->next; + /* Add to the total table list */ + if (!(cursor= (TABLE_LIST *) thd->memdup((char*) aux, + sizeof(*aux)))) + { + send_error(thd,0); + return 1; + } + *new_table_list= cursor; + cursor->table_list= aux; + new_table_list= &cursor->next; + *new_table_list= 0; // end result list + aux->table_list= cursor; + } + } + } + + if (slave_list_first) + { + *new_table_list= slave_list_first; + new_table_list= slave_list_last; + } + *result_arg= new_table_list; + return 0; +} + + +st_select_lex_unit* st_select_lex_unit::master_unit() +{ + return this; +} + + +st_select_lex* st_select_lex_unit::outer_select() +{ + return (st_select_lex*) master; +} + + +bool st_select_lex::add_order_to_list(THD *thd, Item *item, bool asc) +{ + return add_to_list(thd, order_list, item, asc); +} + + +bool st_select_lex::add_item_to_list(THD *thd, Item *item) +{ + return item_list.push_back(item); +} + + +bool st_select_lex::add_group_to_list(THD *thd, Item *item, bool asc) +{ + return add_to_list(thd, group_list, item, asc); +} + + +bool st_select_lex::add_ftfunc_to_list(Item_func_match *func) +{ + return !func || ftfunc_list->push_back(func); // end of memory? +} + + +st_select_lex_unit* st_select_lex::master_unit() +{ + return (st_select_lex_unit*) master; +} + + +st_select_lex* st_select_lex::outer_select() +{ + return (st_select_lex*) master->get_master(); +} + + +bool st_select_lex::set_braces(bool value) +{ + braces= value; + return 0; +} + + +bool st_select_lex::inc_in_sum_expr() +{ + in_sum_expr++; + return 0; +} + + +uint st_select_lex::get_in_sum_expr() +{ + return in_sum_expr; +} + + +TABLE_LIST* st_select_lex::get_table_list() +{ + return (TABLE_LIST*) table_list.first; +} + +List<Item>* st_select_lex::get_item_list() +{ + return &item_list; +} + + +List<String>* st_select_lex::get_use_index() +{ + return use_index_ptr; +} + + +List<String>* st_select_lex::get_ignore_index() +{ + return ignore_index_ptr; +} + + +ulong st_select_lex::get_table_join_options() +{ + return table_join_options; +} + + +bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num) +{ + if (ref_pointer_array) + return 0; + + /* + We have to create array in prepared statement memory if it is + prepared statement + */ + Item_arena *arena= thd->current_arena; + return (ref_pointer_array= + (Item **)arena->alloc(sizeof(Item*) * + (item_list.elements + + select_n_having_items + + order_group_num)* 5)) == 0; +} + + +/* + Find db.table which will be updated in this unit + + SYNOPSIS + st_select_lex_unit::check_updateable() + db - data base name + table - real table name + + RETURN + 1 - found + 0 - OK (table did not found) +*/ + +bool st_select_lex_unit::check_updateable(char *db, char *table) +{ + for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select()) + if (sl->check_updateable(db, table)) + return 1; + return 0; +} + + +/* + Find db.table which will be updated in this select and + underlying ones (except derived tables) + + SYNOPSIS + st_select_lex::check_updateable() + db - data base name + table - real table name + + RETURN + 1 - found + 0 - OK (table did not found) +*/ + +bool st_select_lex::check_updateable(char *db, char *table) +{ + if (find_real_table_in_list(get_table_list(), db, table)) + return 1; + + return check_updateable_in_subqueries(db, table); +} + +/* + Find db.table which will be updated in underlying subqueries + + SYNOPSIS + st_select_lex::check_updateable_in_subqueries() + db - data base name + table - real table name + + RETURN + 1 - found + 0 - OK (table did not found) +*/ + +bool st_select_lex::check_updateable_in_subqueries(char *db, char *table) +{ + for (SELECT_LEX_UNIT *un= first_inner_unit(); + un; + un= un->next_unit()) + { + if (un->first_select()->linkage != DERIVED_TABLE_TYPE && + un->check_updateable(db, table)) + return 1; + } + return 0; +} + + +void st_select_lex_unit::print(String *str) +{ + for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select()) + { + if (sl != first_select()) + { + str->append(" union ", 7); + if (!union_distinct) + str->append("all ", 4); + } + if (sl->braces) + str->append('('); + sl->print(thd, str); + if (sl->braces) + str->append(')'); + } + if (fake_select_lex == global_parameters) + { + if (fake_select_lex->order_list.elements) + { + str->append(" order by ", 10); + fake_select_lex->print_order(str, + (ORDER *) fake_select_lex-> + order_list.first); + } + fake_select_lex->print_limit(thd, str); + } +} + + +void st_select_lex::print_order(String *str, ORDER *order) +{ + for (; order; order= order->next) + { + (*order->item)->print(str); + if (!order->asc) + str->append(" desc", 5); + if (order->next) + str->append(','); + } +} + + +void st_select_lex::print_limit(THD *thd, String *str) +{ + if (explicit_limit) + { + str->append(" limit ", 7); + char buff[20]; + // latin1 is good enough for numbers + String st(buff, sizeof(buff), &my_charset_latin1); + st.set((ulonglong)select_limit, &my_charset_latin1); + str->append(st); + if (offset_limit) + { + str->append(','); + st.set((ulonglong)select_limit, &my_charset_latin1); + str->append(st); } } } + + +st_lex::st_lex() + :result(0) +{} + + +/* + Unlink first table from global table list and first table from outer select + list (lex->select_lex) + + SYNOPSIS + unlink_first_table() + tables Global table list + global_first Save first global table here + local_first Save first local table here + + NOTES + This function assumes that outer select list is non-empty. + + RETURN + global list without first table + +*/ +TABLE_LIST *st_lex::unlink_first_table(TABLE_LIST *tables, + TABLE_LIST **global_first, + TABLE_LIST **local_first) +{ + DBUG_ASSERT(select_lex.table_list.first != 0); + /* + Save pointers to first elements of global table list and list + of tables used in outer select. It does not harm if these lists + are the same. + */ + *global_first= tables; + *local_first= (TABLE_LIST*)select_lex.table_list.first; + + /* Exclude first elements from these lists */ + select_lex.table_list.first= (byte*) (*local_first)->next; + tables= tables->next; + (*global_first)->next= 0; + return tables; +} + + +/* + Link table which was unlinked with unlink_first_table() back. + + SYNOPSIS + link_first_table_back() + tables Global table list + global_first Saved first global table + local_first Saved first local table + + RETURN + global list +*/ +TABLE_LIST *st_lex::link_first_table_back(TABLE_LIST *tables, + TABLE_LIST *global_first, + TABLE_LIST *local_first) +{ + global_first->next= tables; + select_lex.table_list.first= (byte*) local_first; + return global_first; +} + +/* + There are st_select_lex::add_table_to_list & + st_select_lex::set_lock_for_tables are in sql_parse.cc + + st_select_lex::print is in sql_select.h + + st_select_lex_unit::prepare, st_select_lex_unit::exec, + st_select_lex_unit::cleanup, st_select_lex_unit::reinit_exec_mechanism, + st_select_lex_unit::change_result + are in sql_union.cc +*/ |