/* Copyright (c) 2013, 2023, Oracle and/or its affiliates.

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

   This program is also distributed with certain software (including
   but not limited to OpenSSL) that is licensed under separate terms,
   as designated in a particular file or component or in included license
   documentation.  The authors of MySQL hereby grant you an additional
   permission to link the program and your derivative works with the
   separately licensed software that they have included with MySQL.

   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, version 2.0, 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 */

#include "parse_tree_nodes.h"

#include "sp_instr.h"       // sp_instr_set
#include "sql_delete.h"     // Sql_cmd_delete_multi, Sql_cmd_delete
#include "sql_insert.h"     // Sql_cmd_insert...
#include "sql_show_processlist.h" // pfs_processlist_enabled

bool PT_group::contextualize(Parse_context *pc)
{
  if (super::contextualize(pc))
    return true;

  SELECT_LEX *select= pc->select;
  select->parsing_place= CTX_GROUP_BY;

  if (group_list->contextualize(pc))
    return true;
  assert(select == pc->select);

  select->group_list= group_list->value;

  // Ensure we're resetting parsing place of the right select
  assert(select->parsing_place == CTX_GROUP_BY);
  select->parsing_place= CTX_NONE;

  switch (olap) {
  case UNSPECIFIED_OLAP_TYPE:
    break;
  case CUBE_TYPE:
    if (select->linkage == GLOBAL_OPTIONS_TYPE)
    {
      my_error(ER_WRONG_USAGE, MYF(0), "WITH CUBE",
               "global union parameters");
      return true;
    }
    select->olap= CUBE_TYPE;
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "CUBE");
    return true;
  case ROLLUP_TYPE:
    if (select->linkage == GLOBAL_OPTIONS_TYPE)
    {
      my_error(ER_WRONG_USAGE, MYF(0), "WITH ROLLUP",
               "global union parameters");
      return true;
    }
    if (select->is_distinct())
    {
      // DISTINCT+ROLLUP does not work
      my_error(ER_WRONG_USAGE, MYF(0), "WITH ROLLUP", "DISTINCT");
      return true;
    }
    select->olap= ROLLUP_TYPE;
    break;
  default:
    assert(!"unexpected OLAP type!");
  }
  return false;
}


bool PT_order::contextualize(Parse_context *pc)
{
  if (super::contextualize(pc))
    return true;

  THD *thd= pc->thd;
  LEX *lex= thd->lex;
  SELECT_LEX_UNIT * const unit= pc->select->master_unit();
  const bool braces= pc->select->braces;

  if (pc->select->linkage != GLOBAL_OPTIONS_TYPE &&
      pc->select->olap != UNSPECIFIED_OLAP_TYPE &&
      (pc->select->linkage != UNION_TYPE || braces))
  {
    my_error(ER_WRONG_USAGE, MYF(0),
             "CUBE/ROLLUP", "ORDER BY");
    return true;
  }
  if (lex->sql_command != SQLCOM_ALTER_TABLE && !unit->fake_select_lex)
  {
    /*
      A query of the of the form (SELECT ...) ORDER BY order_list is
      executed in the same way as the query
      SELECT ... ORDER BY order_list
      unless the SELECT construct contains ORDER BY or LIMIT clauses.
      Otherwise we create a fake SELECT_LEX if it has not been created
      yet.
    */
    SELECT_LEX *first_sl= unit->first_select();
    if (!unit->is_union() &&
        (first_sl->order_list.elements || first_sl->select_limit))
    {
      if (unit->add_fake_select_lex(lex->thd))
        return true;
      pc->select= unit->fake_select_lex;
    }
  }

  bool context_is_pushed= false;
  if (pc->select->parsing_place == CTX_NONE)
  {
    if (unit->is_union() && !braces)
    {
      /*
        At this point we don't know yet whether this is the last
        select in union or not, but we move ORDER BY to
        fake_select_lex anyway. If there would be one more select
        in union mysql_new_select will correctly throw error.
      */
      pc->select= unit->fake_select_lex;
      lex->push_context(&pc->select->context);
      context_is_pushed= true;
    }
    /*
      To preserve correct markup for the case 
       SELECT group_concat(... ORDER BY (subquery))
      we do not change parsing_place if it's not NONE.
    */
    pc->select->parsing_place= CTX_ORDER_BY;
  }

  if (order_list->contextualize(pc))
    return true;

  if (context_is_pushed)
    lex->pop_context();

  pc->select->order_list= order_list->value;

  // Reset parsing place only for ORDER BY
  if (pc->select->parsing_place == CTX_ORDER_BY)
    pc->select->parsing_place= CTX_NONE;
  return false;
}


