static int lex_one_token(YYSTYPE *yylval, THD *thd)
{
  uchar UNINIT_VAR(c);
  bool comment_closed;
  int	tokval, result_state;
  uint length;
  enum my_lex_states state;
  Lex_input_stream *lip= & thd->m_parser_state->m_lip;
  LEX *lex= thd->lex;
  CHARSET_INFO *const cs= thd->charset();
  const uchar *const state_map= cs->state_map;
  const uchar *const ident_map= cs->ident_map;

  lip->yylval=yylval;			// The global state

  lip->start_token();
  state=lip->next_state;
  lip->next_state=MY_LEX_OPERATOR_OR_IDENT;
  for (;;)
  {
    switch (state) {
    case MY_LEX_OPERATOR_OR_IDENT:	// Next is operator or keyword
    case MY_LEX_START:			// Start of token
      // Skip starting whitespace
      while(state_map[c= lip->yyPeek()] == MY_LEX_SKIP)
      {
	if (c == '\n')
	  lip->yylineno++;

        lip->yySkip();
      }

      /* Start of real token */
      lip->restart_token();
      c= lip->yyGet();
      state= (enum my_lex_states) state_map[c];
      break;
    case MY_LEX_ESCAPE:
      if (!lip->eof() && lip->yyGet() == 'N')
      {					// Allow \N as shortcut for NULL
	yylval->lex_str.str=(char*) "\\N";
	yylval->lex_str.length=2;
	return NULL_SYM;
      }
      /* Fall through */
    case MY_LEX_CHAR:			// Unknown or single char token
    case MY_LEX_SKIP:			// This should not happen
      if (c != ')')
	lip->next_state= MY_LEX_START;	// Allow signed numbers
      return((int) c);

    case MY_LEX_MINUS_OR_COMMENT:
      if (lip->yyPeek() == '-' &&
          (my_isspace(cs,lip->yyPeekn(1)) ||
           my_iscntrl(cs,lip->yyPeekn(1))))
      {
        state=MY_LEX_COMMENT;
        break;
      }
      lip->next_state= MY_LEX_START;	// Allow signed numbers
      return((int) c);

    case MY_LEX_PLACEHOLDER:
      /*
        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.
      */
      lip->next_state= MY_LEX_START;	// Allow signed numbers
      if (lip->stmt_prepare_mode && !ident_map[(uchar) lip->yyPeek()])
        return(PARAM_MARKER);
      return((int) c);

    case MY_LEX_COMMA:
      lip->next_state= MY_LEX_START;	// Allow signed numbers
      /*
        Warning:
        This is a work around, to make the "remember_name" rule in
        sql/sql_yacc.yy work properly.
        The problem is that, when parsing "select expr1, expr2",
        the code generated by bison executes the *pre* action
        remember_name (see select_item) *before* actually parsing the
        first token of expr2.
      */
      lip->restart_token();
      return((int) c);

    case MY_LEX_IDENT_OR_NCHAR:
    {
      uint sep;
      if (lip->yyPeek() != '\'')
      {
	state= MY_LEX_IDENT;
	break;
      }
      /* Found N'string' */
      lip->yySkip();                         // Skip '
      if (lip->get_text(&yylval->lex_str, (sep= lip->yyGetLast()), 2, 1))
      {
	state= MY_LEX_CHAR;             // Read char by char
	break;
      }

      lip->body_utf8_append(lip->m_cpp_text_start);
      lip->body_utf8_append_escape(thd, &yylval->lex_str,
                                   national_charset_info,
                                   lip->m_cpp_text_end, sep);

      lex->text_string_is_7bit= (lip->tok_bitmap & 0x80) ? 0 : 1;
      return(NCHAR_STRING);
    }
    case MY_LEX_IDENT_OR_HEX:
      if (lip->yyPeek() == '\'')
      {					// Found x'hex-number'
	state= MY_LEX_HEX_NUMBER;
	break;
      }
      /* fall through */
    case MY_LEX_IDENT_OR_BIN:
      if (lip->yyPeek() == '\'')
      {                                 // Found b'bin-number'
        state= MY_LEX_BIN_NUMBER;
        break;
      }
      /* fall through */
    case MY_LEX_IDENT:
      const char *start;
#if defined(USE_MB) && defined(USE_MB_IDENT)
      if (use_mb(cs))
      {
	result_state= IDENT_QUOTED;
        int char_length= my_charlen(cs, lip->get_ptr() - 1,
                                        lip->get_end_of_query());
        if (char_length <= 0)
        {
          state= MY_LEX_CHAR;
          continue;
        }
        lip->skip_binary(char_length - 1);

        while (ident_map[c=lip->yyGet()])
        {
          char_length= my_charlen(cs, lip->get_ptr() - 1,
                                      lip->get_end_of_query());
          if (char_length <= 0)
            break;
          lip->skip_binary(char_length - 1);
        }
      }
      else
#endif
      {
        for (result_state= c;
             ident_map[(uchar) (c= lip->yyGet())];
             result_state|= c)
          ;
        /* If there were non-ASCII characters, mark that we must convert */
        result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT;
      }
      length= lip->yyLength();
      start= lip->get_ptr();
      if (lip->ignore_space)
      {
        /*
          If we find a space then this can't be an identifier. We notice this
          below by checking start != lex->ptr.
        */
        for (; state_map[(uchar) c] == MY_LEX_SKIP ; c= lip->yyGet())
        {
          if (c == '\n')
            lip->yylineno++;
        }
      }
      if (start == lip->get_ptr() && c == '.' &&
          ident_map[(uchar) lip->yyPeek()])
	lip->next_state=MY_LEX_IDENT_SEP;
      else
      {					// '(' must follow directly if function
        lip->yyUnget();
	if ((tokval = find_keyword(lip, length, c == '(')))
	{
	  lip->next_state= MY_LEX_START;	// Allow signed numbers
	  return(tokval);		// Was keyword
	}
        lip->yySkip();                  // next state does a unget
      }
      yylval->lex_str=get_token(lip, 0, length);

      /*
         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] == '_')
      {
        CHARSET_INFO *cs= get_charset_by_csname(yylval->lex_str.str + 1,
                                                MY_CS_PRIMARY, MYF(0));
        if (cs)
        {
          yylval->charset= cs;
          lip->m_underscore_cs= cs;

          lip->body_utf8_append(lip->m_cpp_text_start,
                                lip->get_cpp_tok_start() + length);
          return(UNDERSCORE_CHARSET);
        }
      }

      lip->body_utf8_append(lip->m_cpp_text_start);

      lip->body_utf8_append_ident(thd, &yylval->lex_str, lip->m_cpp_text_end);

      return(result_state);			// IDENT or IDENT_QUOTED

    case MY_LEX_IDENT_SEP:                  // Found ident and now '.'
      yylval->lex_str.str= (char*) lip->get_ptr();
      yylval->lex_str.length= 1;
      c= lip->yyGet();                          // should be '.'
      lip->next_state= MY_LEX_IDENT_START;      // Next is ident (not keyword)
      if (!ident_map[(uchar) lip->yyPeek()])    // Probably ` or "
	lip->next_state= MY_LEX_START;
      return((int) c);

    case MY_LEX_NUMBER_IDENT:		// number or ident which num-start
      if (lip->yyGetLast() == '0')
      {
        c= lip->yyGet();
        if (c == 'x')
        {
          while (my_isxdigit(cs,(c = lip->yyGet()))) ;
          if ((lip->yyLength() >= 3) && !ident_map[c])
          {
            /* skip '0x' */
            yylval->lex_str=get_token(lip, 2, lip->yyLength()-2);
            return (HEX_NUM);
          }
          lip->yyUnget();
          state= MY_LEX_IDENT_START;
          break;
        }
        else if (c == 'b')
        {
          while ((c= lip->yyGet()) == '0' || c == '1')
            ;
          if ((lip->yyLength() >= 3) && !ident_map[c])
          {
            /* Skip '0b' */
            yylval->lex_str= get_token(lip, 2, lip->yyLength()-2);
            return (BIN_NUM);
          }
          lip->yyUnget();
          state= MY_LEX_IDENT_START;
          break;
        }
        lip->yyUnget();
      }

      while (my_isdigit(cs, (c = lip->yyGet()))) ;
      if (!ident_map[c])
      {					// Can't be identifier
	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 (my_isdigit(cs,lip->yyPeek()) ||
            (c=(lip->yyGet())) == '+' || c == '-')
	{				// Allow 1E+10
          if (my_isdigit(cs,lip->yyPeek()))     // Number must have digit after sign
	  {
            lip->yySkip();
            while (my_isdigit(cs,lip->yyGet())) ;
            yylval->lex_str=get_token(lip, 0, lip->yyLength());
	    return(FLOAT_NUM);
	  }
	}
        lip->yyUnget();
      }
      // fall through
    case MY_LEX_IDENT_START:			// We come here after '.'
      result_state= IDENT;
#if defined(USE_MB) && defined(USE_MB_IDENT)
      if (use_mb(cs))
      {
	result_state= IDENT_QUOTED;
        while (ident_map[c=lip->yyGet()])
        {
          int char_length= my_charlen(cs, lip->get_ptr() - 1,
                                          lip->get_end_of_query());
          if (char_length <= 0)
            break;
          lip->skip_binary(char_length - 1);
        }
      }
      else
