/*
   Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */


/* A lexical scanner for optimizer hints pseudo-commentary syntax */

#include "sql_lex_hints.h"
#include "lex_hash.h"
#include "parse_tree_helpers.h"
#include "sql_class.h"

/*
  Generated sources:
*/
#include "sql_yacc.h"
#include "lex_token.h"


/**
  Consrtuctor.

  @param thd_arg          The thread handler.
  @param lineno_arg       The starting line number of a hint string in a query.
  @param buf              The rest of a query buffer with hints at the start.
  @param len              The length of the buf.
  @param digest_state_arg The digest buffer to output scanned token data.
*/
Hint_scanner::Hint_scanner(THD *thd_arg,
                           size_t lineno_arg,
                           const char *buf,
                           size_t len,
                           sql_digest_state *digest_state_arg)
  : thd(thd_arg),
    cs(thd->charset()),
    is_ansi_quotes(thd->variables.sql_mode & MODE_ANSI_QUOTES),
    lineno(lineno_arg),
    char_classes(cs->state_maps->hint_map),
    input_buf(buf),
    input_buf_end(input_buf + len),
    ptr(input_buf + 3), // skip "/*+"
    prev_token(0),
    digest_state(digest_state_arg),
    raw_yytext(ptr),
    yytext(ptr),
    yyleng(0),
    has_hints(false)
{}


void HINT_PARSER_error(THD *thd, Hint_scanner *scanner, PT_hint_list **,
                       const char *msg)
{
  if (strcmp(msg, "syntax error") == 0)
    msg= ER_THD(thd, ER_WARN_OPTIMIZER_HINT_SYNTAX_ERROR);
  scanner->syntax_warning(msg);
}


/**
  @brief Push a warning message into MySQL error stack with line
  and position information.

  This function provides semantic action implementers with a way
  to push the famous "You have a syntax error near..." error
  message into the error stack, which is normally produced only if
  a parse error is discovered internally by the Bison generated
  parser.
*/

void Hint_scanner::syntax_warning(const char *msg) const
{
  /* Push an error into the error stack */
  ErrConvString err(raw_yytext, input_buf_end - raw_yytext,
                    thd->variables.character_set_client);

  push_warning_printf(thd, Sql_condition::SL_WARNING,
                      ER_PARSE_ERROR,  ER_THD(thd, ER_PARSE_ERROR),
                      msg, err.ptr(), lineno);
}


/**
  Add hint tokens to main lexer's digest calculation buffer.

  @note This function adds transformed hint keyword token values with the help
        of the TOK_HINT_ADJUST() adjustment macro.
*/
void Hint_scanner::add_hint_token_digest()
{
  if (digest_state == NULL)
    return; // cant add: digest buffer is full

  if (prev_token == 0 || prev_token == HINT_ERROR)
    return; // nothing to add

  if (prev_token == HINT_CLOSE)
  {
    if (has_hints)
      add_digest(TOK_HINT_COMMENT_CLOSE);
    return;
  }
  if (!has_hints)
  { // the 1st hint in the comment
    add_digest(TOK_HINT_COMMENT_OPEN);
    has_hints= true;
  }

  switch (prev_token) {
  case HINT_ARG_NUMBER:
    add_digest(NUM);
    break;
  case HINT_ARG_IDENT:
    add_digest((peek_class() == HINT_CHR_AT) ? TOK_IDENT_AT : IDENT);
    break;
  case HINT_ARG_QB_NAME:
    add_digest('@');
    add_digest(IDENT);
    break;
  default:
    if (prev_token <= UCHAR_MAX) // Single-char token.
      add_digest(prev_token);
    else // keyword
    {
      /* Make sure this is a known hint keyword: */
      switch (prev_token) {
      case BKA_HINT:
      case BNL_HINT:
      case DUPSWEEDOUT_HINT:
      case FIRSTMATCH_HINT:
      case INTOEXISTS_HINT:
      case LOOSESCAN_HINT:
      case MATERIALIZATION_HINT:
      case MAX_EXECUTION_TIME_HINT:
      case MRR_HINT:
      case NO_BKA_HINT:
      case NO_BNL_HINT:
      case NO_ICP_HINT:
      case NO_MRR_HINT:
      case NO_RANGE_OPTIMIZATION_HINT:
      case NO_SEMIJOIN_HINT:
      case QB_NAME_HINT:
      case SEMIJOIN_HINT:
      case SUBQUERY_HINT:
        break;
      default:
        DBUG_ASSERT(false);
      }
      add_digest(TOK_HINT_ADJUST(prev_token));
    }
  }
}