bool PT_table_factor_select_sym::contextualize(Parse_context *pc)
{
  if (super::contextualize(pc))
    return true;

  LEX * const lex= pc->thd->lex;
  SELECT_LEX *const outer_select= pc->select;

  if (!outer_select->embedding || outer_select->end_nested_join(pc->thd))
  {
    /* we are not in parentheses */
    error(pc, pos);
    return true;
  }
  TABLE_LIST *embedding= outer_select->embedding;
  const bool is_deeply_nested= embedding &&
                               !embedding->nested_join->join_list.elements;

  lex->derived_tables|= DERIVED_SUBQUERY;
  if (!lex->expr_allows_subselect ||
      lex->sql_command == (int)SQLCOM_PURGE)
  {
    error(pc, pos);
    return true;
  }

  outer_select->parsing_place= CTX_DERIVED;
  if (outer_select->linkage == GLOBAL_OPTIONS_TYPE)
    return true; // TODO: error(pc, pos)?

  SELECT_LEX * const child= lex->new_query(pc->select);
  if (child == NULL)
    return true;

  // Note that this current select is different from the one above
  pc->select= child;
  pc->select->linkage= DERIVED_TABLE_TYPE;
  pc->select->parsing_place= CTX_SELECT_LIST;
  outer_select->parsing_place= CTX_NONE;

  Yacc_state *yyps= &pc->thd->m_parser_state->m_yacc;

  if (select_options.query_spec_options & SELECT_HIGH_PRIORITY)
  {
    yyps->m_lock_type= TL_READ_HIGH_PRIORITY;
    yyps->m_mdl_type= MDL_SHARED_READ;
  }
  if (select_options.save_to(pc))
    return true;

  if (select_item_list->contextualize(pc))
    return true;
  assert(child == pc->select);

  // Ensure we're resetting parsing place of the right select
  assert(child->parsing_place == CTX_SELECT_LIST);
  child->parsing_place= CTX_NONE;

  if (table_expression->contextualize(pc))
    return true;

  if (is_deeply_nested)
  {
    if (child->set_braces(1))
    {
      error(pc, pos);
      return true;
    }
  }
  if (outer_select->init_nested_join(pc->thd))
    return true;

  /* incomplete derived tables return NULL, we must be
     nested in select_derived rule to be here. */
  value= NULL;

  if (opt_hint_list != NULL && opt_hint_list->contextualize(pc))
    return true;

  return false;
}


bool PT_table_factor_parenthesis::contextualize(Parse_context *pc)
{
  if (super::contextualize(pc))
    return true;

  SELECT_LEX * const outer_select= pc->select;

  if (select_derived_union->contextualize(pc))
    return true;

  /*
    Use outer_select instead of pc->select as derived table has
    altered value of pc->select.
  */
  if (!(select_derived_union->value || opt_table_alias) &&
      outer_select->embedding &&
      !outer_select->embedding->nested_join->join_list.elements)
  {
    /*
      we have a derived table (select_derived_union->value == NULL)
      but no alias,
      Since we are nested in further parentheses so we
      can pass NULL to the outer level parentheses
      Permits parsing of "((((select ...))) as xyz)"
    */
    value= 0;
  }
  else if (!select_derived_union->value)
  {
    /*
      Handle case of derived table, alias may be NULL if there
      are no outer parentheses, add_table_to_list() will throw
      error in this case
    */
    SELECT_LEX_UNIT *unit= pc->select->master_unit();
    pc->select= outer_select;
    Table_ident *ti= new Table_ident(unit);
    if (ti == NULL)
      return true;

    value= pc->select->add_table_to_list(pc->thd,
                                         ti, opt_table_alias, 0,
                                         TL_READ, MDL_SHARED_READ);
    if (value == NULL)
      return true;
    pc->select->add_joined_table(value);
    pc->thd->lex->pop_context();
  }
  else if (opt_table_alias != NULL)
  {
    /*
      Tables with or without joins within parentheses cannot
      have aliases, and we ruled out derived tables above.
    */
    error(pc, alias_pos);
    return true;
  }
  else
  {
    /* nested join: FROM (t1 JOIN t2 ...) */
    value= select_derived_union->value;
  }
  return false;
}