#endif
      {
        for (result_state=0; ident_map[c= lip->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[(uchar) lip->yyPeek()])
	lip->next_state=MY_LEX_IDENT_SEP;// Next is '.'

      yylval->lex_str= get_token(lip, 0, lip->yyLength());

      lip->body_utf8_append(lip->m_cpp_text_start);

      lip->body_utf8_append_ident(thd, &yylval->lex_str, lip->m_cpp_text_end);

      return(result_state);

    case MY_LEX_USER_VARIABLE_DELIMITER:	// Found quote char
    {
      uint double_quotes= 0;
      char quote_char= c;                       // Used char
      while ((c=lip->yyGet()))
      {
        int var_length= my_charlen(cs, lip->get_ptr() - 1,
                                       lip->get_end_of_query());
        if (var_length == 1)
	{
	  if (c == quote_char)
	  {
            if (lip->yyPeek() != quote_char)
	      break;
            c=lip->yyGet();
	    double_quotes++;
	    continue;
	  }
	}
#ifdef USE_MB
        else if (var_length > 1)
        {
          lip->skip_binary(var_length - 1);
        }
#endif
      }
      if (double_quotes)
	yylval->lex_str=get_quoted_token(lip, 1,
                                         lip->yyLength() - double_quotes -1,
					 quote_char);
      else
        yylval->lex_str=get_token(lip, 1, lip->yyLength() -1);
      if (c == quote_char)
        lip->yySkip();                  // Skip end `
      lip->next_state= MY_LEX_START;

      lip->body_utf8_append(lip->m_cpp_text_start);

      lip->body_utf8_append_ident(thd, &yylval->lex_str, lip->m_cpp_text_end);

      return(IDENT_QUOTED);
    }
    case MY_LEX_INT_OR_REAL:		// Complete int or incomplete real
      if (c != '.')
      {					// Found complete integer number.
        yylval->lex_str=get_token(lip, 0, lip->yyLength());
	return int_token(yylval->lex_str.str, (uint) yylval->lex_str.length);
      }
      // fall through
    case MY_LEX_REAL:			// Incomplete real number
      while (my_isdigit(cs,c = lip->yyGet())) ;

      if (c == 'e' || c == 'E')
      {
        c = lip->yyGet();
	if (c == '-' || c == '+')
          c = lip->yyGet();                     // Skip sign
	if (!my_isdigit(cs,c))
	{				// No digit after sign
	  return (ABORT_SYM);
	}
        while (my_isdigit(cs,lip->yyGet())) ;
        yylval->lex_str=get_token(lip, 0, lip->yyLength());
	return(FLOAT_NUM);
      }
      yylval->lex_str=get_token(lip, 0, lip->yyLength());
      return(DECIMAL_NUM);

    case MY_LEX_HEX_NUMBER:		// Found x'hexstring'
      lip->yySkip();                    // Accept opening '
      while (my_isxdigit(cs, (c= lip->yyGet()))) ;
      if (c != '\'')
        return(ABORT_SYM);              // Illegal hex constant
      lip->yySkip();                    // Accept closing '
      length= lip->yyLength();          // Length of hexnum+3
      if ((length % 2) == 0)
        return(ABORT_SYM);              // odd number of hex digits
      yylval->lex_str=get_token(lip,
                                2,          // skip x'
                                length-3);  // don't count x' and last '
      return HEX_STRING;

    case MY_LEX_BIN_NUMBER:           // Found b'bin-string'
      lip->yySkip();                  // Accept opening '
      while ((c= lip->yyGet()) == '0' || c == '1')
        ;
      if (c != '\'')
        return(ABORT_SYM);            // Illegal hex constant
      lip->yySkip();                  // Accept closing '
      length= lip->yyLength();        // Length of bin-num + 3
      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
      lip->next_state= MY_LEX_START;	// Allow signed numbers
      if (state_map[(uchar) lip->yyPeek()] == MY_LEX_CMP_OP ||
          state_map[(uchar) lip->yyPeek()] == MY_LEX_LONG_CMP_OP)
      {
        lip->yySkip();
        if ((tokval= find_keyword(lip, 2, 0)))
          return(tokval);
        lip->yyUnget();
      }
      return(c);

    case MY_LEX_LONG_CMP_OP:		// Incomplete comparison operator
      lip->next_state= MY_LEX_START;
      if (state_map[(uchar) lip->yyPeek()] == MY_LEX_CMP_OP ||
          state_map[(uchar) lip->yyPeek()] == MY_LEX_LONG_CMP_OP)
      {
        lip->yySkip();
        if (state_map[(uchar) lip->yyPeek()] == MY_LEX_CMP_OP)
        {
          lip->yySkip();
          if ((tokval= find_keyword(lip, 3, 0)))
            return(tokval);
          lip->yyUnget();
        }
        if ((tokval= find_keyword(lip, 2, 0)))
          return(tokval);
        lip->yyUnget();
      }
      return(c);

    case MY_LEX_BOOL:
      if (c != lip->yyPeek())
      {
	state=MY_LEX_CHAR;
	break;
      }
      lip->yySkip();
      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->variables.sql_mode & MODE_ANSI_QUOTES)
      {
	state= MY_LEX_USER_VARIABLE_DELIMITER;
	break;
      }
      /* " used for strings */
      /* fall through */
    case MY_LEX_STRING:			// Incomplete text string
    {
      uint sep;
      if (lip->get_text(&yylval->lex_str, (sep= lip->yyGetLast()), 1, 1))
      {
	state= MY_LEX_CHAR;		// Read char by char
	break;
      }
      CHARSET_INFO *strcs= lip->m_underscore_cs ? lip->m_underscore_cs : cs;
      lip->body_utf8_append(lip->m_cpp_text_start);

      lip->body_utf8_append_escape(thd, &yylval->lex_str, strcs,
                                   lip->m_cpp_text_end, sep);
      lip->m_underscore_cs= NULL;

      lex->text_string_is_7bit= (lip->tok_bitmap & 0x80) ? 0 : 1;
      return(TEXT_STRING);
    }
    case MY_LEX_COMMENT:			//  Comment
      lex->select_lex.options|= OPTION_FOUND_COMMENT;
      while ((c = lip->yyGet()) != '\n' && c) ;
      lip->yyUnget();                   // Safety against eof
      state = MY_LEX_START;		// Try again
      break;
    case MY_LEX_LONG_COMMENT:		/* Long C comment? */
      if (lip->yyPeek() != '*')
      {
	state=MY_LEX_CHAR;		// Probable division
	break;
      }
      lex->select_lex.options|= OPTION_FOUND_COMMENT;
      /* Reject '/' '*', since we might need to turn off the echo */
      lip->yyUnget();

      lip->save_in_comment_state();

      if (lip->yyPeekn(2) == '!' ||
          (lip->yyPeekn(2) == 'M' && lip->yyPeekn(3) == '!'))
      {
        bool maria_comment_syntax= lip->yyPeekn(2) == 'M';
        lip->in_comment= DISCARD_COMMENT;
        /* Accept '/' '*' '!', but do not keep this marker. */
        lip->set_echo(FALSE);
        lip->yySkipn(maria_comment_syntax ? 4 : 3);

        /*
          The special comment format is very strict:
          '/' '*' '!', followed by an optional 'M' and exactly
          1-2 digits (major), 2 digits (minor), then 2 digits (dot).
          32302  -> 3.23.02
          50032  -> 5.0.32
          50114  -> 5.1.14
          100000 -> 10.0.0
        */
        if (  my_isdigit(cs, lip->yyPeekn(0))
           && my_isdigit(cs, lip->yyPeekn(1))
           && my_isdigit(cs, lip->yyPeekn(2))
           && my_isdigit(cs, lip->yyPeekn(3))
           && my_isdigit(cs, lip->yyPeekn(4))
           )
        {
          ulong version;
          uint length= 5;
          char *end_ptr= (char*) lip->get_ptr()+length;
          int error;
          if (my_isdigit(cs, lip->yyPeekn(5)))
          {
            end_ptr++;                          // 6 digit number
            length++;
          }

          version= (ulong) my_strtoll10(lip->get_ptr(), &end_ptr, &error);

          /*
            MySQL-5.7 has new features and might have new SQL syntax that
            MariaDB-10.0 does not understand. Ignore all versioned comments
            with MySQL versions in the range 50700-999999, but
            do not ignore MariaDB specific comments for the same versions.
          */
          if (version <= MYSQL_VERSION_ID &&
              (version < 50700 || version > 99999 || maria_comment_syntax))
          {
            /* Accept 'M' 'm' 'm' 'd' 'd' */
            lip->yySkipn(length);
            /* Expand the content of the special comment as real code */
            lip->set_echo(TRUE);
            state=MY_LEX_START;
            break;  /* Do not treat contents as a comment.  */
          }
          else
          {
#ifdef WITH_WSREP
	    if (WSREP(thd) && version == 99997 && thd->wsrep_exec_mode == LOCAL_STATE)
	    {
	      WSREP_DEBUG("consistency check: %s", thd->query());
	      thd->wsrep_consistency_check= CONSISTENCY_CHECK_DECLARED;
	      lip->yySkipn(5);
	      lip->set_echo(TRUE);
	      state=MY_LEX_START;
	      break;  /* Do not treat contents as a comment.  */
	    }
#endif /* WITH_WSREP */
            /*
              Patch and skip the conditional comment to avoid it
              being propagated infinitely (eg. to a slave).
            */
            char *pcom= lip->yyUnput(' ');
            comment_closed= ! consume_comment(lip, 1);
            if (! comment_closed)
            {
              *pcom= '!';
            }
            /* version allowed to have one level of comment inside. */
          }
        }
        else
        {
          /* Not a version comment. */
          state=MY_LEX_START;
          lip->set_echo(TRUE);
          break;
        }
      }
      else
      {
        lip->in_comment= PRESERVE_COMMENT;
        lip->yySkip();                  // Accept /
        lip->yySkip();                  // Accept *
        comment_closed= ! consume_comment(lip, 0);
        /* regular comments can have zero comments inside. */
      }
      /*
        Discard:
        - regular '/' '*' comments,
        - special comments '/' '*' '!' for a future version,
        by scanning until we find a closing '*' '/' marker.

        Nesting regular comments isn't allowed.  The first
        '*' '/' returns the parser to the previous state.

        /#!VERSI oned containing /# regular #/ is allowed #/

		Inside one versioned comment, another versioned comment
		is treated as a regular discardable comment.  It gets
		no special parsing.
      */

      /* Unbalanced comments with a missing '*' '/' are a syntax error */
      if (! comment_closed)
        return (ABORT_SYM);
      state = MY_LEX_START;             // Try again
      lip->restore_in_comment_state();
      break;
    case MY_LEX_END_LONG_COMMENT:
      if ((lip->in_comment != NO_COMMENT) && lip->yyPeek() == '/')
      {
        /* Reject '*' '/' */
        lip->yyUnget();
        /* Accept '*' '/', with the proper echo */
        lip->set_echo(lip->in_comment == PRESERVE_COMMENT);
        lip->yySkipn(2);
        /* And start recording the tokens again */
        lip->set_echo(TRUE);
        lip->in_comment=NO_COMMENT;
        state=MY_LEX_START;
      }
      else
	state=MY_LEX_CHAR;		// Return '*'
      break;
    case MY_LEX_SET_VAR:		// Check if ':='
      if (lip->yyPeek() != '=')
      {
	state=MY_LEX_CHAR;		// Return ':'
	break;
      }
      lip->yySkip();
      return (SET_VAR);
    case MY_LEX_SEMICOLON:			// optional line terminator
      state= MY_LEX_CHAR;               // Return ';'
      break;
    case MY_LEX_EOL:
      if (lip->eof())
      {
        lip->yyUnget();                 // Reject the last '\0'
        lip->set_echo(FALSE);
        lip->yySkip();
        lip->set_echo(TRUE);
        /* Unbalanced comments with a missing '*' '/' are a syntax error */
        if (lip->in_comment != NO_COMMENT)
          return (ABORT_SYM);
        lip->next_state=MY_LEX_END;     // Mark for next loop
        return(END_OF_INPUT);
      }
      state=MY_LEX_CHAR;
      break;
    case 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 */
    case MY_LEX_REAL_OR_POINT:
      if (my_isdigit(cs,lip->yyPeek()))
	state = MY_LEX_REAL;		// Real
      else
      {
	state= MY_LEX_IDENT_SEP;	// return '.'
        lip->yyUnget();                 // Put back '.'
      }
      break;
    case MY_LEX_USER_END:		// end '@' of user@hostname
      switch (state_map[(uchar) lip->yyPeek()]) {
      case MY_LEX_STRING:
      case MY_LEX_USER_VARIABLE_DELIMITER:
      case MY_LEX_STRING_OR_DELIMITER:
	break;
      case MY_LEX_USER_END:
	lip->next_state=MY_LEX_SYSTEM_VAR;
	break;
      default:
	lip->next_state=MY_LEX_HOSTNAME;
	break;
      }
      yylval->lex_str.str=(char*) lip->get_ptr();
      yylval->lex_str.length=1;
      return((int) '@');
    case MY_LEX_HOSTNAME:		// end '@' of user@hostname
      for (c=lip->yyGet() ;
	   my_isalnum(cs,c) || c == '.' || c == '_' ||  c == '$';
           c= lip->yyGet()) ;
      yylval->lex_str=get_token(lip, 0, lip->yyLength());
      return(LEX_HOSTNAME);
    case MY_LEX_SYSTEM_VAR:
      yylval->lex_str.str=(char*) lip->get_ptr();
      yylval->lex_str.length=1;
      lip->yySkip();                                    // Skip '@'
      lip->next_state= (state_map[(uchar) lip->yyPeek()] ==
			MY_LEX_USER_VARIABLE_DELIMITER ?
			MY_LEX_OPERATOR_OR_IDENT :
			MY_LEX_IDENT_OR_KEYWORD);
      return((int) '@');
    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
      */

      for (result_state= 0; ident_map[c= lip->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 == '.')
	lip->next_state=MY_LEX_IDENT_SEP;
      length= lip->yyLength();
      if (length == 0)
        return(ABORT_SYM);              // Names must be nonempty.
      if ((tokval= find_keyword(lip, length,0)))
      {
        lip->yyUnget();                         // Put back 'c'
	return(tokval);				// Was keyword
      }
      yylval->lex_str=get_token(lip, 0, length);

      lip->body_utf8_append(lip->m_cpp_text_start);

      lip->body_utf8_append_ident(thd, &yylval->lex_str, lip->m_cpp_text_end);

      return(result_state);
    }
  }
}


static void fix_prepare_info_in_table_list(THD *thd, TABLE_LIST *tbl)
{
  for (; tbl; tbl= tbl->next_local)
  {
    if (tbl->on_expr && !tbl->prep_on_expr)
    {
      thd->check_and_register_item_tree(&tbl->prep_on_expr, &tbl->on_expr);
      tbl->on_expr= tbl->on_expr->copy_andor_structure(thd);
    }
    if (tbl->is_view_or_derived() && tbl->is_merged_derived())
    {
      SELECT_LEX *sel= tbl->get_single_select();
      fix_prepare_info_in_table_list(thd, sel->get_table_list());
    }
  }
}

MDEV-25636: Bug report: abortion in sql/sql_parse.cc:6294

The asserion failure was caused by this query

  select /*id=1*/ from t1
  where
   col= ( select /*id=2*/ from ... where corr_cond1
          union
          select /*id=4*/ from ... where corr_cond2)

Here,
- select with id=2 was correlated due to corr_cond1.
- select with id=4 was initially correlated due to corr_cond2, but then
  the optimizer optimized away the correlation, making the select with id=4
  uncorrelated.

However, since select with id=2 remained correlated, the execution had to
re-compute the whole UNION. When it tried to execute select with id=4, it
hit an assertion  (join buffer already free'd).

This is because select with id=4 has freed its execution structures after
it has been executed once. The select is uncorrelated, so it did not expect
it would need to be executed for the second time.

Fixed this by adding this logic in
st_select_lex::optimize_unflattened_subqueries():

  If a member of a UNION is correlated, mark all its members as
  correlated, so that they are prepared to be executed multiple times.


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();

}