bool PT_internal_variable_name_2d::contextualize(Parse_context *pc)
{
  if (super::contextualize(pc))
    return true;

  THD *thd= pc->thd;
  LEX *lex= thd->lex;
  sp_head *sp= lex->sphead;

  if (check_reserved_words(&ident1))
  {
    error(pc, pos);
    return true;
  }

  if (sp && sp->m_type == SP_TYPE_TRIGGER &&
      (!my_strcasecmp(system_charset_info, ident1.str, "NEW") ||
       !my_strcasecmp(system_charset_info, ident1.str, "OLD")))
  {
    if (ident1.str[0]=='O' || ident1.str[0]=='o')
    {
      my_error(ER_TRG_CANT_CHANGE_ROW, MYF(0), "OLD", "");
      return true;
    }
    if (sp->m_trg_chistics.event == TRG_EVENT_DELETE)
    {
      my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0),
               "NEW", "on DELETE");
      return true;
    }
    if (sp->m_trg_chistics.action_time == TRG_ACTION_AFTER)
    {
      my_error(ER_TRG_CANT_CHANGE_ROW, MYF(0), "NEW", "after ");
      return true;
    }
    /* This special combination will denote field of NEW row */
    value.var= trg_new_row_fake_var;
    value.base_name= ident2;
  }
  else
  {
    sys_var *tmp=find_sys_var(thd, ident2.str, ident2.length);
    if (!tmp)
      return true;
    if (!tmp->is_struct())
      my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), ident2.str);
    value.var= tmp;
    value.base_name= ident1;
  }
  return false;
}


bool PT_option_value_no_option_type_internal::contextualize(Parse_context *pc)
{
  if (super::contextualize(pc) || name->contextualize(pc))
    return true;

  THD *thd= pc->thd;
  LEX *lex= thd->lex;
  sp_head *sp= lex->sphead;

  if (sp)
    sp->m_parser_data.push_expr_start_ptr(expr_pos.raw.start);

  if (opt_expr != NULL && opt_expr->itemize(pc, &opt_expr))
    return true;

  const char *expr_start_ptr= NULL;

  if (sp)
    expr_start_ptr= sp->m_parser_data.pop_expr_start_ptr();

  if (name->value.var == trg_new_row_fake_var)
  {
    assert(sp);
    assert(expr_start_ptr);

    /* We are parsing trigger and this is a trigger NEW-field. */

    LEX_STRING expr_query= EMPTY_STR;

    if (!opt_expr)
    {
      // This is: SET NEW.x = DEFAULT
      // DEFAULT clause is not supported in triggers.

      error(pc, expr_pos);
      return true;
    }
    else if (lex->is_metadata_used())
    {
      expr_query= make_string(thd, expr_start_ptr, expr_pos.raw.end);

      if (!expr_query.str)
        return true;
    }

    if (set_trigger_new_row(pc, name->value.base_name, opt_expr, expr_query))
      return true;
  }
  else if (name->value.var)
  {
    /* We're not parsing SP and this is a system variable. */

    if (set_system_variable(thd, &name->value, lex->option_type, opt_expr))
      return true;
  }
  else
  {
    assert(sp);
    assert(expr_start_ptr);

    /* We're parsing SP and this is an SP-variable. */

    sp_pcontext *pctx= lex->get_sp_current_parsing_ctx();
    sp_variable *spv= pctx->find_variable(name->value.base_name, false);

    LEX_STRING expr_query= EMPTY_STR;

    if (!opt_expr)
    {
      /*
        This is: SET x = DEFAULT, where x is a SP-variable.
        This is not supported.
      */

      error(pc, expr_pos);
      return true;
    }
    else if (lex->is_metadata_used())
    {
      expr_query= make_string(thd, expr_start_ptr, expr_pos.raw.end);

      if (!expr_query.str)
        return true;
    }

    /*
      NOTE: every SET-expression has its own LEX-object, even if it is
      a multiple SET-statement, like:

        SET spv1 = expr1, spv2 = expr2, ...

      Every SET-expression has its own sp_instr_set. Thus, the
      instruction owns the LEX-object, i.e. the instruction is
      responsible for destruction of the LEX-object.
    */

    sp_instr_set *i=
      new sp_instr_set(sp->instructions(), lex,
                       spv->offset, opt_expr, expr_query,
                       true); // The instruction owns its lex.

    if (!i || sp->add_instr(thd, i))
      return true;
  }
  return false;
}