st_parsing_options::reset()
{
  allows_variable= TRUE;
}


void st_select_lex::append_table_to_list(TABLE_LIST *TABLE_LIST::*link,
                                         TABLE_LIST *table)
{
  TABLE_LIST *tl;
  for (tl= leaf_tables.head(); tl->*link; tl= tl->*link) ;
  tl->*link= table;
}


bool LEX::save_prep_leaf_tables()
{
  if (!thd->save_prep_leaf_list)
    return FALSE;

  Query_arena *arena= thd->stmt_arena, backup;
  arena= thd->activate_stmt_arena_if_needed(&backup);
  //It is used for DETETE/UPDATE so top level has only one SELECT
  DBUG_ASSERT(select_lex.next_select() == NULL);
  bool res= select_lex.save_prep_leaf_tables(thd);

  if (arena)
    thd->restore_active_arena(arena, &backup);

  if (res)
    return TRUE;

  thd->save_prep_leaf_list= FALSE;
  return FALSE;
}


void Lex_input_stream::reduce_digest_token(uint token_left, uint token_right)
{
  if (m_digest != NULL)
  {
    m_digest= digest_reduce_token(m_digest, token_left, token_right);
  }
}


void st_select_lex_node::move_as_slave(st_select_lex_node *new_master)
{
  exclude_from_tree();
  if (new_master->slave)
  {
    st_select_lex_node *curr= new_master->slave;
    for ( ; curr->next ; curr= curr->next) ;
    prev= &curr->next;
  }
  else
    prev= &new_master->slave;
  *prev= this;
  next= 0;
  master= new_master;
}


TABLE_LIST *LEX::unlink_first_table(bool *link_to_local)
{
  TABLE_LIST *first;
  if ((first= query_tables))
  {
    /*
      Exclude from global table list
    */
    if ((query_tables= query_tables->next_global))
      query_tables->prev_global= &query_tables;
    else
      query_tables_last= &query_tables;
    first->next_global= 0;

    /*
      and from local list if it is not empty
    */
    if ((*link_to_local= MY_TEST(select_lex.table_list.first)))
    {
      select_lex.context.table_list=
        select_lex.context.first_name_resolution_table= first->next_local;
      select_lex.table_list.first= first->next_local;
      select_lex.table_list.elements--;	//safety
      first->next_local= 0;
      /*
        Ensure that the global list has the same first table as the local
        list.
      */
      first_lists_tables_same();
    }
  }
  return first;
}


LEX::copy_db_to(char **p_db, size_t *p_db_length) const
{
  if (sphead && sphead->m_name.str)
  {
    DBUG_ASSERT(sphead->m_db.str && sphead->m_db.length);
    /*
      It is safe to assign the string by-pointer, both sphead and
      its statements reside in the same memory root.
    */
    *p_db= sphead->m_db.str;
    if (p_db_length)
      *p_db_length= sphead->m_db.length;
    return FALSE;
  }
  return thd->copy_db_to(p_db, p_db_length);
}


void st_select_lex::update_correlated_cache()
{
  TABLE_LIST *tl;
  List_iterator<TABLE_LIST> ti(leaf_tables);

  is_correlated= false;

  while ((tl= ti++))
  {
    //    is_correlated|= tl->is_with_table_recursive_reference();
    if (tl->on_expr)
      is_correlated|= MY_TEST(tl->on_expr->used_tables() & OUTER_REF_TABLE_BIT);
    for (TABLE_LIST *embedding= tl->embedding ; embedding ;
         embedding= embedding->embedding)
    {
      if (embedding->on_expr)
        is_correlated|= MY_TEST(embedding->on_expr->used_tables() &
                                OUTER_REF_TABLE_BIT);
    }
  }

  if (join->conds)
    is_correlated|= MY_TEST(join->conds->used_tables() & OUTER_REF_TABLE_BIT);

  is_correlated|= join->having_is_correlated;

  if (join->having)
    is_correlated|= MY_TEST(join->having->used_tables() & OUTER_REF_TABLE_BIT);

  if (join->tmp_having)
    is_correlated|= MY_TEST(join->tmp_having->used_tables() &
                            OUTER_REF_TABLE_BIT);

  Item *item;
  List_iterator_fast<Item> it(join->fields_list);
  while ((item= it++))
    is_correlated|= MY_TEST(item->used_tables() & OUTER_REF_TABLE_BIT);

  for (ORDER *order= group_list.first; order; order= order->next)
    is_correlated|= MY_TEST((*order->item)->used_tables() &
                            OUTER_REF_TABLE_BIT);

  if (!master_unit()->is_union())
  {
    for (ORDER *order= order_list.first; order; order= order->next)
      is_correlated|= MY_TEST((*order->item)->used_tables() &
                              OUTER_REF_TABLE_BIT);
  }

  if (!is_correlated)
    uncacheable&= ~UNCACHEABLE_DEPENDENT;
}


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;
}


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;
}


void st_select_lex_unit::print(String *str, enum_query_type query_type)
{
  bool union_all= !union_distinct;
  if (with_clause)
    with_clause->print(str, query_type);
  for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
  {
    if (sl != first_select())
    {
      str->append(STRING_WITH_LEN(" union "));
      if (union_all)
	str->append(STRING_WITH_LEN("all "));
      else if (union_distinct == sl)
        union_all= TRUE;
    }
    if (sl->braces)
      str->append('(');
    sl->print(thd, str, query_type);
    if (sl->braces)
      str->append(')');
  }
  if (fake_select_lex)
  {
    if (fake_select_lex->order_list.elements)
    {
      str->append(STRING_WITH_LEN(" order by "));
      fake_select_lex->print_order(str,
        fake_select_lex->order_list.first,
        query_type);
    }
    fake_select_lex->print_limit(thd, str, query_type);
  }
  else if (saved_fake_select_lex)
    saved_fake_select_lex->print_limit(thd, str, query_type);
}


bool Lex_input_stream::get_text(LEX_STRING *dst, uint sep,
                                int pre_skip, int post_skip)
{
  uchar c;
  uint found_escape=0;
  CHARSET_INFO *cs= m_thd->charset();

  tok_bitmap= 0;
  while (! eof())
  {
    c= yyGet();
    tok_bitmap|= c;
#ifdef USE_MB
    {
      int l;
      if (use_mb(cs) &&
          (l = my_ismbchar(cs,
                           get_ptr() -1,
                           get_end_of_query()))) {
        skip_binary(l-1);
        continue;
      }
    }
#endif
    if (c == '\\' &&
        !(m_thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES))
    {					// Escaped character
      found_escape=1;
      if (eof())
	return true;
      yySkip();
    }
    else if (c == sep)
    {
      if (c == yyGet())                 // Check if two separators in a row
      {
        found_escape=1;                 // duplicate. Remember for delete
	continue;
      }
      else
        yyUnget();

      /* Found end. Unescape and return string */
      const char *str, *end;

      str= get_tok_start();
      end= get_ptr();
      /* Extract the text from the token */
      str += pre_skip;
      end -= post_skip;
      DBUG_ASSERT(end >= str);

      if (!(dst->str= (char*) m_thd->alloc((uint) (end - str) + 1)))
      {
        dst->str= (char*) "";        // Sql_alloc has set error flag
        dst->length= 0;
        return true;
      }

      m_cpp_text_start= get_cpp_tok_start() + pre_skip;
      m_cpp_text_end= get_cpp_ptr() - post_skip;

      if (!found_escape)
      {
        memcpy(dst->str, str, dst->length= (end - str));
        dst->str[dst->length]= 0;
      }
      else
      {
        dst->length= unescape(cs, dst->str, str, end, sep);
      }
      return false;
    }
  }
  return true;                         // unexpected end of query
}


void lex_end_nops(LEX *lex)
{
  DBUG_ENTER("lex_end_nops");
  sp_head::destroy(lex->sphead);
  lex->sphead= NULL;

  /* Reset LEX_MASTER_INFO */
  lex->mi.reset(lex->sql_command == SQLCOM_CHANGE_MASTER);
  delete_dynamic(&lex->delete_gtid_domain);

  DBUG_VOID_RETURN;
}


void LEX::link_first_table_back(TABLE_LIST *first,
				   bool link_to_local)
{
  if (first)
  {
    if ((first->next_global= query_tables))
      query_tables->prev_global= &first->next_global;
    else
      query_tables_last= &first->next_global;
    query_tables= first;

    if (link_to_local)
    {
      first->next_local= select_lex.table_list.first;
      select_lex.context.table_list= first;
      select_lex.table_list.first= first;
      select_lex.table_list.elements++;	//safety
    }
  }
}


uint Lex_input_stream::get_body_utf8_maximum_length(THD *thd)
{
  /*
    String literals can grow during escaping:
    1a. Character string '<TAB>' can grow to '\t', 3 bytes to 4 bytes growth.
    1b. Character string '1000 times <TAB>' grows from
        1002 to 2002 bytes (including quotes), which gives a little bit
        less than 2 times growth.
    "2" should be a reasonable multiplier that safely covers escaping needs.
  */
  return (m_buf_length / thd->variables.character_set_client->mbminlen) *
          my_charset_utf8_bin.mbmaxlen * 2/*for escaping*/;
}


void st_select_lex_node::init_query()
{
  options= 0;
  sql_cache= SQL_CACHE_UNSPECIFIED;
  linkage= UNSPECIFIED_TYPE;
  no_table_names_allowed= 0;
  uncacheable= 0;
}


void st_select_lex_node::init_select()
{
}


Yacc_state::~Yacc_state()
{
  if (yacc_yyss)
  {
    my_free(yacc_yyss);
    my_free(yacc_yyvs);
  }
}


void lex_start(THD *thd)
{
  LEX *lex= thd->lex;
  DBUG_ENTER("lex_start");
  DBUG_PRINT("info", ("Lex %p", thd->lex));

  lex->thd= lex->unit.thd= thd;

  lex->stmt_lex= lex; // default, should be rewritten for VIEWs And CTEs
  DBUG_ASSERT(!lex->explain);

  lex->context_stack.empty();
  lex->unit.init_query();
  lex->unit.init_select();
  /* 'parent_lex' is used in init_query() so it must be before it. */
  lex->select_lex.parent_lex= lex;
  lex->select_lex.init_query();
  lex->current_select_number= 1;
  lex->curr_with_clause= 0;
  lex->with_clauses_list= 0;
  lex->with_clauses_list_last_next= &lex->with_clauses_list;
  lex->clone_spec_offset= 0;
  lex->value_list.empty();
  lex->update_list.empty();
  lex->set_var_list.empty();
  lex->param_list.empty();
  lex->view_list.empty();
  lex->with_column_list.empty();
  lex->with_persistent_for_clause= FALSE;
  lex->column_list= NULL;
  lex->index_list= NULL;
  lex->prepared_stmt_params.empty();
  lex->auxiliary_table_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->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->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED;
  lex->select_lex.init_order();
  lex->select_lex.group_list.empty();
  if (lex->select_lex.group_list_ptrs)
    lex->select_lex.group_list_ptrs->clear();
  lex->describe= 0;
  lex->analyze_stmt= 0;
  lex->explain_json= false;
  lex->subqueries= FALSE;
  lex->context_analysis_only= 0;
  lex->derived_tables= 0;
  lex->with_cte_resolution= false;
  lex->only_cte_resolution= false;
  lex->safe_to_cache_query= 1;
  lex->parsing_options.reset();
  lex->empty_field_list_on_rset= 0;
  lex->select_lex.select_number= 1;
  lex->part_info= 0;
  lex->select_lex.in_sum_expr=0;
  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->select_lex.gorder_list.empty();
  lex->m_sql_cmd= NULL;
  lex->duplicates= DUP_ERROR;
  lex->ignore= 0;
  lex->spname= NULL;
  lex->spcont= NULL;
  lex->proc_list.first= 0;
  lex->escape_used= FALSE;
  lex->query_tables= 0;
  lex->reset_query_tables_list(FALSE);
  lex->expr_allows_subselect= TRUE;
  lex->use_only_table_context= FALSE;
  lex->parse_vcol_expr= FALSE;
  lex->check_exists= FALSE;
  lex->create_info.lex_start();
  lex->verbose= 0;

  lex->name= null_lex_str;
  lex->event_parse_data= NULL;
  lex->profile_options= PROFILE_NONE;
  lex->nest_level=0 ;
  lex->select_lex.nest_level_base= &lex->unit;
  lex->allow_sum_func= 0;
  lex->in_sum_func= NULL;

  lex->used_tables= 0;
  lex->only_view= FALSE;
  lex->reset_slave_info.all= false;
  lex->limit_rows_examined= 0;
  lex->limit_rows_examined_cnt= ULONGLONG_MAX;
  lex->var_list.empty();
  lex->stmt_var_list.empty();
  lex->proc_list.elements=0;

  lex->save_group_list.empty();
  lex->save_order_list.empty();
  lex->win_ref= NULL;
  lex->win_frame= NULL;
  lex->frame_top_bound= NULL;
  lex->frame_bottom_bound= NULL;
  lex->win_spec= NULL;

  lex->is_lex_started= TRUE;
  DBUG_VOID_RETURN;
}