bool PT_option_value_no_option_type_password::contextualize(Parse_context *pc)
{
  if (super::contextualize(pc))
    return true;

  THD *thd= pc->thd;
  LEX *lex= thd->lex;
  sp_head *sp= lex->sphead;
  sp_pcontext *pctx= lex->get_sp_current_parsing_ctx();
  LEX_STRING pw= { C_STRING_WITH_LEN("password") };

  if (pctx && pctx->find_variable(pw, false))
  {
    my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str);
    return true;
  }

  LEX_USER *user= (LEX_USER*) thd->alloc(sizeof(LEX_USER));

  if (!user)
    return true;

  LEX_CSTRING sctx_user= thd->security_context()->user();
  user->user.str= (char *) sctx_user.str;
  user->user.length= sctx_user.length;

  LEX_CSTRING sctx_priv_host= thd->security_context()->priv_host();
  assert(sctx_priv_host.str);
  user->host.str= (char *) sctx_priv_host.str;
  user->host.length= sctx_priv_host.length;

  set_var_password *var= new set_var_password(user,
                                              const_cast<char *>(password));
  if (var == NULL)
    return true;

  lex->var_list.push_back(var);
  lex->autocommit= true;
  lex->is_set_password_sql= true;

  if (sp)
    sp->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;

  if (sp_create_assignment_instr(pc->thd, expr_pos.raw.end))
    return true;

  return false;
}


/*
  Given a table in the source list, find a correspondent table in the
  table references list.

  @param src Source table to match.
  @param ref Table references list.

  @remark The source table list (tables listed before the FROM clause
  or tables listed in the FROM clause before the USING clause) may
  contain table names or aliases that must match unambiguously one,
  and only one, table in the target table list (table references list,
  after FROM/USING clause).

  @return Matching table, NULL otherwise.
*/

static TABLE_LIST *multi_delete_table_match(TABLE_LIST *tbl, TABLE_LIST *tables)
{
  TABLE_LIST *match= NULL;
  DBUG_ENTER("multi_delete_table_match");

  for (TABLE_LIST *elem= tables; elem; elem= elem->next_local)
  {
    int cmp;

    if (tbl->is_fqtn && elem->is_alias)
      continue; /* no match */
    if (tbl->is_fqtn && elem->is_fqtn)
      cmp= my_strcasecmp(table_alias_charset, tbl->table_name, elem->table_name) ||
           strcmp(tbl->db, elem->db);
    else if (elem->is_alias)
      cmp= my_strcasecmp(table_alias_charset, tbl->alias, elem->alias);
    else
      cmp= my_strcasecmp(table_alias_charset, tbl->table_name, elem->table_name) ||
           strcmp(tbl->db, elem->db);

    if (cmp)
      continue;

    if (match)
    {
      my_error(ER_NONUNIQ_TABLE, MYF(0), elem->alias);
      DBUG_RETURN(NULL);
    }

    match= elem;
  }

  if (!match)
    my_error(ER_UNKNOWN_TABLE, MYF(0), tbl->table_name, "MULTI DELETE");

  DBUG_RETURN(match);
}


/**
  Link tables in auxilary table list of multi-delete with corresponding
  elements in main table list, and set proper locks for them.

  @param pc   Parse context

  @retval
    FALSE   success
  @retval
    TRUE    error
*/

static bool multi_delete_set_locks_and_link_aux_tables(Parse_context *pc)
{
  LEX * const lex= pc->thd->lex;
  TABLE_LIST *tables= pc->select->table_list.first;
  TABLE_LIST *target_tbl;
  DBUG_ENTER("multi_delete_set_locks_and_link_aux_tables");

  for (target_tbl= lex->auxiliary_table_list.first;
       target_tbl; target_tbl= target_tbl->next_local)
  {
    /* All tables in aux_tables must be found in FROM PART */
    TABLE_LIST *walk= multi_delete_table_match(target_tbl, tables);
    if (!walk)
      DBUG_RETURN(TRUE);
    if (!walk->is_derived())
    {
      target_tbl->table_name= walk->table_name;
      target_tbl->table_name_length= walk->table_name_length;
    }
    walk->updating= target_tbl->updating;
    walk->lock_type= target_tbl->lock_type;
    /* We can assume that tables to be deleted from are locked for write. */
    assert(walk->lock_type >= TL_WRITE_ALLOW_WRITE);
    walk->mdl_request.set_type(mdl_type_for_dml(walk->lock_type));
    target_tbl->correspondent_table= walk;	// Remember corresponding table
  }
  DBUG_RETURN(FALSE);
}