void LEX::cleanup_after_one_table_open()
{
  /*
    thd->lex->derived_tables & additional units may be set if we open
    a view. It is necessary to clear thd->lex->derived_tables flag
    to prevent processing of derived tables during next open_and_lock_tables
    if next table is a real table and cleanup & remove underlying units
    NOTE: all units will be connected to thd->lex->select_lex, because we
    have not UNION on most upper level.
    */
  if (all_selects_list != &select_lex)
  {
    derived_tables= 0;
    select_lex.exclude_from_table_unique_test= false;
    /* cleunup underlying units (units of VIEW) */
    for (SELECT_LEX_UNIT *un= select_lex.first_inner_unit();
         un;
         un= un->next_unit())
      un->cleanup();
    /* reduce all selects list to default state */
    all_selects_list= &select_lex;
    /* remove underlying units (units of VIEW) subtree */
    select_lex.cut_subtree();
  }
}


ha_rows st_select_lex::get_offset()
{
  ulonglong val= 0;

  if (offset_limit)
  {
    // see comment for st_select_lex::get_limit()
    bool fix_fields_successful= true;
    if (!offset_limit->fixed)
    {
      fix_fields_successful= !offset_limit->fix_fields(master_unit()->thd,
                                                       NULL);

      DBUG_ASSERT(fix_fields_successful);
    }
    val= fix_fields_successful ? offset_limit->val_uint() : HA_POS_ERROR;
  }

  return (ha_rows)val;
}


bool st_select_lex::save_prep_leaf_tables(THD *thd)
{
  if (prep_leaf_list_state == SAVED)
    return FALSE;

  List_iterator_fast<TABLE_LIST> li(leaf_tables);
  TABLE_LIST *table;

  /*
    Check that the SELECT_LEX was really prepared and so tables are setup.

    It can be subquery in SET clause of UPDATE which was not prepared yet, so
    its tables are not yet setup and ready for storing.
  */
  if (prep_leaf_list_state != READY)
    return FALSE;

  while ((table= li++))
  {
    if (leaf_tables_prep.push_back(table))
      return TRUE;
  }
  prep_leaf_list_state= SAVED;
  for (SELECT_LEX_UNIT *u= first_inner_unit(); u; u= u->next_unit())
  {
    for (SELECT_LEX *sl= u->first_select(); sl; sl= sl->next_select())
    {
      if (sl->save_prep_leaf_tables(thd))
        return TRUE;
    }
  }

  return FALSE;
}


void LEX::reset_n_backup_query_tables_list(Query_tables_list *backup)
{
  backup->set_query_tables_list(this);
  /*
    We have to perform full initialization here since otherwise we
    will damage backed up state.
  */
  this->reset_query_tables_list(TRUE);
}


void SELECT_LEX::mark_as_belong_to_derived(TABLE_LIST *derived)
{
  /* Mark tables as belonging to this DT */
  TABLE_LIST *tl;
  List_iterator<TABLE_LIST> ti(leaf_tables);
  while ((tl= ti++))
    tl->belong_to_derived= derived;
}


bool st_select_lex::add_item_to_list(THD *thd, Item *item)
{
  DBUG_ENTER("st_select_lex::add_item_to_list");
  DBUG_PRINT("info", ("Item: %p", item));
  DBUG_RETURN(item_list.push_back(item, thd->mem_root));
}


void st_select_lex_unit::set_limit(st_select_lex *sl)
{
  DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());

  offset_limit_cnt= sl->get_offset();
  select_limit_cnt= sl->get_limit();
  if (select_limit_cnt + offset_limit_cnt >= select_limit_cnt)
    select_limit_cnt+= offset_limit_cnt;
  else
    select_limit_cnt= HA_POS_ERROR;
}


bool st_select_lex_node::inc_in_sum_expr()           { return 1; }


bool LEX::restore_set_statement_var()
{
  bool err= false;
  DBUG_ENTER("LEX::restore_set_statement_var");
  if (!old_var_list.is_empty())
  {
    DBUG_PRINT("info", ("vars: %d", old_var_list.elements));
    err= sql_set_variables(thd, &old_var_list, false);
    old_var_list.empty();
    free_arena_for_set_stmt();
  }
  DBUG_ASSERT(!is_arena_for_set_stmt());
  DBUG_RETURN(err);
}


st_select_lex_node *st_select_lex_node:: insert_chain_before(
				         st_select_lex_node **ptr_pos_to_insert,
                                         st_select_lex_node *end_chain_node)
{
  end_chain_node->link_next= *ptr_pos_to_insert;
  (*ptr_pos_to_insert)->link_prev= &end_chain_node->link_next;
  this->link_prev= ptr_pos_to_insert;
  return this;
}


void Lex_input_stream::body_utf8_append(const char *ptr,
                                        const char *end_ptr)
{
  DBUG_ASSERT(m_cpp_buf <= ptr && ptr <= m_cpp_buf + m_buf_length);
  DBUG_ASSERT(m_cpp_buf <= end_ptr && end_ptr <= m_cpp_buf + m_buf_length);

  if (!m_body_utf8)
    return;

  if (m_cpp_utf8_processed_ptr >= ptr)
    return;

  size_t bytes_to_copy= ptr - m_cpp_utf8_processed_ptr;

  memcpy(m_body_utf8_ptr, m_cpp_utf8_processed_ptr, bytes_to_copy);
  m_body_utf8_ptr += bytes_to_copy;
  *m_body_utf8_ptr= 0;

  m_cpp_utf8_processed_ptr= end_ptr;
}


void Lex_input_stream::body_utf8_start(THD *thd, const char *begin_ptr)
{
  DBUG_ASSERT(begin_ptr);
  DBUG_ASSERT(m_cpp_buf <= begin_ptr && begin_ptr <= m_cpp_buf + m_buf_length);

  uint body_utf8_length= get_body_utf8_maximum_length(thd);

  m_body_utf8= (char *) thd->alloc(body_utf8_length + 1);
  m_body_utf8_ptr= m_body_utf8;
  *m_body_utf8_ptr= 0;

  m_cpp_utf8_processed_ptr= begin_ptr;
}


void Query_tables_list::destroy_query_tables_list()
{
  my_hash_free(&sroutines);
}


int MYSQLlex(YYSTYPE *yylval, THD *thd)
{
  Lex_input_stream *lip= & thd->m_parser_state->m_lip;
  int token;

  if (lip->lookahead_token >= 0)
  {
    /*
      The next token was already parsed in advance,
      return it.
    */
    token= lip->lookahead_token;
    lip->lookahead_token= -1;
    *yylval= *(lip->lookahead_yylval);
    lip->lookahead_yylval= NULL;
    return token;
  }

  token= lex_one_token(yylval, thd);
  lip->add_digest_token(token, yylval);

  switch(token) {
  case WITH:
    /*
      Parsing 'WITH' 'ROLLUP' or 'WITH' 'CUBE' requires 2 look ups,
      which makes the grammar LALR(2).
      Replace by a single 'WITH_ROLLUP' or 'WITH_CUBE' token,
      to transform the grammar into a LALR(1) grammar,
      which sql_yacc.yy can process.
    */
    token= lex_one_token(yylval, thd);
    lip->add_digest_token(token, yylval);
    switch(token) {
    case CUBE_SYM:
      return WITH_CUBE_SYM;
    case ROLLUP_SYM:
      return WITH_ROLLUP_SYM;
    default:
      /*
        Save the token following 'WITH'
      */
      lip->lookahead_yylval= lip->yylval;
      lip->yylval= NULL;
      lip->lookahead_token= token;
      return WITH;
    }
    break;
  default:
    break;
  }
  return token;
}


void st_select_lex_unit::init_query()
{
  st_select_lex_node::init_query();
  linkage= GLOBAL_OPTIONS_TYPE;
  select_limit_cnt= HA_POS_ERROR;
  offset_limit_cnt= 0;
  union_distinct= 0;
  prepared= optimized= executed= 0;
  optimize_started= 0;
  item= 0;
  union_result= 0;
  table= 0;
  fake_select_lex= 0;
  saved_fake_select_lex= 0;
  cleaned= 0;
  item_list.empty();
  describe= 0;
  found_rows_for_union= 0;
  derived= 0;
  is_view= false;
  with_clause= 0;
  with_element= 0;
  cloned_from= 0;
  columns_are_renamed= false;
}


void lex_end(LEX *lex)
{
  DBUG_ENTER("lex_end");
  DBUG_PRINT("enter", ("lex: %p", lex));

  lex_unlock_plugins(lex);
  lex_end_nops(lex);

  DBUG_VOID_RETURN;
}


void binlog_unsafe_map_init()
{
  memset((void*) binlog_unsafe_map, 0, sizeof(uint) * 256);

  /*
    Classify a statement as unsafe when there is a mixed statement and an
    on-going transaction at any point of the execution if:

      1. The mixed statement is about to update a transactional table and
      a non-transactional table.

      2. The mixed statement is about to update a transactional table and
      read from a non-transactional table.

      3. The mixed statement is about to update a non-transactional table
      and temporary transactional table.

      4. The mixed statement is about to update a temporary transactional
      table and read from a non-transactional table.

      5. The mixed statement is about to update a transactional table and
      a temporary non-transactional table.

      6. The mixed statement is about to update a transactional table and
      read from a temporary non-transactional table.

      7. The mixed statement is about to update a temporary transactional
      table and temporary non-transactional table.

      8. The mixed statement is about to update a temporary transactional
      table and read from a temporary non-transactional table.

    After updating a transactional table if:

      9. The mixed statement is about to update a non-transactional table
      and read from a transactional table.

      10. The mixed statement is about to update a non-transactional table
      and read from a temporary transactional table.

      11. The mixed statement is about to update a temporary non-transactional
      table and read from a transactional table.

      12. The mixed statement is about to update a temporary non-transactional
      table and read from a temporary transactional table.

      13. The mixed statement is about to update a temporary non-transactional
      table and read from a non-transactional table.

    The reason for this is that locks acquired may not protected a concurrent
    transaction of interfering in the current execution and by consequence in
    the result.
  */
  /* Case 1. */
  UNSAFE(LEX::STMT_WRITES_TRANS_TABLE, LEX::STMT_WRITES_NON_TRANS_TABLE,
    BINLOG_DIRECT_ON | BINLOG_DIRECT_OFF);
  /* Case 2. */
  UNSAFE(LEX::STMT_WRITES_TRANS_TABLE, LEX::STMT_READS_NON_TRANS_TABLE,
    BINLOG_DIRECT_ON | BINLOG_DIRECT_OFF);
  /* Case 3. */
  UNSAFE(LEX::STMT_WRITES_NON_TRANS_TABLE, LEX::STMT_WRITES_TEMP_TRANS_TABLE,
    BINLOG_DIRECT_ON | BINLOG_DIRECT_OFF);
  /* Case 4. */
  UNSAFE(LEX::STMT_WRITES_TEMP_TRANS_TABLE, LEX::STMT_READS_NON_TRANS_TABLE,
    BINLOG_DIRECT_ON | BINLOG_DIRECT_OFF);
  /* Case 5. */
  UNSAFE(LEX::STMT_WRITES_TRANS_TABLE, LEX::STMT_WRITES_TEMP_NON_TRANS_TABLE,
    BINLOG_DIRECT_ON);
  /* Case 6. */
  UNSAFE(LEX::STMT_WRITES_TRANS_TABLE, LEX::STMT_READS_TEMP_NON_TRANS_TABLE,
    BINLOG_DIRECT_ON);
  /* Case 7. */
  UNSAFE(LEX::STMT_WRITES_TEMP_TRANS_TABLE, LEX::STMT_WRITES_TEMP_NON_TRANS_TABLE,
    BINLOG_DIRECT_ON);
  /* Case 8. */
  UNSAFE(LEX::STMT_WRITES_TEMP_TRANS_TABLE, LEX::STMT_READS_TEMP_NON_TRANS_TABLE,
    BINLOG_DIRECT_ON);
  /* Case 9. */
  UNSAFE(LEX::STMT_WRITES_NON_TRANS_TABLE, LEX::STMT_READS_TRANS_TABLE,
    (BINLOG_DIRECT_ON | BINLOG_DIRECT_OFF) & TRX_CACHE_NOT_EMPTY);
  /* Case 10 */
  UNSAFE(LEX::STMT_WRITES_NON_TRANS_TABLE, LEX::STMT_READS_TEMP_TRANS_TABLE,
    (BINLOG_DIRECT_ON | BINLOG_DIRECT_OFF) & TRX_CACHE_NOT_EMPTY);
  /* Case 11. */
  UNSAFE(LEX::STMT_WRITES_TEMP_NON_TRANS_TABLE, LEX::STMT_READS_TRANS_TABLE,
    BINLOG_DIRECT_ON & TRX_CACHE_NOT_EMPTY);
  /* Case 12. */
  UNSAFE(LEX::STMT_WRITES_TEMP_NON_TRANS_TABLE, LEX::STMT_READS_TEMP_TRANS_TABLE,
    BINLOG_DIRECT_ON & TRX_CACHE_NOT_EMPTY);
  /* Case 13. */
  UNSAFE(LEX::STMT_WRITES_TEMP_NON_TRANS_TABLE, LEX::STMT_READS_NON_TRANS_TABLE,
     BINLOG_DIRECT_OFF & TRX_CACHE_NOT_EMPTY);
}


my_unescape(CHARSET_INFO *cs, char *to, const char *str, const char *end,
            int sep, bool backslash_escapes)
{
  char *start= to;
  for ( ; str != end ; str++)
  {
#ifdef USE_MB
    int l;
    if (use_mb(cs) && (l= my_ismbchar(cs, str, end)))
    {
      while (l--)
        *to++ = *str++;
      str--;
      continue;
    }
#endif
    if (backslash_escapes && *str == '\\' && str + 1 != end)
    {
      switch(*++str) {
      case 'n':
        *to++='\n';
        break;
      case 't':
        *to++= '\t';
        break;
      case 'r':
        *to++ = '\r';
        break;
      case 'b':
        *to++ = '\b';
        break;
      case '0':
        *to++= 0;                      // Ascii null
        break;
      case 'Z':                        // ^Z must be escaped on Win32
        *to++='\032';
        break;
      case '_':
      case '%':
        *to++= '\\';                   // remember prefix for wildcard
        /* Fall through */
      default:
        *to++= *str;
        break;
      }
    }
    else if (*str == sep)
      *to++= *str++;                // Two ' or "
    else
      *to++ = *str;
  }
  *to= 0;
  return to - start;
}


void LEX::restore_backup_query_tables_list(Query_tables_list *backup)
{
  this->destroy_query_tables_list();
  this->set_query_tables_list(backup);
}


void unsafe_mixed_statement(LEX::enum_stmt_accessed_table a,
                            LEX::enum_stmt_accessed_table b, uint condition)
{
  int type= 0;
  int index= (1U << a) | (1U << b);

  for (type= 0; type < 256; type++)
  {
    if ((type & index) == index)
    {
      binlog_unsafe_map[type] |= condition;
    }
  }
}


void st_select_lex::init_select()
{
  st_select_lex_node::init_select();
  sj_nests.empty();
  sj_subselects.empty();
  group_list.empty();
  if (group_list_ptrs)
    group_list_ptrs->clear();
  type= db= 0;
  having= 0;
  table_join_options= 0;
  in_sum_expr= with_wild= 0;
  options= 0;
  sql_cache= SQL_CACHE_UNSPECIFIED;
  ftfunc_list_alloc.empty();
  inner_sum_func_list= 0;
  ftfunc_list= &ftfunc_list_alloc;
  linkage= UNSPECIFIED_TYPE;
  order_list.elements= 0;
  order_list.first= 0;
  order_list.next= &order_list.first;
  /* Set limit and offset to default values */
  select_limit= 0;      /* denotes the default limit = HA_POS_ERROR */
  offset_limit= 0;      /* denotes the default offset = 0 */
  with_sum_func= 0;
  with_all_modifier= 0;
  is_correlated= 0;
  cur_pos_in_select_list= UNDEF_POS;
  cond_value= having_value= Item::COND_UNDEF;
  inner_refs_list.empty();
  insert_tables= 0;
  merged_into= 0;
  m_non_agg_field_used= false;
  m_agg_func_used= false;
  name_visibility_map= 0;
  with_dep= 0;
  join= 0;
  lock_type= TL_READ_DEFAULT;
}


bool LEX::can_use_merged()
{
  switch (sql_command)
  {
  case SQLCOM_SELECT:
  case SQLCOM_CREATE_TABLE:
  case SQLCOM_UPDATE:
  case SQLCOM_UPDATE_MULTI:
  case SQLCOM_DELETE:
  case SQLCOM_DELETE_MULTI:
  case SQLCOM_INSERT:
  case SQLCOM_INSERT_SELECT:
  case SQLCOM_REPLACE:
  case SQLCOM_REPLACE_SELECT:
  case SQLCOM_LOAD:
    return TRUE;
  default:
    return FALSE;
  }
}


Lex_input_stream::reset(char *buffer, unsigned int length)
{
  yylineno= 1;
  yylval= NULL;
  lookahead_token= -1;
  lookahead_yylval= NULL;
  m_ptr= buffer;
  m_tok_start= NULL;
  m_tok_end= NULL;
  m_end_of_query= buffer + length;
  m_tok_start_prev= NULL;
  m_buf= buffer;
  m_buf_length= length;
  m_echo= TRUE;
  m_cpp_tok_start= NULL;
  m_cpp_tok_start_prev= NULL;
  m_cpp_tok_end= NULL;
  m_body_utf8= NULL;
  m_cpp_utf8_processed_ptr= NULL;
  next_state= MY_LEX_START;
  found_semicolon= NULL;
  ignore_space= MY_TEST(m_thd->variables.sql_mode & MODE_IGNORE_SPACE);
  stmt_prepare_mode= FALSE;
  multi_statements= TRUE;
  in_comment=NO_COMMENT;
  m_underscore_cs= NULL;
  m_cpp_ptr= m_cpp_buf;
}


int my_wc_mb_utf8_with_escape(CHARSET_INFO *cs, my_wc_t escape, my_wc_t wc,
                              uchar *str, uchar *end)
{
  DBUG_ASSERT(escape > 0);
  if (str + 1 >= end)
    return MY_CS_TOOSMALL2;  // Not enough space, need at least two bytes.
  *str= (uchar)escape;
  int cnvres= my_charset_utf8_handler.wc_mb(cs, wc, str + 1, end);
  if (cnvres > 0)
    return cnvres + 1;       // The character was normally put
  if (cnvres == MY_CS_ILUNI)
    return MY_CS_ILUNI;      // Could not encode "wc" (e.g. non-BMP character)
  DBUG_ASSERT(cnvres <= MY_CS_TOOSMALL);
  return cnvres - 1;         // Not enough space
}


List<Item>* st_select_lex_node::get_item_list()      { return 0; }


void st_select_lex::replace_leaf_table(TABLE_LIST *table, List<TABLE_LIST> &tbl_list)
{
  TABLE_LIST *tl;
  List_iterator<TABLE_LIST> ti(leaf_tables);
  while ((tl= ti++))
  {
    if (tl == table)
    {
      ti.replace(tbl_list);
      break;
    }
  }
}


void Lex_input_stream::body_utf8_append(const char *ptr)
{
  body_utf8_append(ptr, ptr);
}


bool SELECT_LEX::merge_subquery(THD *thd, TABLE_LIST *derived,
                                SELECT_LEX *subq_select,
                                uint table_no, table_map map)
{
  derived->wrap_into_nested_join(subq_select->top_join_list);

  ftfunc_list->append(subq_select->ftfunc_list);
  if (join ||
      thd->lex->sql_command == SQLCOM_UPDATE_MULTI ||
      thd->lex->sql_command == SQLCOM_DELETE_MULTI)
  {
    List_iterator_fast<Item_in_subselect> li(subq_select->sj_subselects);
    Item_in_subselect *in_subq;
    while ((in_subq= li++))
    {
      sj_subselects.push_back(in_subq, thd->mem_root);
      if (in_subq->emb_on_expr_nest == NO_JOIN_NEST)
         in_subq->emb_on_expr_nest= derived;
    }
  }

  /* Walk through child's tables and adjust table map, tablenr,
   * parent_lex */
  subq_select->remap_tables(derived, map, table_no, this);
  subq_select->merged_into= this;

  replace_leaf_table(derived, subq_select->leaf_tables);

  return FALSE;
}


ha_rows st_select_lex::get_limit()
{
  ulonglong val= HA_POS_ERROR;

  if (select_limit)
  {
    /*
      fix_fields() has not been called for select_limit. That's due to the
      historical reasons -- this item could be only of type Item_int, and
      Item_int does not require fix_fields(). Thus, fix_fields() was never
      called for select_limit.

      Some time ago, Item_splocal was also allowed for LIMIT / OFFSET clauses.
      However, the fix_fields() behavior was not updated, which led to a crash
      in some cases.

      There is no single place where to call fix_fields() for LIMIT / OFFSET
      items during the fix-fields-phase. Thus, for the sake of readability,
      it was decided to do it here, on the evaluation phase (which is a
      violation of design, but we chose the lesser of two evils).

      We can call fix_fields() here, because select_limit can be of two
      types only: Item_int and Item_splocal. Item_int::fix_fields() is trivial,
      and Item_splocal::fix_fields() (or rather Item_sp_variable::fix_fields())
      has the following properties:
        1) it does not affect other items;
        2) it does not fail.

      Nevertheless DBUG_ASSERT was added to catch future changes in
      fix_fields() implementation. Also added runtime check against a result
      of fix_fields() in order to handle error condition in non-debug build.
    */
    bool fix_fields_successful= true;
    if (!select_limit->fixed)
    {
      fix_fields_successful= !select_limit->fix_fields(master_unit()->thd,
                                                       NULL);

      DBUG_ASSERT(fix_fields_successful);
    }
    val= fix_fields_successful ? select_limit->val_uint() : HA_POS_ERROR;
  }

  return (ha_rows)val;
}


void st_select_lex_node::exclude()
{
  /* exclude from global list */
  fast_exclude();
  /* exclude from other structures */
  exclude_from_tree();
  /*
     We do not need following statements, because prev pointer of first
     list element point to master->slave
     if (master->slave == this)
       master->slave= next;
  */
}


static inline uint int_token(const char *str,uint length)
{
  if (length < long_len)			// quick normal case
    return NUM;
  bool neg=0;

  if (*str == '+')				// Remove sign and pre-zeros
  {
    str++; length--;
  }
  else if (*str == '-')
  {
    str++; length--;
    neg=1;
  }
  while (*str == '0' && length)
  {
    str++; length --;
  }
  if (length < long_len)
    return NUM;

  uint smaller,bigger;
  const char *cmp;
  if (neg)
  {
    if (length == long_len)
    {
      cmp= signed_long_str+1;
      smaller=NUM;				// If <= signed_long_str
      bigger=LONG_NUM;				// If >= signed_long_str
    }
    else if (length < signed_longlong_len)
      return LONG_NUM;
    else if (length > signed_longlong_len)
      return DECIMAL_NUM;
    else
    {
      cmp=signed_longlong_str+1;
      smaller=LONG_NUM;				// If <= signed_longlong_str
      bigger=DECIMAL_NUM;
    }
  }
  else
  {
    if (length == long_len)
    {
      cmp= long_str;
      smaller=NUM;
      bigger=LONG_NUM;
    }
    else if (length < longlong_len)
      return LONG_NUM;
    else if (length > longlong_len)
    {
      if (length > unsigned_longlong_len)
        return DECIMAL_NUM;
      cmp=unsigned_longlong_str;
      smaller=ULONGLONG_NUM;
      bigger=DECIMAL_NUM;
    }
    else
    {
      cmp=longlong_str;
      smaller=LONG_NUM;
      bigger= ULONGLONG_NUM;
    }
  }
  while (*cmp && *cmp++ == *str++) ;
  return ((uchar) str[-1] <= (uchar) cmp[-1]) ? smaller : bigger;
}


void SELECT_LEX::mark_const_derived(bool empty)
{
  TABLE_LIST *derived= master_unit()->derived;
  /* join == NULL in  DELETE ... RETURNING */
  if (!(join && join->thd->lex->describe) && derived)
  {
    if (!empty)
      increase_derived_records(1);
    if (!master_unit()->is_union() && !derived->is_merged_derived())
      derived->fill_me= TRUE;
  }
}