bool PT_delete::add_table(Parse_context *pc, Table_ident *table)
{
  const ulong table_opts= is_multitable() ? TL_OPTION_UPDATING | TL_OPTION_ALIAS
                                          : TL_OPTION_UPDATING;
  const thr_lock_type lock_type=
    (opt_delete_options & DELETE_LOW_PRIORITY) ? TL_WRITE_LOW_PRIORITY
                                               : TL_WRITE_DEFAULT;
  const enum_mdl_type mdl_type=
    (opt_delete_options & DELETE_LOW_PRIORITY) ? MDL_SHARED_WRITE_LOW_PRIO
                                               : MDL_SHARED_WRITE;
   return !pc->select->add_table_to_list(pc->thd, table, NULL, table_opts,
                                         lock_type, mdl_type, NULL,
                                         opt_use_partition);
}

bool PT_delete::contextualize(Parse_context *pc)
{
  if (super::contextualize(pc))
    return true;

  LEX * const lex= pc->thd->lex;

  lex->sql_command= is_multitable() ? SQLCOM_DELETE_MULTI : SQLCOM_DELETE;
  lex->set_ignore(MY_TEST(opt_delete_options & DELETE_IGNORE));
  lex->select_lex->init_order();
  if (opt_delete_options & DELETE_QUICK)
    pc->select->add_base_options(OPTION_QUICK);

  if (is_multitable())
  {
    for (Table_ident **i= table_list.begin(); i != table_list.end(); ++i)
    {
      if (add_table(pc, *i))
        return true;
    }
  }
  else if (add_table(pc, table_ident))
    return true;

  if (is_multitable())
    mysql_init_multi_delete(lex);
  else
    pc->select->top_join_list.push_back(pc->select->get_table_list());

  Yacc_state * const yyps= &pc->thd->m_parser_state->m_yacc;
  yyps->m_lock_type= TL_READ_DEFAULT;
  yyps->m_mdl_type= MDL_SHARED_READ;

  if (is_multitable() && join_table_list->contextualize(pc))
    return true;

  if (opt_where_clause != NULL &&
      opt_where_clause->itemize(pc, &opt_where_clause))
    return true;
  pc->select->set_where_cond(opt_where_clause);

  if (opt_order_clause != NULL && opt_order_clause->contextualize(pc))
    return true;

  assert(pc->select->select_limit == NULL);
  if (opt_delete_limit_clause != NULL)
  {
    if (opt_delete_limit_clause->itemize(pc, &opt_delete_limit_clause))
      return true;
    pc->select->select_limit= opt_delete_limit_clause;
    lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
    pc->select->explicit_limit= 1;
  }

  if (is_multitable() && multi_delete_set_locks_and_link_aux_tables(pc))
    return true;

  if (opt_hints != NULL && opt_hints->contextualize(pc))
    return true;

  return false;
}


Sql_cmd *PT_delete::make_cmd(THD *thd)
{
  Parse_context pc(thd, thd->lex->current_select());
  if (contextualize(&pc))
    return NULL;
  if (is_multitable())
    return new (thd->mem_root) Sql_cmd_delete_multi;
  else
    return new (thd->mem_root) Sql_cmd_delete;
}