static int find_keyword(Lex_input_stream *lip, uint len, bool function)
{
  const char *tok= lip->get_tok_start();

  SYMBOL *symbol= get_hash_symbol(tok, len, function);
  if (symbol)
  {
    lip->yylval->symbol.symbol=symbol;
    lip->yylval->symbol.str= (char*) tok;
    lip->yylval->symbol.length=len;

    if ((symbol->tok == NOT_SYM) &&
        (lip->m_thd->variables.sql_mode & MODE_HIGH_NOT_PRECEDENCE))
      return NOT2_SYM;
    if ((symbol->tok == OR_OR_SYM) &&
	!(lip->m_thd->variables.sql_mode & MODE_PIPES_AS_CONCAT))
      return OR2_SYM;

    return symbol->tok;
  }
  return 0;
}


bool LEX::is_partition_management() const
{
  return (sql_command == SQLCOM_ALTER_TABLE &&
          (alter_info.flags ==  Alter_info::ALTER_ADD_PARTITION ||
           alter_info.flags ==  Alter_info::ALTER_REORGANIZE_PARTITION));
}


int st_select_lex_unit::save_union_explain(Explain_query *output)
{
  SELECT_LEX *first= first_select();

  if (output->get_union(first->select_number))
    return 0; /* Already added */

  Explain_union *eu=
    new (output->mem_root) Explain_union(output->mem_root,
                                         thd->lex->analyze_stmt);

  if (with_element && with_element->is_recursive)
    eu->is_recursive_cte= true;

  if (derived)
    eu->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
  /*
    Note: Non-merged semi-joins cannot be made out of UNIONs currently, so we
    dont ever set EXPLAIN_NODE_NON_MERGED_SJ.
  */

  for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
    eu->add_select(sl->select_number);

  eu->fake_select_type= "UNION RESULT";
  eu->using_filesort= MY_TEST(global_parameters()->order_list.first);
  eu->using_tmp= union_needs_tmp_table();

  // Save the UNION node
  output->add_node(eu);

  if (eu->get_select_id() == 1)
    output->query_plan_ready();

  return 0;
}


int my_wc_mb_utf8_escape_double_quote(CHARSET_INFO *cs, my_wc_t wc,
                                      uchar *str, uchar *end)
{
  return my_wc_mb_utf8_escape(cs, wc, str, end, '"', 0);
}


Lex_input_stream::get_escape_func(THD *thd, my_wc_t sep) const
{
  return thd->backslash_escapes() ?
         (sep == '"' ? my_wc_mb_utf8_escape_double_quote_and_backslash:
                       my_wc_mb_utf8_escape_single_quote_and_backslash) :
         (sep == '"' ? my_wc_mb_utf8_escape_double_quote:
                       my_wc_mb_utf8_escape_single_quote);
}


bool st_select_lex::optimize_unflattened_subqueries(bool const_only)
{
  SELECT_LEX_UNIT *next_unit= NULL;
  for (SELECT_LEX_UNIT *un= first_inner_unit();
       un;
       un= next_unit ? next_unit : un->next_unit())
  {
    Item_subselect *subquery_predicate= un->item;
    next_unit= NULL;

    if (subquery_predicate)
    {
      if (!subquery_predicate->fixed)
      {
	/*
	 This subquery was excluded as part of some expression so it is
	 invisible from all prepared expression.
       */
	next_unit= un->next_unit();
	un->exclude_level();
	if (next_unit)
	  continue;
	break;
      }
      if (subquery_predicate->substype() == Item_subselect::IN_SUBS)
      {
        Item_in_subselect *in_subs= (Item_in_subselect*) subquery_predicate;
        if (in_subs->is_jtbm_merged)
          continue;
      }

      if (const_only && !subquery_predicate->const_item())
      {
        /* Skip non-constant subqueries if the caller asked so. */
        continue;
      }

      bool empty_union_result= true;
      bool is_correlated_unit= false;
      bool first= true;
      bool union_plan_saved= false;
      /*
        If the subquery is a UNION, optimize all the subqueries in the UNION. If
        there is no UNION, then the loop will execute once for the subquery.
      */
      for (SELECT_LEX *sl= un->first_select(); sl; sl= sl->next_select())
      {
        JOIN *inner_join= sl->join;
        if (first)
          first= false;
        else
        {
          if (!union_plan_saved)
          {
            union_plan_saved= true;
            if (un->save_union_explain(un->thd->lex->explain))
              return true; /* Failure */
          }
        }
        if (!inner_join)
          continue;
        SELECT_LEX *save_select= un->thd->lex->current_select;
        ulonglong save_options;
        int res;
        /* We need only 1 row to determine existence */
        un->set_limit(un->global_parameters());
        un->thd->lex->current_select= sl;
        save_options= inner_join->select_options;
        if (options & SELECT_DESCRIBE)
        {
          /* Optimize the subquery in the context of EXPLAIN. */
          sl->set_explain_type(FALSE);
          sl->options|= SELECT_DESCRIBE;
          inner_join->select_options|= SELECT_DESCRIBE;
        }
        if ((res= inner_join->optimize()))
          return TRUE;
        if (!inner_join->cleaned)
          sl->update_used_tables();
        sl->update_correlated_cache();
        is_correlated_unit|= sl->is_correlated;
        inner_join->select_options= save_options;
        un->thd->lex->current_select= save_select;

        Explain_query *eq;
        if ((eq= inner_join->thd->lex->explain))
        {
          Explain_select *expl_sel;
          if ((expl_sel= eq->get_select(inner_join->select_lex->select_number)))
          {
            sl->set_explain_type(TRUE);
            expl_sel->select_type= sl->type;
          }
        }

        if (empty_union_result)
        {
          /*
            If at least one subquery in a union is non-empty, the UNION result
            is non-empty. If there is no UNION, the only subquery is non-empy.
          */
          empty_union_result= inner_join->empty_result();
        }
        if (res)
          return TRUE;
      }
      if (empty_union_result)
        subquery_predicate->no_rows_in_result();

      if (is_correlated_unit)
      {
        /*
          Some parts of UNION are not correlated. This means we will need to
          re-execute the whole UNION every time. Mark all parts of the UNION
          as correlated so that they are prepared to be executed multiple
          times (if we don't do that, some part of the UNION may free its
          execution data at the end of first execution and crash on the second
          execution)
        */
        for (SELECT_LEX *sl= un->first_select(); sl; sl= sl->next_select())
          sl->uncacheable |= UNCACHEABLE_DEPENDENT;
      }
      else
        un->uncacheable&= ~UNCACHEABLE_DEPENDENT;
      subquery_predicate->is_correlated= is_correlated_unit;
    }
  }
  return FALSE;
}


void st_select_lex::print_order(String *str,
                                ORDER *order,
                                enum_query_type query_type)
{
  for (; order; order= order->next)
  {
    if (order->counter_used)
    {
      char buffer[20];
      size_t length= my_snprintf(buffer, 20, "%d", order->counter);
      str->append(buffer, (uint) length);
    }
    else
    {
      /* replace numeric reference with equivalent for ORDER constant */
      if (order->item[0]->type() == Item::INT_ITEM &&
          order->item[0]->basic_const_item())
      {
        /* make it expression instead of integer constant */
        str->append(STRING_WITH_LEN("''"));
      }
      else
        (*order->item)->print(str, query_type);
    }
    if (order->direction == ORDER::ORDER_DESC)
       str->append(STRING_WITH_LEN(" desc"));
    if (order->next)
      str->append(',');
  }
}


bool st_select_lex_unit::union_needs_tmp_table()
{
  if (with_element && with_element->is_recursive)
    return true;
  return union_distinct != NULL ||
    global_parameters()->order_list.elements != 0 ||
    thd->lex->sql_command == SQLCOM_INSERT_SELECT ||
    thd->lex->sql_command == SQLCOM_REPLACE_SELECT;
}


List<Item>* st_select_lex::get_item_list()
{
  return &item_list;
}


bool st_select_lex::test_limit()
{
  if (select_limit != 0)
  {
    my_error(ER_NOT_SUPPORTED_YET, MYF(0),
             "LIMIT & IN/ALL/ANY/SOME subquery");
    return(1);
  }
  return(0);
}


TABLE_LIST* st_select_lex::get_table_list()
{
  return table_list.first;
}


int st_select_lex_unit::save_union_explain_part2(Explain_query *output)
{
  Explain_union *eu= output->get_union(first_select()->select_number);
  if (fake_select_lex)
  {
    for (SELECT_LEX_UNIT *unit= fake_select_lex->first_inner_unit();
         unit; unit= unit->next_unit())
    {
      if (!(unit->item && unit->item->eliminated))
      {
        eu->add_child(unit->first_select()->select_number);
      }
    }
    fake_select_lex->join->explain= &eu->fake_select_lex_explain;
  }
  return 0;
}


bool consume_comment(Lex_input_stream *lip, int remaining_recursions_permitted)
{
  // only one level of nested comments are allowed
  DBUG_ASSERT(remaining_recursions_permitted == 0 ||
              remaining_recursions_permitted == 1);
  uchar c;
  while (! lip->eof())
  {
    c= lip->yyGet();

    if (remaining_recursions_permitted == 1)
    {
      if ((c == '/') && (lip->yyPeek() == '*'))
      {
        lip->yyUnput('(');  // Replace nested "/*..." with "(*..."
        lip->yySkip();      // and skip "("

        lip->yySkip(); /* Eat asterisk */
        if (consume_comment(lip, 0))
          return true;

        lip->yyUnput(')');  // Replace "...*/" with "...*)"
        lip->yySkip();      // and skip ")"
        continue;
      }
    }

    if (c == '*')
    {
      if (lip->yyPeek() == '/')
      {
        lip->yySkip(); /* Eat slash */
        return FALSE;
      }
    }

    if (c == '\n')
      lip->yylineno++;
  }

  return TRUE;
}


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,
                                                  enum_mdl_type mdl_type,
						  List<Index_hint> *hints,
                                                  List<String> *partition_names,
                                                  LEX_STRING *option)
{
  return 0;
}


void st_select_lex::set_index_hint_type(enum index_hint_type type_arg,
                                        index_clause_map clause)
{
  current_index_hint_type= type_arg;
  current_index_hint_clause= clause;
}


bool st_select_lex::mark_as_dependent(THD *thd, st_select_lex *last,
                                      Item_ident *dependency)
{

  DBUG_ASSERT(this != last);

  /*
    Mark all selects from resolved to 1 before select where was
    found table as depended (of select where was found table)

    We move by name resolution context, bacause during merge can some select
    be excleded from SELECT tree
  */
  Name_resolution_context *c= &this->context;
  do
  {
    SELECT_LEX *s= c->select_lex;
    if (!(s->uncacheable & UNCACHEABLE_DEPENDENT_GENERATED))
    {
      // Select is dependent of outer select
      s->uncacheable= (s->uncacheable & ~UNCACHEABLE_UNITED) |
                       UNCACHEABLE_DEPENDENT_GENERATED;
      SELECT_LEX_UNIT *munit= s->master_unit();
      munit->uncacheable= (munit->uncacheable & ~UNCACHEABLE_UNITED) |
                       UNCACHEABLE_DEPENDENT_GENERATED;
      for (SELECT_LEX *sl= munit->first_select(); sl ; sl= sl->next_select())
      {
        if (sl != s &&
            !(sl->uncacheable & (UNCACHEABLE_DEPENDENT_GENERATED |
                                 UNCACHEABLE_UNITED)))
          sl->uncacheable|= UNCACHEABLE_UNITED;
      }
    }

    Item_subselect *subquery_expr= s->master_unit()->item;
    if (subquery_expr && subquery_expr->mark_as_dependent(thd, last,
                                                          dependency))
      return TRUE;
  } while ((c= c->outer_context) != NULL && (c->select_lex != last));
  is_correlated= TRUE;
  this->master_unit()->item->is_correlated= TRUE;
  return FALSE;
}


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;
}


void st_select_lex::set_unique_exclude()
{
  exclude_from_table_unique_test= TRUE;
  for (SELECT_LEX_UNIT *unit= first_inner_unit();
       unit;
       unit= unit->next_unit())
  {
    if (unit->derived && unit->derived->is_view_or_derived())
    {
      for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select())
        sl->set_unique_exclude();
    }
  }
}