bool PT_update::contextualize(Parse_context *pc)
{
  if (super::contextualize(pc))
    return true;

  LEX *lex= pc->thd->lex;
  lex->sql_command= SQLCOM_UPDATE;
  lex->duplicates= DUP_ERROR;

  lex->set_ignore(opt_ignore);
  if (join_table_list->contextualize(pc))
    return true;
  pc->select->parsing_place= CTX_UPDATE_VALUE_LIST;

  if (column_list->contextualize(pc) ||
      value_list->contextualize(pc))
  {
    return true;
  }
  pc->select->item_list= column_list->value;

  // Ensure we're resetting parsing context of the right select
  assert(pc->select->parsing_place == CTX_UPDATE_VALUE_LIST);
  pc->select->parsing_place= CTX_NONE;
  if (lex->select_lex->table_list.elements > 1)
    lex->sql_command= SQLCOM_UPDATE_MULTI;
  else if (lex->select_lex->get_table_list()->is_derived())
  {
    /* it is single table update and it is update of derived table */
    my_error(ER_NON_UPDATABLE_TABLE, MYF(0),
             lex->select_lex->get_table_list()->alias, "UPDATE");
    return true;
  }

  /*
    In case of multi-update setting write lock for all tables may
    be too pessimistic. We will decrease lock level if possible in
    mysql_multi_update().
  */
  pc->select->set_lock_for_tables(opt_low_priority);

  if (opt_where_clause != NULL &&
      opt_where_clause->itemize(pc, &opt_where_clause))
  {
    return true;
  }
  pc->select->set_where_cond(opt_where_clause);

  if (opt_order_clause != NULL && opt_order_clause->contextualize(pc))
    return true;

  assert(pc->select->select_limit == NULL);
  if (opt_limit_clause != NULL)
  {
    if (opt_limit_clause->itemize(pc, &opt_limit_clause))
      return true;
    pc->select->select_limit= opt_limit_clause;
    lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
    pc->select->explicit_limit= 1;
  }

  if (opt_hints != NULL && opt_hints->contextualize(pc))
    return true;

  return false;
}


Sql_cmd *PT_update::make_cmd(THD *thd)
{
  Parse_context pc(thd, thd->lex->current_select());
  if (contextualize(&pc))
    return NULL;
  sql_cmd.update_value_list= value_list->value;
  sql_cmd.sql_command= thd->lex->sql_command;

  return &sql_cmd;
}


bool PT_create_select::contextualize(Parse_context *pc)
{
  if (super::contextualize(pc))
    return true;

  LEX *lex= pc->thd->lex;
  if (lex->sql_command == SQLCOM_INSERT)
    lex->sql_command= SQLCOM_INSERT_SELECT;
  else if (lex->sql_command == SQLCOM_REPLACE)
    lex->sql_command= SQLCOM_REPLACE_SELECT;
  /*
    The following work only with the local list, the global list
    is created correctly in this case
  */
  assert(pc->select == lex->current_select());
  SQL_I_List<TABLE_LIST> save_list;
  pc->select->table_list.save_and_clear(&save_list);
  pc->select->parsing_place= CTX_SELECT_LIST;

  if (options.query_spec_options & SELECT_HIGH_PRIORITY)
  {
    Yacc_state *yyps= &pc->thd->m_parser_state->m_yacc;
    yyps->m_lock_type= TL_READ_HIGH_PRIORITY;
    yyps->m_mdl_type= MDL_SHARED_READ;
  }
  if (options.save_to(pc))
    return true;

  if (item_list->contextualize(pc))
    return true;

  // Ensure we're resetting parsing context of the right select
  assert(pc->select->parsing_place == CTX_SELECT_LIST);
  pc->select->parsing_place= CTX_NONE;

  if (table_expression->contextualize(pc))
    return true;
  /*
    The following work only with the local list, the global list
    is created correctly in this case
  */
  pc->select->table_list.push_front(&save_list);

  if (opt_hints != NULL && opt_hints->contextualize(pc))
    return true;

  return false;
}


bool PT_insert_values_list::contextualize(Parse_context *pc)
{
  if (super::contextualize(pc))
    return true;
  List_iterator<List_item> it1(many_values);
  List<Item> *item_list;
  while ((item_list= it1++))
  {
    List_iterator<Item> it2(*item_list);
    Item *item;
    while ((item= it2++))
    {
      if (item->itemize(pc, &item))
        return true;
      it2.replace(item);
    }
  }

  return false;
}


bool PT_insert_query_expression::contextualize(Parse_context *pc)
{
  if (super::contextualize(pc) || create_select->contextualize(pc))
    return true;

  pc->select->set_braces(braces);

  if (opt_union != NULL && opt_union->contextualize(pc))
    return true;

  return false;
}


bool PT_insert::contextualize(Parse_context *pc)
{
  if (super::contextualize(pc))
    return true;

  LEX *lex= pc->thd->lex;
  if (is_replace)
  {
    lex->sql_command = SQLCOM_REPLACE;
    lex->duplicates= DUP_REPLACE;
  }
  else
  {
    lex->sql_command= SQLCOM_INSERT;
    lex->duplicates= DUP_ERROR;
    lex->set_ignore(ignore);
  }

  Yacc_state *yyps= &pc->thd->m_parser_state->m_yacc;
  if (!pc->select->add_table_to_list(pc->thd, table_ident, NULL,
                                     TL_OPTION_UPDATING,
                                     yyps->m_lock_type,
                                     yyps->m_mdl_type,
                                     NULL,
                                     opt_use_partition))
  {
    return true;
  }
  pc->select->set_lock_for_tables(lock_option);

  assert(lex->current_select() == lex->select_lex);

  if (column_list->contextualize(pc))
    return true;

  if (has_select())
  {
    if (insert_query_expression->contextualize(pc))
      return true;
    lex->bulk_insert_row_cnt= 0;
  }
  else
  {
    if (row_value_list->contextualize(pc))
      return true;
    lex->bulk_insert_row_cnt= row_value_list->get_many_values().elements;
  }


  if (opt_on_duplicate_column_list != NULL)
  {
    assert(!is_replace);
    assert(opt_on_duplicate_value_list != NULL &&
           opt_on_duplicate_value_list->elements() ==
           opt_on_duplicate_column_list->elements());

    lex->duplicates= DUP_UPDATE;
    TABLE_LIST *first_table= lex->select_lex->table_list.first;
    /* Fix lock for ON DUPLICATE KEY UPDATE */
    if (first_table->lock_type == TL_WRITE_CONCURRENT_DEFAULT)
      first_table->lock_type= TL_WRITE_DEFAULT;

    pc->select->parsing_place= CTX_UPDATE_VALUE_LIST;

    if (opt_on_duplicate_column_list->contextualize(pc) ||
        opt_on_duplicate_value_list->contextualize(pc))
      return true;

    // Ensure we're resetting parsing context of the right select
    assert(pc->select->parsing_place == CTX_UPDATE_VALUE_LIST);
    pc->select->parsing_place= CTX_NONE;
  }

  if (opt_hints != NULL && opt_hints->contextualize(pc))
    return true;

  return false;
}


Sql_cmd *PT_insert::make_cmd(THD *thd)
{
  Parse_context pc(thd, thd->lex->current_select());
  if (contextualize(&pc))
    return NULL;

  Sql_cmd_insert_base *sql_cmd;
  if (has_select())
    sql_cmd= new (thd->mem_root) Sql_cmd_insert_select(is_replace,
                                                       thd->lex->duplicates);
  else
    sql_cmd= new (thd->mem_root) Sql_cmd_insert(is_replace, thd->lex->duplicates);
  if (sql_cmd == NULL)
    return NULL;

  if (!has_select())
    sql_cmd->insert_many_values= row_value_list->get_many_values();

  sql_cmd->insert_field_list= column_list->value;
  if (opt_on_duplicate_column_list != NULL)
  {
    assert(!is_replace);
    sql_cmd->insert_update_list= opt_on_duplicate_column_list->value;
    sql_cmd->insert_value_list= opt_on_duplicate_value_list->value;
  }

  return sql_cmd;
}


/**
  @brief
  make_cmd for PT_alter_instance.
  Contextualize parse tree node and return sql_cmd handle.

  @params thd [in] Thread handle

  @returns
    sql_cmd Success
    NULL    Failure
*/

Sql_cmd *PT_alter_instance::make_cmd(THD *thd)
{
  Parse_context pc(thd, thd->lex->current_select());
  if (contextualize(&pc))
    return NULL;
  return &sql_cmd;
}

/**
  @brief
  Prepare parse tree node and set required information

  @params pc [in] Parser context

  @returns
    false Success
    true Error
*/

bool PT_alter_instance::contextualize(Parse_context *pc)
{
  if (super::contextualize(pc))
    return true;

  LEX *lex= pc->thd->lex;
  lex->no_write_to_binlog= false;
  return false;
}

Sql_cmd *PT_show_processlist::make_cmd(THD *thd) {
  LEX *lex = thd->lex;
  lex->sql_command = m_sql_command;

  // Read once, to avoid race conditions.
  bool use_pfs = pfs_processlist_enabled;

  m_sql_cmd.set_use_pfs(use_pfs);
  if (use_pfs) {
    if (build_processlist_query(m_pos, thd, m_sql_cmd.verbose()))
      return NULL;
  }

  return &m_sql_cmd;
}