void Lex_input_stream::body_utf8_append_ident(THD *thd,
                                              const LEX_STRING *txt,
                                              const char *end_ptr)
{
  if (!m_cpp_utf8_processed_ptr)
    return;

  LEX_STRING utf_txt;
  CHARSET_INFO *txt_cs= thd->charset();

  if (!my_charset_same(txt_cs, &my_charset_utf8_general_ci))
  {
    thd->convert_string(&utf_txt,
                        &my_charset_utf8_general_ci,
                        txt->str, (uint) txt->length,
                        txt_cs);
  }
  else
  {
    utf_txt.str= txt->str;
    utf_txt.length= txt->length;
  }

  /* NOTE: utf_txt.length is in bytes, not in symbols. */

  memcpy(m_body_utf8_ptr, utf_txt.str, utf_txt.length);
  m_body_utf8_ptr += utf_txt.length;
  *m_body_utf8_ptr= 0;

  m_cpp_utf8_processed_ptr= end_ptr;
}


ulong st_select_lex_node::get_table_join_options()
{
  return 0;
}


void LEX::first_lists_tables_same()
{
  TABLE_LIST *first_table= select_lex.table_list.first;
  if (query_tables != first_table && first_table != 0)
  {
    TABLE_LIST *next;
    if (query_tables_last == &first_table->next_global)
      query_tables_last= first_table->prev_global;

    if (query_tables_own_last == &first_table->next_global)
      query_tables_own_last= first_table->prev_global;

    if ((next= *first_table->prev_global= first_table->next_global))
      next->prev_global= first_table->prev_global;
    /* include in new place */
    first_table->next_global= query_tables;
    /*
       We are sure that query_tables is not 0, because first_table was not
       first table in the global list => we can use
       query_tables->prev_global without check of query_tables
    */
    query_tables->prev_global= &first_table->next_global;
    first_table->prev_global= &query_tables;
    query_tables= first_table;
  }
}


bool LEX::table_or_sp_used()
{
  DBUG_ENTER("table_or_sp_used");

  if (sroutines.records || query_tables)
    DBUG_RETURN(TRUE);

  DBUG_RETURN(FALSE);
}


uint st_select_lex_node::get_in_sum_expr()           { return 0; }


bool st_select_lex::handle_derived(LEX *lex, uint phases)
{
  return lex->handle_list_of_derived(table_list.first, phases);
}


bool LEX::set_arena_for_set_stmt(Query_arena *backup)
{
  DBUG_ENTER("LEX::set_arena_for_set_stmt");
  DBUG_ASSERT(arena_for_set_stmt== 0);
  if (!mem_root_for_set_stmt)
  {
    mem_root_for_set_stmt= new MEM_ROOT();
    if (!(mem_root_for_set_stmt))
      DBUG_RETURN(1);
    init_sql_alloc(mem_root_for_set_stmt, ALLOC_ROOT_SET, ALLOC_ROOT_SET,
                   MYF(MY_THREAD_SPECIFIC));
  }
  if (!(arena_for_set_stmt= new(mem_root_for_set_stmt)
        Query_arena_memroot(mem_root_for_set_stmt,
                            Query_arena::STMT_INITIALIZED)))
    DBUG_RETURN(1);
  DBUG_PRINT("info", ("mem_root: %p  arena: %p",
                      mem_root_for_set_stmt,
                      arena_for_set_stmt));
  thd->set_n_backup_active_arena(arena_for_set_stmt, backup);
  DBUG_RETURN(0);
}


init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex)
{
  TABLE_LIST *table_list;
  Table_ident *table_ident;
  SELECT_LEX *select_lex= &lex->select_lex;
  Name_resolution_context *context= &select_lex->context;
  /*
    We will call the parser to create a part_info struct based on the
    partition string stored in the frm file.
    We will use a local lex object for this purpose. However we also
    need to set the Name_resolution_object for this lex object. We
    do this by using add_table_to_list where we add the table that
    we're working with to the Name_resolution_context.
  */
  thd->lex= lex;
  lex_start(thd);
  context->init();
  if ((!(table_ident= new Table_ident(thd,
                                      table->s->table_name,
                                      table->s->db, TRUE))) ||
      (!(table_list= select_lex->add_table_to_list(thd,
                                                   table_ident,
                                                   NULL,
                                                   0))))
    return TRUE;
  context->resolve_in_table_list_only(table_list);
  lex->use_only_table_context= TRUE;
  lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VCOL_EXPR;
  select_lex->cur_pos_in_select_list= UNDEF_POS;
  table->map= 1; //To ensure correct calculation of const item
  table_list->table= table;
  table_list->cacheable_table= false;
  return FALSE;
}


LEX::LEX()
  : explain(NULL),
    result(0), arena_for_set_stmt(0), mem_root_for_set_stmt(0),
    option_type(OPT_DEFAULT), context_analysis_only(0), sphead(0),
    is_lex_started(0), limit_rows_examined_cnt(ULONGLONG_MAX)
{

  init_dynamic_array2(&plugins, sizeof(plugin_ref), plugins_static_buffer,
                      INITIAL_LEX_PLUGIN_LIST_SIZE,
                      INITIAL_LEX_PLUGIN_LIST_SIZE, 0);
  reset_query_tables_list(TRUE);
  mi.init();
  init_dynamic_array2(&delete_gtid_domain, sizeof(uint32),
                      gtid_domain_static_buffer,
                      initial_gtid_domain_buffer_size,
                      initial_gtid_domain_buffer_size, 0);
}


int my_wc_mb_utf8_escape_double_quote_and_backslash(CHARSET_INFO *cs,
                                                    my_wc_t wc,
                                                    uchar *str, uchar *end)
{
  return my_wc_mb_utf8_escape(cs, wc, str, end, '"', '\\');
}


int LEX::print_explain(select_result_sink *output, uint8 explain_flags,
                       bool is_analyze, bool *printed_anything)
{
  int res;
  if (explain && explain->have_query_plan())
  {
    res= explain->print_explain(output, explain_flags, is_analyze);
    *printed_anything= true;
  }
  else
  {
    res= 0;
    *printed_anything= false;
  }
  return res;
}


bool LEX::only_view_structure()
{
  switch (sql_command) {
  case SQLCOM_SHOW_CREATE:
  case SQLCOM_SHOW_TABLES:
  case SQLCOM_SHOW_FIELDS:
  case SQLCOM_REVOKE_ALL:
  case SQLCOM_REVOKE:
  case SQLCOM_GRANT:
  case SQLCOM_CREATE_VIEW:
    return TRUE;
  default:
    return FALSE;
  }
}


bool st_select_lex::add_gorder_to_list(THD *thd, Item *item, bool asc)
{
  return add_to_list(thd, gorder_list, item, asc);
}


void Lex_input_stream::add_digest_token(uint token, LEX_YYSTYPE yylval)
{
  if (m_digest != NULL)
  {
    m_digest= digest_add_token(m_digest, token, yylval);
  }
}


inline int lex_casecmp(const char *s, const char *t, uint len)
{
  while (len-- != 0 &&
	 to_upper_lex[(uchar) *s++] == to_upper_lex[(uchar) *t++]) ;
  return (int) len+1;
}


int my_wc_mb_utf8_escape_single_quote(CHARSET_INFO *cs, my_wc_t wc,
                                      uchar *str, uchar *end)
{
  return my_wc_mb_utf8_escape(cs, wc, str, end, '\'', 0);
}


void trim_whitespace(CHARSET_INFO *cs, LEX_STRING *str, uint *prefix_length)
{
  /*
    TODO:
    This code assumes that there are no multi-bytes characters
    that can be considered white-space.
  */

  *prefix_length= 0;
  while ((str->length > 0) && (my_isspace(cs, str->str[0])))
  {
    (*prefix_length)++;
    str->length --;
    str->str ++;
  }

  /*
    FIXME:
    Also, parsing backward is not safe with multi bytes characters
  */
  while ((str->length > 0) && (my_isspace(cs, str->str[str->length-1])))
  {
    str->length --;
  }
}


void st_select_lex::fix_prepare_information(THD *thd, Item **conds,
                                            Item **having_conds)
{
  DBUG_ENTER("st_select_lex::fix_prepare_information");
  if (!thd->stmt_arena->is_conventional() &&
      !(changed_elements & TOUCHED_SEL_COND))
  {
    changed_elements|= TOUCHED_SEL_COND;
    if (group_list.first)
    {
      if (!group_list_ptrs)
      {
        void *mem= thd->stmt_arena->alloc(sizeof(Group_list_ptrs));
        group_list_ptrs= new (mem) Group_list_ptrs(thd->stmt_arena->mem_root);
      }
      group_list_ptrs->reserve(group_list.elements);
      for (ORDER *order= group_list.first; order; order= order->next)
      {
        group_list_ptrs->push_back(order);
      }
    }
    if (*conds)
    {
      thd->check_and_register_item_tree(&prep_where, conds);
      *conds= where= prep_where->copy_andor_structure(thd);
    }
    if (*having_conds)
    {
      thd->check_and_register_item_tree(&prep_having, having_conds);
      *having_conds= having= prep_having->copy_andor_structure(thd);
    }
    fix_prepare_info_in_table_list(thd, table_list.first);
  }
  DBUG_VOID_RETURN;
}


bool LEX::need_correct_ident()
{
  switch(sql_command)
  {
  case SQLCOM_SHOW_CREATE:
  case SQLCOM_SHOW_TABLES:
  case SQLCOM_CREATE_VIEW:
    return TRUE;
  default:
    return FALSE;
  }
}


TABLE_LIST* st_select_lex_node::get_table_list()     { return 0; }


bool is_lex_native_function(const LEX_STRING *name)
{
  DBUG_ASSERT(name != NULL);
  return (get_hash_symbol(name->str, (uint) name->length, 1) != 0);
}


void LEX::cleanup_lex_after_parse_error(THD *thd)
{
  /*
    Delete sphead for the side effect of restoring of the original
    LEX state, thd->lex, thd->mem_root and thd->free_list if they
    were replaced when parsing stored procedure statements.  We
    will never use sphead object after a parse error, so it's okay
    to delete it only for the sake of the side effect.
    TODO: make this functionality explicit in sp_head class.
    Sic: we must nullify the member of the main lex, not the
    current one that will be thrown away
  */
  if (thd->lex->sphead)
  {
    thd->lex->sphead->restore_thd_mem_root(thd);
    sp_head::destroy(thd->lex->sphead);
    thd->lex->sphead= NULL;
  }
}


static LEX_STRING get_quoted_token(Lex_input_stream *lip,
                                   uint skip,
                                   uint length, char quote)
{
  LEX_STRING tmp;
  const char *from, *end;
  char *to;
  lip->yyUnget();                       // ptr points now after last token char
  tmp.length= length;
  tmp.str=(char*) lip->m_thd->alloc(tmp.length+1);
  from= lip->get_tok_start() + skip;
  to= tmp.str;
  end= to+length;

  lip->m_cpp_text_start= lip->get_cpp_tok_start() + skip;
  lip->m_cpp_text_end= lip->m_cpp_text_start + length;

  for ( ; to != end; )
  {
    if ((*to++= *from++) == quote)
    {
      from++;					// Skip double quotes
      lip->m_cpp_text_start++;
    }
  }
  *to= 0;					// End null for safety
  return tmp;
}


void SELECT_LEX::increase_derived_records(ha_rows records)
{
  SELECT_LEX_UNIT *unit= master_unit();
  DBUG_ASSERT(unit->derived);

  if (unit->with_element && unit->with_element->is_recursive)
  {
    st_select_lex *first_recursive= unit->with_element->first_recursive;
    st_select_lex *sl= unit->first_select();
    for ( ; sl != first_recursive; sl= sl->next_select())
    {
      if (sl == this)
        break;
    }
    if (sl == first_recursive)
      return;
  }

  select_union *result= (select_union*)unit->result;
  if (HA_ROWS_MAX - records > result->records)
    result->records+= records;
  else
    result->records= HA_ROWS_MAX;
}


static LEX_STRING get_token(Lex_input_stream *lip, uint skip, uint length)
{
  LEX_STRING tmp;
  lip->yyUnget();                       // ptr points now after last token char
  tmp.length= length;
  tmp.str= lip->m_thd->strmake(lip->get_tok_start() + skip, tmp.length);

  lip->m_cpp_text_start= lip->get_cpp_tok_start() + skip;
  lip->m_cpp_text_end= lip->m_cpp_text_start + tmp.length;

  return tmp;
}


st_select_lex* st_select_lex_unit::outer_select()
{
  return (st_select_lex*) master;
}


void st_select_lex::print_limit(THD *thd,
                                String *str,
                                enum_query_type query_type)
{
  SELECT_LEX_UNIT *unit= master_unit();
  Item_subselect *item= unit->item;

  if (item && unit->global_parameters() == this)
  {
    Item_subselect::subs_type subs_type= item->substype();
    if (subs_type == Item_subselect::IN_SUBS ||
        subs_type == Item_subselect::ALL_SUBS)
    {
      return;
    }
  }
  if (explicit_limit && select_limit)
  {
    str->append(STRING_WITH_LEN(" limit "));
    if (offset_limit)
    {
      offset_limit->print(str, query_type);
      str->append(',');
    }
    select_limit->print(str, query_type);
  }
}


int my_wc_mb_utf8_opt_escape(CHARSET_INFO *cs,
                             my_wc_t wc, my_wc_t escape, my_wc_t ewc,
                             uchar *str, uchar *end)
{
  return escape ? my_wc_mb_utf8_with_escape(cs, escape, ewc, str, end) :
                  my_charset_utf8_handler.wc_mb(cs, wc, str, end);
}


uint st_select_lex::get_in_sum_expr()
{
  return in_sum_expr;
}


bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num)
{

  if (!((options & SELECT_DISTINCT) && !group_list.elements))
    hidden_bit_fields= 0;

  // find_order_in_list() may need some extra space, so multiply by two.
  order_group_num*= 2;

  /*
    We have to create array in prepared statement memory if it is a
    prepared statement
  */
  Query_arena *arena= thd->stmt_arena;
  const size_t n_elems= (n_sum_items +
                       n_child_sum_items +
                       item_list.elements +
                       select_n_reserved +
                       select_n_having_items +
                       select_n_where_fields +
                       order_group_num +
                       hidden_bit_fields +
                       fields_in_window_functions) * (size_t) 5;
  DBUG_ASSERT(n_elems % 5 == 0);
  if (!ref_pointer_array.is_null())
  {
    /*
      We need to take 'n_sum_items' into account when allocating the array,
      and this may actually increase during the optimization phase due to
      MIN/MAX rewrite in Item_in_subselect::single_value_transformer.
      In the usual case we can reuse the array from the prepare phase.
      If we need a bigger array, we must allocate a new one.
     */
    if (ref_pointer_array.size() >= n_elems)
      return false;
   }
  Item **array= static_cast<Item**>(arena->alloc(sizeof(Item*) * n_elems));
  if (array != NULL)
    ref_pointer_array= Ref_ptr_array(array, n_elems);

  return array == NULL;
}


bool st_select_lex::get_free_table_map(table_map *map, uint *tablenr)
{
  *map= 0;
  *tablenr= 0;
  TABLE_LIST *tl;
  List_iterator<TABLE_LIST> ti(leaf_tables);
  while ((tl= ti++))
  {
    if (tl->table->map > *map)
      *map= tl->table->map;
    if (tl->table->tablenr > *tablenr)
      *tablenr= tl->table->tablenr;
  }
  (*map)<<= 1;
  (*tablenr)++;
  if (*tablenr >= MAX_TABLES)
    return TRUE;
  return FALSE;
}


ulong st_select_lex::get_table_join_options()
{
  return table_join_options;
}


void st_select_lex::set_explain_type(bool on_the_fly)
{
  bool is_primary= FALSE;
  if (next_select())
    is_primary= TRUE;

  if (!is_primary && first_inner_unit())
  {
    /*
      If there is at least one materialized derived|view then it's a PRIMARY select.
      Otherwise, all derived tables/views were merged and this select is a SIMPLE one.
    */
    for (SELECT_LEX_UNIT *un= first_inner_unit(); un; un= un->next_unit())
    {
      if ((!un->derived || un->derived->is_materialized_derived()))
      {
        is_primary= TRUE;
        break;
      }
    }
  }

  if (on_the_fly && !is_primary && have_merged_subqueries)
    is_primary= TRUE;

  SELECT_LEX *first= master_unit()->first_select();
  /* drop UNCACHEABLE_EXPLAIN, because it is for internal usage only */
  uint8 is_uncacheable= (uncacheable & ~UNCACHEABLE_EXPLAIN);

  bool using_materialization= FALSE;
  Item_subselect *parent_item;
  if ((parent_item= master_unit()->item) &&
      parent_item->substype() == Item_subselect::IN_SUBS)
  {
    Item_in_subselect *in_subs= (Item_in_subselect*)parent_item;
    /*
      Surprisingly, in_subs->is_set_strategy() can return FALSE here,
      even for the last invocation of this function for the select.
    */
    if (in_subs->test_strategy(SUBS_MATERIALIZATION))
      using_materialization= TRUE;
  }

  if (&master_unit()->thd->lex->select_lex == this)
  {
     type= is_primary ? "PRIMARY" : "SIMPLE";
  }
  else
  {
    if (this == first)
    {
      /* If we're a direct child of a UNION, we're the first sibling there */
      if (linkage == DERIVED_TABLE_TYPE)
        type= "DERIVED";
      else if (using_materialization)
        type= "MATERIALIZED";
      else
      {
         if (is_uncacheable & UNCACHEABLE_DEPENDENT)
           type= "DEPENDENT SUBQUERY";
         else
         {
           type= is_uncacheable? "UNCACHEABLE SUBQUERY" :
                                 "SUBQUERY";
         }
      }
    }
    else
    {
      /* This a non-first sibling in UNION */
      if (is_uncacheable & UNCACHEABLE_DEPENDENT)
        type= "DEPENDENT UNION";
      else if (using_materialization)
        type= "MATERIALIZED UNION";
      else
      {
        type= is_uncacheable ? "UNCACHEABLE UNION": "UNION";
        if (this == master_unit()->fake_select_lex)
          type= "UNION RESULT";
        /*
          join below may be =NULL when this functions is called at an early
          stage. It will be later called again and we will set the correct
          value.
        */
        if (join)
        {
          bool uses_cte= false;
          for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_BUSH_ROOTS,
                                                     WITH_CONST_TABLES);
               tab;
               tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS))
          {
            /*
              pos_in_table_list=NULL for e.g. post-join aggregation JOIN_TABs.
            */
            if (!(tab->table && tab->table->pos_in_table_list))
	      continue;
            TABLE_LIST *tbl= tab->table->pos_in_table_list;
            if (tbl->with && tbl->with->is_recursive &&
                tbl->is_with_table_recursive_reference())
            {
              uses_cte= true;
              break;
            }
          }
          if (uses_cte)
            type= "RECURSIVE UNION";
        }
      }
    }
  }

  if (!on_the_fly)
    options|= SELECT_DESCRIBE;
}


void SELECT_LEX::update_used_tables()
{
  TABLE_LIST *tl;
  List_iterator<TABLE_LIST> ti(leaf_tables);

  while ((tl= ti++))
  {
    if (tl->table && !tl->is_view_or_derived())
    {
      TABLE_LIST *embedding= tl->embedding;
      for (embedding= tl->embedding; embedding; embedding=embedding->embedding)
      {
        if (embedding->is_view_or_derived())
	{
          DBUG_ASSERT(embedding->is_merged_derived());
          TABLE *tab= tl->table;
          tab->covering_keys= tab->s->keys_for_keyread;
          tab->covering_keys.intersect(tab->keys_in_use_for_query);
          /*
            View/derived was merged. Need to recalculate read_set/vcol_set
            bitmaps here. For example:
              CREATE VIEW v1 AS SELECT f1,f2,f3 FROM t1;
              SELECT f1 FROM v1;
            Initially, the view definition will put all f1,f2,f3 in the
            read_set for t1. But after the view is merged, only f1 should
            be in the read_set.
          */
          bitmap_clear_all(tab->read_set);
          if (tab->vcol_set)
            bitmap_clear_all(tab->vcol_set);
          break;
        }
      }
    }
  }

  ti.rewind();
  while ((tl= ti++))
  {
    TABLE_LIST *embedding= tl;
    do
    {
      bool maybe_null;
      if ((maybe_null= MY_TEST(embedding->outer_join)))
      {
	tl->table->maybe_null= maybe_null;
        break;
      }
    }
    while ((embedding= embedding->embedding));
    if (tl->on_expr)
    {
      tl->on_expr->update_used_tables();
      tl->on_expr->walk(&Item::eval_not_null_tables, 0, NULL);
    }
    /*
      - There is no need to check sj_on_expr, because merged semi-joins inject
        sj_on_expr into the parent's WHERE clase.
      - For non-merged semi-joins (aka JTBMs), we need to check their
        left_expr. There is no need to check the rest of the subselect, we know
        it is uncorrelated and so cannot refer to any tables in this select.
    */
    if (tl->jtbm_subselect)
    {
      Item *left_expr= tl->jtbm_subselect->left_expr;
      left_expr->walk(&Item::update_table_bitmaps_processor, FALSE, NULL);
    }

    embedding= tl->embedding;
    while (embedding)
    {
      if (embedding->on_expr &&
          embedding->nested_join->join_list.head() == tl)
      {
        embedding->on_expr->update_used_tables();
        embedding->on_expr->walk(&Item::eval_not_null_tables, 0, NULL);
      }
      tl= embedding;
      embedding= tl->embedding;
    }
  }

  if (join->conds)
  {
    join->conds->update_used_tables();
    join->conds->walk(&Item::eval_not_null_tables, 0, NULL);
  }
  if (join->having)
  {
    join->having->update_used_tables();
  }

  Item *item;
  List_iterator_fast<Item> it(join->all_fields);
  select_list_tables= 0;
  while ((item= it++))
  {
    item->update_used_tables();
    select_list_tables|= item->used_tables();
  }
  Item_outer_ref *ref;
  List_iterator_fast<Item_outer_ref> ref_it(inner_refs_list);
  while ((ref= ref_it++))
  {
    item= ref->outer_ref;
    item->update_used_tables();
  }
  for (ORDER *order= group_list.first; order; order= order->next)
    (*order->item)->update_used_tables();
  if (!master_unit()->is_union() || master_unit()->global_parameters() != this)
  {
    for (ORDER *order= order_list.first; order; order= order->next)
      (*order->item)->update_used_tables();
  }
  join->result->update_used_tables();
}


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;
}


bool st_select_lex::add_group_to_list(THD *thd, Item *item, bool asc)
{
  return add_to_list(thd, group_list, item, asc);
}


st_select_lex::check_cond_extraction_for_grouping_fields(Item *cond,
                                                         TABLE_LIST *derived)
{
  cond->clear_extraction_flag();
  if (cond->type() == Item::COND_ITEM)
  {
    bool and_cond= ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC;
    List<Item> *arg_list=  ((Item_cond*) cond)->argument_list();
    List_iterator<Item> li(*arg_list);
    uint count= 0;         // to count items not containing NO_EXTRACTION_FL
    uint count_full= 0;    // to count items with FULL_EXTRACTION_FL
    Item *item;
    while ((item=li++))
    {
      check_cond_extraction_for_grouping_fields(item, derived);
      if (item->get_extraction_flag() !=  NO_EXTRACTION_FL)
      {
        count++;
        if (item->get_extraction_flag() == FULL_EXTRACTION_FL)
          count_full++;
      }
      else if (!and_cond)
        break;
    }
    if ((and_cond && count == 0) || item)
      cond->set_extraction_flag(NO_EXTRACTION_FL);
    if (count_full == arg_list->elements)
      cond->set_extraction_flag(FULL_EXTRACTION_FL);
    if (cond->get_extraction_flag() != 0)
    {
      li.rewind();
      while ((item=li++))
        item->clear_extraction_flag();
    }
  }
  else
  {
    int fl= cond->excl_dep_on_grouping_fields(this) ?
      FULL_EXTRACTION_FL : NO_EXTRACTION_FL;
    cond->set_extraction_flag(fl);
  }
}


bool LEX::can_not_use_merged()
{
  switch (sql_command)
  {
  case SQLCOM_CREATE_VIEW:
  case SQLCOM_SHOW_CREATE:
  /*
    SQLCOM_SHOW_FIELDS is necessary to make
    information schema tables working correctly with views.
    see get_schema_tables_result function
  */
  case SQLCOM_SHOW_FIELDS:
    return TRUE;
  default:
    return FALSE;
  }
}


void st_select_lex::collect_grouping_fields(THD *thd)
{
  grouping_tmp_fields.empty();
  List_iterator<Item> li(join->fields_list);
  Item *item= li++;
  for (uint i= 0; i < master_unit()->derived->table->s->fields; i++, (item=li++))
  {
    for (ORDER *ord= join->group_list; ord; ord= ord->next)
    {
      if ((*ord->item)->eq((Item*)item, 0))
      {
	Grouping_tmp_field *grouping_tmp_field=
	  new Grouping_tmp_field(master_unit()->derived->table->field[i], item);
	grouping_tmp_fields.push_back(grouping_tmp_field);
      }
    }
  }
}


bool st_select_lex::add_ftfunc_to_list(THD *thd, Item_func_match *func)
{
  return !func || ftfunc_list->push_back(func, thd->mem_root); // end of memory?
}