bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx)
{
  TABLE *table;
  const char *key;
  uint	key_length;
  char	*alias= table_list->alias;
  uint flags= ot_ctx->get_flags();
  MDL_ticket *mdl_ticket;
  TABLE_SHARE *share;
  uint gts_flags;
  DBUG_ENTER("open_table");

  /*
    The table must not be opened already. The table can be pre-opened for
    some statements if it is a temporary table.

    open_temporary_table() must be used to open temporary tables.
  */
  DBUG_ASSERT(!table_list->table);

  /* an open table operation needs a lot of the stack space */
  if (check_stack_overrun(thd, STACK_MIN_SIZE_FOR_OPEN, (uchar *)&alias))
    DBUG_RETURN(TRUE);

  if (!(flags & MYSQL_OPEN_IGNORE_KILLED) && thd->killed)
  {
    thd->send_kill_message();
    DBUG_RETURN(TRUE);
  }

  /*
    Check if we're trying to take a write lock in a read only transaction.

    Note that we allow write locks on log tables as otherwise logging
    to general/slow log would be disabled in read only transactions.
  */
  if (table_list->mdl_request.is_write_lock_request() &&
      thd->tx_read_only &&
      !(flags & (MYSQL_LOCK_LOG_TABLE | MYSQL_OPEN_HAS_MDL_LOCK)))
  {
    my_error(ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION, MYF(0));
    DBUG_RETURN(true);
  }

  if (!table_list->db)
  {
    my_error(ER_NO_DB_ERROR, MYF(0));
    DBUG_RETURN(true);
  }

  key_length= get_table_def_key(table_list, &key);

  /*
    If we're in pre-locked or LOCK TABLES mode, let's try to find the
    requested table in the list of pre-opened and locked tables. If the
    table is not there, return an error - we can't open not pre-opened
    tables in pre-locked/LOCK TABLES mode.
    TODO: move this block into a separate function.
  */
  if (thd->locked_tables_mode &&
      ! (flags & MYSQL_OPEN_GET_NEW_TABLE))
  {						// Using table locks
    TABLE *best_table= 0;
    int best_distance= INT_MIN;
    for (table=thd->open_tables; table ; table=table->next)
    {
      if (table->s->table_cache_key.length == key_length &&
	  !memcmp(table->s->table_cache_key.str, key, key_length))
      {
        if (!my_strcasecmp(system_charset_info, table->alias.c_ptr(), alias) &&
            table->query_id != thd->query_id && /* skip tables already used */
            (thd->locked_tables_mode == LTM_LOCK_TABLES ||
             table->query_id == 0))
        {
          int distance= ((int) table->reginfo.lock_type -
                         (int) table_list->lock_type);

          /*
            Find a table that either has the exact lock type requested,
            or has the best suitable lock. In case there is no locked
            table that has an equal or higher lock than requested,
            we us the closest matching lock to be able to produce an error
            message about wrong lock mode on the table. The best_table
            is changed if bd < 0 <= d or bd < d < 0 or 0 <= d < bd.

            distance <  0 - No suitable lock found
            distance >  0 - we have lock mode higher then we require
            distance == 0 - we have lock mode exactly which we need
          */
          if ((best_distance < 0 && distance > best_distance) ||
              (distance >= 0 && distance < best_distance))
          {
            best_distance= distance;
            best_table= table;
            if (best_distance == 0)
            {
              /*
                We have found a perfect match and can finish iterating
                through open tables list. Check for table use conflict
                between calling statement and SP/trigger is done in
                lock_tables().
              */
              break;
            }
          }
        }
      }
    }
    if (best_table)
    {
      table= best_table;
      table->query_id= thd->query_id;
      table->init(thd, table_list);
      DBUG_PRINT("info",("Using locked table"));
      goto reset;
    }

    if (is_locked_view(thd, table_list))
      DBUG_RETURN(FALSE); // VIEW

    /*
      No table in the locked tables list. In case of explicit LOCK TABLES
      this can happen if a user did not include the table into the list.
      In case of pre-locked mode locked tables list is generated automatically,
      so we may only end up here if the table did not exist when
      locked tables list was created.
    */
    if (thd->locked_tables_mode == LTM_PRELOCKED)
      my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias);
    else
      my_error(ER_TABLE_NOT_LOCKED, MYF(0), alias);
    DBUG_RETURN(TRUE);
  }

  /*
    Non pre-locked/LOCK TABLES mode, and the table is not temporary.
    This is the normal use case.
  */

  if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK))
  {
    /*
      We are not under LOCK TABLES and going to acquire write-lock/
      modify the base table. We need to acquire protection against
      global read lock until end of this statement in order to have
      this statement blocked by active FLUSH TABLES WITH READ LOCK.

      We don't need to acquire this protection under LOCK TABLES as
      such protection already acquired at LOCK TABLES time and
      not released until UNLOCK TABLES.

      We don't block statements which modify only temporary tables
      as these tables are not preserved by any form of
      backup which uses FLUSH TABLES WITH READ LOCK.

      TODO: The fact that we sometimes acquire protection against
            GRL only when we encounter table to be write-locked
            slightly increases probability of deadlock.
            This problem will be solved once Alik pushes his
            temporary table refactoring patch and we can start
            pre-acquiring metadata locks at the beggining of
            open_tables() call.
    */
    if (table_list->mdl_request.is_write_lock_request() &&
        ! (flags & (MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK |
                    MYSQL_OPEN_FORCE_SHARED_MDL |
                    MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
                    MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK)) &&
        ! ot_ctx->has_protection_against_grl())
    {
      MDL_request protection_request;
      MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);

      if (thd->global_read_lock.can_acquire_protection())
        DBUG_RETURN(TRUE);

      protection_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
                              MDL_STATEMENT);

      /*
        Install error handler which if possible will convert deadlock error
        into request to back-off and restart process of opening tables.
      */
      thd->push_internal_handler(&mdl_deadlock_handler);
      bool result= thd->mdl_context.acquire_lock(&protection_request,
                                                 ot_ctx->get_timeout());
      thd->pop_internal_handler();

      if (result)
        DBUG_RETURN(TRUE);

      ot_ctx->set_has_protection_against_grl();
    }

    if (open_table_get_mdl_lock(thd, ot_ctx, &table_list->mdl_request,
                                flags, &mdl_ticket) ||
        mdl_ticket == NULL)
    {
      DEBUG_SYNC(thd, "before_open_table_wait_refresh");
      DBUG_RETURN(TRUE);
    }
    DEBUG_SYNC(thd, "after_open_table_mdl_shared");
  }
  else
  {
    /*
      Grab reference to the MDL lock ticket that was acquired
      by the caller.
    */
    mdl_ticket= table_list->mdl_request.ticket;
  }

  if (table_list->open_strategy == TABLE_LIST::OPEN_IF_EXISTS)
  {
    if (!ha_table_exists(thd, table_list->db, table_list->table_name))
      DBUG_RETURN(FALSE);
  }
  else if (table_list->open_strategy == TABLE_LIST::OPEN_STUB)
    DBUG_RETURN(FALSE);

  /* Table exists. Let us try to open it. */

  if (table_list->i_s_requested_object & OPEN_TABLE_ONLY)
    gts_flags= GTS_TABLE;
  else if (table_list->i_s_requested_object &  OPEN_VIEW_ONLY)
    gts_flags= GTS_VIEW;
  else
    gts_flags= GTS_TABLE | GTS_VIEW;

retry_share:

  share= tdc_acquire_share(thd, table_list, gts_flags, &table);

  if (!share)
  {
    /*
      Hide "Table doesn't exist" errors if the table belongs to a view.
      The check for thd->is_error() is necessary to not push an
      unwanted error in case the error was already silenced.
      @todo Rework the alternative ways to deal with ER_NO_SUCH TABLE.
    */
    if (thd->is_error())
    {
      if (table_list->parent_l)
      {
        thd->clear_error();
        my_error(ER_WRONG_MRG_TABLE, MYF(0));
      }
      else if (table_list->belong_to_view)
      {
        TABLE_LIST *view= table_list->belong_to_view;
        thd->clear_error();
        my_error(ER_VIEW_INVALID, MYF(0),
                 view->view_db.str, view->view_name.str);
      }
    }
    DBUG_RETURN(TRUE);
  }

  /*
    Check if this TABLE_SHARE-object corresponds to a view. Note, that there is
    no need to check TABLE_SHARE::tdc.flushed as we do for regular tables,
    because view shares are always up to date.
  */
  if (share->is_view)
  {
    /*
      If parent_l of the table_list is non null then a merge table
      has this view as child table, which is not supported.
    */
    if (table_list->parent_l)
    {
      my_error(ER_WRONG_MRG_TABLE, MYF(0));
      goto err_lock;
    }

    /*
      This table is a view. Validate its metadata version: in particular,
      that it was a view when the statement was prepared.
    */
    if (check_and_update_table_version(thd, table_list, share))
      goto err_lock;

    /* Open view */
    if (mysql_make_view(thd, share, table_list, false))
      goto err_lock;

    /* TODO: Don't free this */
    tdc_release_share(share);

    DBUG_ASSERT(table_list->view);

    DBUG_RETURN(FALSE);
  }

#ifdef WITH_WSREP
  if (!((flags & MYSQL_OPEN_IGNORE_FLUSH) ||
        (thd->wsrep_applier)))
#else
  if (!(flags & MYSQL_OPEN_IGNORE_FLUSH))
#endif
  {
    if (share->tdc->flushed)
    {
      DBUG_PRINT("info", ("Found old share version: %lld  current: %lld",
                          share->tdc->version, tdc_refresh_version()));
      /*
        We already have an MDL lock. But we have encountered an old
        version of table in the table definition cache which is possible
        when someone changes the table version directly in the cache
        without acquiring a metadata lock (e.g. this can happen during
        "rolling" FLUSH TABLE(S)).
        Release our reference to share, wait until old version of
        share goes away and then try to get new version of table share.
      */
      if (table)
        tc_release_table(table);
      else
        tdc_release_share(share);

      MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);
      bool wait_result;

      thd->push_internal_handler(&mdl_deadlock_handler);
      wait_result= tdc_wait_for_old_version(thd, table_list->db,
                                            table_list->table_name,
                                            ot_ctx->get_timeout(),
                                            mdl_ticket->get_deadlock_weight());
      thd->pop_internal_handler();

      if (wait_result)
        DBUG_RETURN(TRUE);

      goto retry_share;
    }

    if (thd->open_tables && thd->open_tables->s->tdc->flushed)
    {
      /*
        If the version changes while we're opening the tables,
        we have to back off, close all the tables opened-so-far,
        and try to reopen them. Note: refresh_version is currently
        changed only during FLUSH TABLES.
      */
      if (table)
        tc_release_table(table);
      else
        tdc_release_share(share);
      (void)ot_ctx->request_backoff_action(Open_table_context::OT_REOPEN_TABLES,
                                           NULL);
      DBUG_RETURN(TRUE);
    }
  }

  if (table)
  {
    DBUG_ASSERT(table->file != NULL);
    MYSQL_REBIND_TABLE(table->file);
  }
  else
  {
    enum open_frm_error error;

    /* make a new table */
    if (!(table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME))))
      goto err_lock;

    error= open_table_from_share(thd, share, alias,
                                 HA_OPEN_KEYFILE | HA_TRY_READ_ONLY,
                                 EXTRA_RECORD,
                                 thd->open_options, table, FALSE);

    if (error)
    {
      my_free(table);

      if (error == OPEN_FRM_DISCOVER)
        (void) ot_ctx->request_backoff_action(Open_table_context::OT_DISCOVER,
                                              table_list);
      else if (share->crashed)
      {
        if (!(flags & MYSQL_OPEN_IGNORE_REPAIR))
          (void) ot_ctx->request_backoff_action(Open_table_context::OT_REPAIR,
                                                table_list);
        else
          table_list->crashed= 1;  /* Mark that table was crashed */
      }
      goto err_lock;
    }
    if (open_table_entry_fini(thd, share, table))
    {
      closefrm(table);
      my_free(table);
      goto err_lock;
    }

    /* Add table to the share's used tables list. */
    tc_add_table(thd, table);
  }

  table->mdl_ticket= mdl_ticket;
  table->reginfo.lock_type=TL_READ;		/* Assume read */

  table->init(thd, table_list);

  table->next= thd->open_tables;		/* Link into simple list */
  thd->set_open_tables(table);

 reset:
  /*
    Check that there is no reference to a condition from an earlier query
    (cf. Bug#58553).
  */
  DBUG_ASSERT(table->file->pushed_cond == NULL);
  table_list->updatable= 1; // It is not derived table nor non-updatable VIEW
  table_list->table= table;

#ifdef WITH_PARTITION_STORAGE_ENGINE
  if (table->part_info)
  {
    /* Set all [named] partitions as used. */
    if (table->part_info->set_partition_bitmaps(table_list))
      DBUG_RETURN(true);
  }
  else if (table_list->partition_names)
  {
    /* Don't allow PARTITION () clause on a nonpartitioned table */
    my_error(ER_PARTITION_CLAUSE_ON_NONPARTITIONED, MYF(0));
    DBUG_RETURN(true);
  }
#endif

  DBUG_RETURN(FALSE);

err_lock:
  tdc_release_share(share);

  DBUG_PRINT("exit", ("failed"));
  DBUG_RETURN(TRUE);
}


static my_bool close_cached_connection_tables_callback(
  TDC_element *element, close_cached_connection_tables_arg *arg)
{
  TABLE_LIST *tmp;

  mysql_mutex_lock(&element->LOCK_table_share);
  /* Ignore if table is not open or does not have a connect_string */
  if (!element->share || !element->share->connect_string.length ||
      !element->ref_count)
    goto end;

  /* Compare the connection string */
  if (arg->connection &&
      (arg->connection->length > element->share->connect_string.length ||
       (arg->connection->length < element->share->connect_string.length &&
        (element->share->connect_string.str[arg->connection->length] != '/' &&
         element->share->connect_string.str[arg->connection->length] != '\\')) ||
       strncasecmp(arg->connection->str, element->share->connect_string.str,
                   arg->connection->length)))
    goto end;

  /* close_cached_tables() only uses these elements */
  if (!(tmp= (TABLE_LIST*) alloc_root(arg->thd->mem_root, sizeof(TABLE_LIST))) ||
      !(tmp->db= strdup_root(arg->thd->mem_root, element->share->db.str)) ||
      !(tmp->table_name= strdup_root(arg->thd->mem_root,
                                     element->share->table_name.str)))
  {
    mysql_mutex_unlock(&element->LOCK_table_share);
    return TRUE;
  }

  tmp->next_local= arg->tables;
  arg->tables= tmp;

end:
  mysql_mutex_unlock(&element->LOCK_table_share);
  return FALSE;
}

MDEV-25766 Unused CTE lead to a crash in find_field_in_tables/find_order_in_list

Do not assume that subquery Item always present.


static bool setup_natural_join_row_types(THD *thd,
                                         List<TABLE_LIST> *from_clause,
                                         Name_resolution_context *context)
{
  DBUG_ENTER("setup_natural_join_row_types");
  thd->where= "from clause";
  if (from_clause->elements == 0)
    DBUG_RETURN(false); /* We come here in the case of UNIONs. */

  /*
     Do not redo work if already done:
     1) for stored procedures,
     2) for multitable update after lock failure and table reopening.
  */
  if (!context->select_lex->first_natural_join_processing)
  {
    context->first_name_resolution_table= context->natural_join_first_table;
    DBUG_PRINT("info", ("using cached setup_natural_join_row_types"));
    DBUG_RETURN(false);
  }

  List_iterator_fast<TABLE_LIST> table_ref_it(*from_clause);
  TABLE_LIST *table_ref; /* Current table reference. */
  /* Table reference to the left of the current. */
  TABLE_LIST *left_neighbor;
  /* Table reference to the right of the current. */
  TABLE_LIST *right_neighbor= NULL;

  /* Note that tables in the list are in reversed order */
  for (left_neighbor= table_ref_it++; left_neighbor ; )
  {
    table_ref= left_neighbor;
    do
    {
      left_neighbor= table_ref_it++;
    }
    while (left_neighbor && left_neighbor->sj_subq_pred);

    if (store_top_level_join_columns(thd, table_ref,
                                     left_neighbor, right_neighbor))
      DBUG_RETURN(true);
    if (left_neighbor)
    {
      TABLE_LIST *first_leaf_on_the_right;
      first_leaf_on_the_right= table_ref->first_leaf_for_name_resolution();
      left_neighbor->next_name_resolution_table= first_leaf_on_the_right;
    }
    right_neighbor= table_ref;
  }

  /*
    Store the top-most, left-most NATURAL/USING join, so that we start
    the search from that one instead of context->table_list. At this point
    right_neighbor points to the left-most top-level table reference in the
    FROM clause.
  */
  DBUG_ASSERT(right_neighbor);
  context->first_name_resolution_table=
    right_neighbor->first_leaf_for_name_resolution();
  /*
    This is only to ensure that first_name_resolution_table doesn't
    change on re-execution
  */
  context->natural_join_first_table= context->first_name_resolution_table;
  context->select_lex->first_natural_join_processing= false;
  DBUG_RETURN (false);
}


find_item_in_list(Item *find, List<Item> &items, uint *counter,
                  find_item_error_report_type report_error,
                  enum_resolution_type *resolution, uint limit)
{
  List_iterator<Item> li(items);
  uint n_items= limit == 0 ? items.elements : limit;
  Item **found=0, **found_unaliased= 0, *item;
  const char *db_name=0;
  const char *field_name=0;
  const char *table_name=0;
  bool found_unaliased_non_uniq= 0;
  /*
    true if the item that we search for is a valid name reference
    (and not an item that happens to have a name).
  */
  bool is_ref_by_name= 0;
  uint unaliased_counter= 0;

  *resolution= NOT_RESOLVED;

  is_ref_by_name= (find->type() == Item::FIELD_ITEM  ||
                   find->type() == Item::REF_ITEM);
  if (is_ref_by_name)
  {
    field_name= ((Item_ident*) find)->field_name;
    table_name= ((Item_ident*) find)->table_name;
    db_name=    ((Item_ident*) find)->db_name;
  }

  for (uint i= 0; i < n_items; i++)
  {
    item= li++;
    if (field_name &&
        (item->real_item()->type() == Item::FIELD_ITEM ||
         ((item->type() == Item::REF_ITEM) &&
          (((Item_ref *)item)->ref_type() == Item_ref::VIEW_REF))))
    {
      Item_ident *item_field= (Item_ident*) item;

      /*
	In case of group_concat() with ORDER BY condition in the QUERY
	item_field can be field of temporary table without item name
	(if this field created from expression argument of group_concat()),
	=> we have to check presence of name before compare
      */
      if (!item_field->name)
        continue;

      if (table_name)
      {
        /*
          If table name is specified we should find field 'field_name' in
          table 'table_name'. According to SQL-standard we should ignore
          aliases in this case.

          Since we should NOT prefer fields from the select list over
          other fields from the tables participating in this select in
          case of ambiguity we have to do extra check outside this function.

          We use strcmp for table names and database names as these may be
          case sensitive. In cases where they are not case sensitive, they
          are always in lower case.

	  item_field->field_name and item_field->table_name can be 0x0 if
	  item is not fix_field()'ed yet.
        */
        if (item_field->field_name && item_field->table_name &&
	    !my_strcasecmp(system_charset_info, item_field->field_name,
                           field_name) &&
            !my_strcasecmp(table_alias_charset, item_field->table_name,
                           table_name) &&
            (!db_name || (item_field->db_name &&
                          !strcmp(item_field->db_name, db_name))))
        {
          if (found_unaliased)
          {
            if ((*found_unaliased)->eq(item, 0))
              continue;
            /*
              Two matching fields in select list.
              We already can bail out because we are searching through
              unaliased names only and will have duplicate error anyway.
            */
            if (report_error != IGNORE_ERRORS)
              my_error(ER_NON_UNIQ_ERROR, MYF(0),
                       find->full_name(), current_thd->where);
            return (Item**) 0;
          }
          found_unaliased= li.ref();
          unaliased_counter= i;
          *resolution= RESOLVED_IGNORING_ALIAS;
          if (db_name)
            break;                              // Perfect match
        }
      }
      else
      {
        int fname_cmp= my_strcasecmp(system_charset_info,
                                     item_field->field_name,
                                     field_name);
        if (!my_strcasecmp(system_charset_info,
                           item_field->name,field_name))
        {
          /*
            If table name was not given we should scan through aliases
            and non-aliased fields first. We are also checking unaliased
            name of the field in then next  else-if, to be able to find
            instantly field (hidden by alias) if no suitable alias or
            non-aliased field was found.
          */
          if (found)
          {
            if ((*found)->eq(item, 0))
              continue;                           // Same field twice
            if (report_error != IGNORE_ERRORS)
              my_error(ER_NON_UNIQ_ERROR, MYF(0),
                       find->full_name(), current_thd->where);
            return (Item**) 0;
          }
          found= li.ref();
          *counter= i;
          *resolution= fname_cmp ? RESOLVED_AGAINST_ALIAS:
	                           RESOLVED_WITH_NO_ALIAS;
        }
        else if (!fname_cmp)
        {
          /*
            We will use non-aliased field or react on such ambiguities only if
            we won't be able to find aliased field.
            Again if we have ambiguity with field outside of select list
            we should prefer fields from select list.
          */
          if (found_unaliased)
          {
            if ((*found_unaliased)->eq(item, 0))
              continue;                           // Same field twice
            found_unaliased_non_uniq= 1;
          }
          found_unaliased= li.ref();
          unaliased_counter= i;
        }
      }
    }
    else if (!table_name)
    {
      if (is_ref_by_name && find->name && item->name &&
	  !my_strcasecmp(system_charset_info,item->name,find->name))
      {
        found= li.ref();
        *counter= i;
        *resolution= RESOLVED_AGAINST_ALIAS;
        break;
      }
      else if (find->eq(item,0))
      {
        found= li.ref();
        *counter= i;
        *resolution= RESOLVED_IGNORING_ALIAS;
        break;
      }
    }
  }
  if (!found)
  {
    if (found_unaliased_non_uniq)
    {
      if (report_error != IGNORE_ERRORS)
        my_error(ER_NON_UNIQ_ERROR, MYF(0),
                 find->full_name(), current_thd->where);
      return (Item **) 0;
    }
    if (found_unaliased)
    {
      found= found_unaliased;
      *counter= unaliased_counter;
      *resolution= RESOLVED_BEHIND_ALIAS;
    }
  }
  if (found)
    return found;
  if (report_error != REPORT_EXCEPT_NOT_FOUND)
  {
    if (report_error == REPORT_ALL_ERRORS)
      my_error(ER_BAD_FIELD_ERROR, MYF(0),
               find->full_name(), current_thd->where);
    return (Item **) 0;
  }
  else
    return (Item **) not_found_item;
}


find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
                        const char *name, uint length,
                        const char *item_name, const char *db_name,
                        const char *table_name, Item **ref,
                        bool check_privileges, bool allow_rowid,
                        uint *cached_field_index_ptr,
                        bool register_tree_change, TABLE_LIST **actual_table)
{
  Field *fld;
  DBUG_ENTER("find_field_in_table_ref");
  DBUG_ASSERT(table_list->alias);
  DBUG_ASSERT(name);
  DBUG_ASSERT(item_name);
  DBUG_PRINT("enter",
             ("table: '%s'  field name: '%s'  item name: '%s'  ref %p",
              table_list->alias, name, item_name, ref));

  /*
    Check that the table and database that qualify the current field name
    are the same as the table reference we are going to search for the field.

    Exclude from the test below nested joins because the columns in a
    nested join generally originate from different tables. Nested joins
    also have no table name, except when a nested join is a merge view
    or an information schema table.

    We include explicitly table references with a 'field_translation' table,
    because if there are views over natural joins we don't want to search
    inside the view, but we want to search directly in the view columns
    which are represented as a 'field_translation'.

    TODO: Ensure that table_name, db_name and tables->db always points to
          something !
  */
  if (/* Exclude nested joins. */
      (!table_list->nested_join ||
       /* Include merge views and information schema tables. */
       table_list->field_translation) &&
      /*
        Test if the field qualifiers match the table reference we plan
        to search.
      */
      table_name && table_name[0] &&
      (my_strcasecmp(table_alias_charset, table_list->alias, table_name) ||
       (db_name && db_name[0] && (!table_list->db || !table_list->db[0])) ||
       (db_name && db_name[0] && table_list->db && table_list->db[0] &&
        (table_list->schema_table ?
         my_strcasecmp(system_charset_info, db_name, table_list->db) :
         strcmp(db_name, table_list->db)))))
    DBUG_RETURN(0);

  *actual_table= NULL;

  if (table_list->field_translation)
  {
    /* 'table_list' is a view or an information schema table. */
    if ((fld= find_field_in_view(thd, table_list, name, length, item_name, ref,
                                 register_tree_change)))
      *actual_table= table_list;
  }
  else if (!table_list->nested_join)
  {
    /* 'table_list' is a stored table. */
    DBUG_ASSERT(table_list->table);
    if ((fld= find_field_in_table(thd, table_list->table, name, length,
                                  allow_rowid,
                                  cached_field_index_ptr)))
      *actual_table= table_list;
  }
  else
  {
    /*
      'table_list' is a NATURAL/USING join, or an operand of such join that
      is a nested join itself.

      If the field name we search for is qualified, then search for the field
      in the table references used by NATURAL/USING the join.
    */
    if (table_name && table_name[0])
    {
      List_iterator<TABLE_LIST> it(table_list->nested_join->join_list);
      TABLE_LIST *table;
      while ((table= it++))
      {
        if ((fld= find_field_in_table_ref(thd, table, name, length, item_name,
                                          db_name, table_name, ref,
                                          check_privileges, allow_rowid,
                                          cached_field_index_ptr,
                                          register_tree_change, actual_table)))
          DBUG_RETURN(fld);
      }
      DBUG_RETURN(0);
    }
    /*
      Non-qualified field, search directly in the result columns of the
      natural join. The condition of the outer IF is true for the top-most
      natural join, thus if the field is not qualified, we will search
      directly the top-most NATURAL/USING join.
    */
    fld= find_field_in_natural_join(thd, table_list, name, length, ref,
                                    register_tree_change, actual_table);
  }

  if (fld)
  {
#ifndef NO_EMBEDDED_ACCESS_CHECKS
    /* Check if there are sufficient access rights to the found field. */
    if (check_privileges &&
        !table_list->is_derived() &&
        check_column_grant_in_table_ref(thd, *actual_table, name, length))
      fld= WRONG_GRANT;
    else
#endif
      if (thd->mark_used_columns != MARK_COLUMNS_NONE)
      {
        /*
          Get rw_set correct for this field so that the handler
          knows that this field is involved in the query and gets
          retrieved/updated
         */
        Field *field_to_set= NULL;
        if (fld == view_ref_found)
        {
          if (!ref)
            DBUG_RETURN(fld);
          Item *it= (*ref)->real_item();
          if (it->type() == Item::FIELD_ITEM)
            field_to_set= ((Item_field*)it)->field;
          else
          {
            if (thd->mark_used_columns == MARK_COLUMNS_READ)
              it->walk(&Item::register_field_in_read_map, 0, 0);
            else
              it->walk(&Item::register_field_in_write_map, 0, 0);
          }
        }
        else
          field_to_set= fld;
        if (field_to_set)
        {
          TABLE *table= field_to_set->table;
          if (thd->mark_used_columns == MARK_COLUMNS_READ)
            field_to_set->register_field_in_read_map();
          else
            bitmap_set_bit(table->write_set, field_to_set->field_index);
        }
      }
  }
  DBUG_RETURN(fld);
}


Open_table_context::recover_from_failed_open()
{
  bool result= FALSE;
  MDL_deadlock_discovery_repair_handler handler;
  /*
    Install error handler to mark transaction to rollback on DEADLOCK error.
  */
  m_thd->push_internal_handler(&handler);

  /* Execute the action. */
  switch (m_action)
  {
    case OT_BACKOFF_AND_RETRY:
      break;
    case OT_REOPEN_TABLES:
      break;
    case OT_DISCOVER:
      {
        if ((result= lock_table_names(m_thd, m_thd->lex->create_info,
                                      m_failed_table, NULL,
                                      get_timeout(), 0)))
          break;

        tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
                         m_failed_table->table_name, FALSE);

        m_thd->get_stmt_da()->clear_warning_info(m_thd->query_id);
        m_thd->clear_error();                 // Clear error message

        No_such_table_error_handler no_such_table_handler;
        bool open_if_exists= m_failed_table->open_strategy == TABLE_LIST::OPEN_IF_EXISTS;

        if (open_if_exists)
          m_thd->push_internal_handler(&no_such_table_handler);

        result= !tdc_acquire_share(m_thd, m_failed_table,
                                   GTS_TABLE | GTS_FORCE_DISCOVERY | GTS_NOLOCK);
        if (open_if_exists)
        {
          m_thd->pop_internal_handler();
          if (result && no_such_table_handler.safely_trapped_errors())
            result= FALSE;
        }

        /*
          Rollback to start of the current statement to release exclusive lock
          on table which was discovered but preserve locks from previous statements
          in current transaction.
        */
        m_thd->mdl_context.rollback_to_savepoint(start_of_statement_svp());
        break;
      }
    case OT_REPAIR:
      {
        if ((result= lock_table_names(m_thd, m_thd->lex->create_info,
                                      m_failed_table, NULL,
                                      get_timeout(), 0)))
          break;

        tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
                         m_failed_table->table_name, FALSE);

        result= auto_repair_table(m_thd, m_failed_table);
        /*
          Rollback to start of the current statement to release exclusive lock
          on table which was discovered but preserve locks from previous statements
          in current transaction.
        */
        m_thd->mdl_context.rollback_to_savepoint(start_of_statement_svp());
        break;
      }
    default:
      DBUG_ASSERT(0);
  }
  m_thd->pop_internal_handler();
  /*
    Reset the pointers to conflicting MDL request and the
    TABLE_LIST element, set when we need auto-discovery or repair,
    for safety.
  */
  m_failed_table= NULL;
  /*
    Reset flag indicating that we have already acquired protection
    against GRL. It is no longer valid as the corresponding lock was
    released by close_tables_for_reopen().
  */
  m_has_protection_against_grl= FALSE;
  /* Prepare for possible another back-off. */
  m_action= OT_NO_ACTION;
  return result;
}


OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
{
  list_open_tables_arg argument;
  DBUG_ENTER("list_open_tables");

  argument.thd= thd;
  argument.db= db;
  argument.wild= wild;
  bzero((char*) &argument.table_list, sizeof(argument.table_list));
  argument.start_list= &argument.open_list;
  argument.open_list= 0;

  if (tdc_iterate(thd, (my_hash_walk_action) list_open_tables_callback,
                  &argument, true))
    DBUG_RETURN(0);

  DBUG_RETURN(argument.open_list);
}


static bool inject_reprepare(THD *thd)
{
  if (thd->m_reprepare_observer && thd->stmt_arena->is_reprepared == FALSE)
  {
    thd->m_reprepare_observer->report_error(thd);
    return TRUE;
  }

  return FALSE;
}


open_and_process_table(THD *thd, TABLE_LIST *tables, uint *counter, uint flags,
                       Prelocking_strategy *prelocking_strategy,
                       bool has_prelocking_list, Open_table_context *ot_ctx)
{
  bool error= FALSE;
  bool safe_to_ignore_table= FALSE;
  LEX *lex= thd->lex;
  DBUG_ENTER("open_and_process_table");
  DEBUG_SYNC(thd, "open_and_process_table");

  /*
    Ignore placeholders for derived tables. After derived tables
    processing, link to created temporary table will be put here.
    If this is derived table for view then we still want to process
    routines used by this view.
  */
  if (tables->derived)
  {
    if (!tables->view)
    {
      if (!tables->is_derived())
        tables->set_derived();
      goto end;
    }
    /*
      We restore view's name and database wiped out by derived tables
      processing and fall back to standard open process in order to
      obtain proper metadata locks and do other necessary steps like
      stored routine processing.
    */
    tables->db= tables->view_db.str;
    tables->db_length= tables->view_db.length;
    tables->table_name= tables->view_name.str;
    tables->table_name_length= tables->view_name.length;
  }

  if (!tables->derived &&
      is_infoschema_db(tables->db, tables->db_length))
  {
    /*
      Check whether the information schema contains a table
      whose name is tables->schema_table_name
    */
    ST_SCHEMA_TABLE *schema_table= tables->schema_table;
    if (!schema_table ||
        (schema_table->hidden &&
         ((sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) == 0 ||
          /*
            this check is used for show columns|keys from I_S hidden table
          */
          lex->sql_command == SQLCOM_SHOW_FIELDS ||
          lex->sql_command == SQLCOM_SHOW_KEYS)))
    {
      my_error(ER_UNKNOWN_TABLE, MYF(0),
               tables->table_name, INFORMATION_SCHEMA_NAME.str);
      DBUG_RETURN(1);
    }
  }
  /*
    If this TABLE_LIST object is a placeholder for an information_schema
    table, create a temporary table to represent the information_schema
    table in the query. Do not fill it yet - will be filled during
    execution.
  */
  if (tables->schema_table)
  {
    /*
      If this information_schema table is merged into a mergeable
      view, ignore it for now -- it will be filled when its respective
      TABLE_LIST is processed. This code works only during re-execution.
    */
    if (tables->view)
    {
      MDL_ticket *mdl_ticket;
      /*
        We still need to take a MDL lock on the merged view to protect
        it from concurrent changes.
      */
      if (!open_table_get_mdl_lock(thd, ot_ctx, &tables->mdl_request,
                                   flags, &mdl_ticket) &&
          mdl_ticket != NULL)
        goto process_view_routines;
      /* Fall-through to return error. */
    }
    else if (!mysql_schema_table(thd, lex, tables) &&
             !check_and_update_table_version(thd, tables, tables->table->s))
    {
      goto end;
    }
    error= TRUE;
    goto end;
  }
  DBUG_PRINT("tcache", ("opening table: '%s'.'%s'  item: %p",
                        tables->db, tables->table_name, tables)); //psergey: invalid read of size 1 here
  (*counter)++;

  /*
    Not a placeholder: must be a base/temporary table or a view. Let us open it.
  */
  if (tables->table)
  {
    /*
      If this TABLE_LIST object has an associated open TABLE object
      (TABLE_LIST::table is not NULL), that TABLE object must be a pre-opened
      temporary table.
    */
    DBUG_ASSERT(is_temporary_table(tables));
  }
  else if (tables->open_type == OT_TEMPORARY_ONLY)
  {
    /*
      OT_TEMPORARY_ONLY means that we are in CREATE TEMPORARY TABLE statement.
      Also such table list element can't correspond to prelocking placeholder
      or to underlying table of merge table.
      So existing temporary table should have been preopened by this moment
      and we can simply continue without trying to open temporary or base
      table.
    */
    DBUG_ASSERT(tables->open_strategy);
    DBUG_ASSERT(!tables->prelocking_placeholder);
    DBUG_ASSERT(!tables->parent_l);
    DBUG_RETURN(0);
  }

  /* Not a placeholder: must be a base table or a view. Let us open it. */
  if (tables->prelocking_placeholder)
  {
    /*
      For the tables added by the pre-locking code, attempt to open
      the table but fail silently if the table does not exist.
      The real failure will occur when/if a statement attempts to use
      that table.
    */
    No_such_table_error_handler no_such_table_handler;
    thd->push_internal_handler(&no_such_table_handler);

    /*
      We're opening a table from the prelocking list.

      Since this table list element might have been added after pre-opening
      of temporary tables we have to try to open temporary table for it.

      We can't simply skip this table list element and postpone opening of
      temporary table till the execution of substatement for several reasons:
      - Temporary table can be a MERGE table with base underlying tables,
        so its underlying tables has to be properly open and locked at
        prelocking stage.
      - Temporary table can be a MERGE table and we might be in PREPARE
        phase for a prepared statement. In this case it is important to call
        HA_ATTACH_CHILDREN for all merge children.
        This is necessary because merge children remember "TABLE_SHARE ref type"
        and "TABLE_SHARE def version" in the HA_ATTACH_CHILDREN operation.
        If HA_ATTACH_CHILDREN is not called, these attributes are not set.
        Then, during the first EXECUTE, those attributes need to be updated.
        That would cause statement re-preparing (because changing those
        attributes during EXECUTE is caught by THD::m_reprepare_observers).
        The problem is that since those attributes are not set in merge
        children, another round of PREPARE will not help.
    */
    error= thd->open_temporary_table(tables);

    if (!error && !tables->table)
      error= open_table(thd, tables, ot_ctx);

    thd->pop_internal_handler();
    safe_to_ignore_table= no_such_table_handler.safely_trapped_errors();
  }
  else if (tables->parent_l && (thd->open_options & HA_OPEN_FOR_REPAIR))
  {
    /*
      Also fail silently for underlying tables of a MERGE table if this
      table is opened for CHECK/REPAIR TABLE statement. This is needed
      to provide complete list of problematic underlying tables in
      CHECK/REPAIR TABLE output.
    */
    Repair_mrg_table_error_handler repair_mrg_table_handler;
    thd->push_internal_handler(&repair_mrg_table_handler);

    error= thd->open_temporary_table(tables);

    if (!error && !tables->table)
      error= open_table(thd, tables, ot_ctx);

    thd->pop_internal_handler();
    safe_to_ignore_table= repair_mrg_table_handler.safely_trapped_errors();
  }
  else
  {
    if (tables->parent_l)
    {
      /*
        Even if we are opening table not from the prelocking list we
        still might need to look for a temporary table if this table
        list element corresponds to underlying table of a merge table.
      */
      error= thd->open_temporary_table(tables);
    }

    if (!error && !tables->table)
      error= open_table(thd, tables, ot_ctx);
  }

  if (error)
  {
    if (! ot_ctx->can_recover_from_failed_open() && safe_to_ignore_table)
    {
      DBUG_PRINT("info", ("open_table: ignoring table '%s'.'%s'",
                          tables->db, tables->alias));
      error= FALSE;
    }
    goto end;
  }

  /*
    We can't rely on simple check for TABLE_LIST::view to determine
    that this is a view since during re-execution we might reopen
    ordinary table in place of view and thus have TABLE_LIST::view
    set from repvious execution and TABLE_LIST::table set from
    current.
  */
  if (!tables->table && tables->view)
  {
    /* VIEW placeholder */
    (*counter)--;

    /*
      tables->next_global list consists of two parts:
      1) Query tables and underlying tables of views.
      2) Tables used by all stored routines that this statement invokes on
         execution.
      We need to know where the bound between these two parts is. If we've
      just opened a view, which was the last table in part #1, and it
      has added its base tables after itself, adjust the boundary pointer
      accordingly.
    */
    if (lex->query_tables_own_last == &(tables->next_global) &&
        tables->view->query_tables)
      lex->query_tables_own_last= tables->view->query_tables_last;
    /*
      Let us free memory used by 'sroutines' hash here since we never
      call destructor for this LEX.
    */
    my_hash_free(&tables->view->sroutines);
    goto process_view_routines;
  }

  /*
    Special types of open can succeed but still don't set
    TABLE_LIST::table to anything.
  */
  if (tables->open_strategy && !tables->table)
    goto end;

  error= extend_table_list(thd, tables, prelocking_strategy, has_prelocking_list);
  if (error)
    goto end;

  /* Copy grant information from TABLE_LIST instance to TABLE one. */
  tables->table->grant= tables->grant;

  /* Check and update metadata version of a base table. */
  error= check_and_update_table_version(thd, tables, tables->table->s);

  if (error)
    goto end;
  /*
    After opening a MERGE table add the children to the query list of
    tables, so that they are opened too.
    Note that placeholders don't have the handler open.
  */
  /* MERGE tables need to access parent and child TABLE_LISTs. */
  DBUG_ASSERT(tables->table->pos_in_table_list == tables);
  /* Non-MERGE tables ignore this call. */
  if (tables->table->file->extra(HA_EXTRA_ADD_CHILDREN_LIST))
  {
    error= TRUE;
    goto end;
  }

process_view_routines:
  /*
    Again we may need cache all routines used by this view and add
    tables used by them to table list.
  */
  if (tables->view &&
      thd->locked_tables_mode <= LTM_LOCK_TABLES &&
      ! has_prelocking_list)
  {
    bool need_prelocking= FALSE;
    TABLE_LIST **save_query_tables_last= lex->query_tables_last;

    error= prelocking_strategy->handle_view(thd, lex, tables,
                                            &need_prelocking);

    if (need_prelocking && ! lex->requires_prelocking())
      lex->mark_as_requiring_prelocking(save_query_tables_last);

    if (error)
      goto end;
  }

end:
  DBUG_RETURN(error);
}


set_new_item_local_context(THD *thd, Item_ident *item, TABLE_LIST *table_ref)
{
  Name_resolution_context *context;
  if (!(context= new (thd->mem_root) Name_resolution_context))
    return TRUE;
  context->init();
  context->first_name_resolution_table=
    context->last_name_resolution_table= table_ref;
  item->context= context;
  return FALSE;
}


int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
	       List<Item> *sum_func_list,
	       uint wild_num, uint *hidden_bit_fields)
{
  if (!wild_num)
    return(0);

  Item *item;
  List_iterator<Item> it(fields);
  Query_arena *arena, backup;
  DBUG_ENTER("setup_wild");

  /*
    Don't use arena if we are not in prepared statements or stored procedures
    For PS/SP we have to use arena to remember the changes
  */
  arena= thd->activate_stmt_arena_if_needed(&backup);

  thd->lex->current_select->cur_pos_in_select_list= 0;
  while (wild_num && (item= it++))
  {
    if (item->type() == Item::FIELD_ITEM &&
        ((Item_field*) item)->field_name &&
	((Item_field*) item)->field_name[0] == '*' &&
	!((Item_field*) item)->field)
    {
      uint elem= fields.elements;
      bool any_privileges= ((Item_field *) item)->any_privileges;
      Item_subselect *subsel= thd->lex->current_select->master_unit()->item;
      if (subsel &&
          subsel->substype() == Item_subselect::EXISTS_SUBS)
      {
        /*
          It is EXISTS(SELECT * ...) and we can replace * by any constant.

          Item_int do not need fix_fields() because it is basic constant.
        */
        it.replace(new (thd->mem_root) Item_int(thd, "Not_used", (longlong) 1,
                                MY_INT64_NUM_DECIMAL_DIGITS));
      }
      else if (insert_fields(thd, ((Item_field*) item)->context,
                             ((Item_field*) item)->db_name,
                             ((Item_field*) item)->table_name, &it,
                             any_privileges, hidden_bit_fields))
      {
	if (arena)
	  thd->restore_active_arena(arena, &backup);
	DBUG_RETURN(-1);
      }
      if (sum_func_list)
      {
	/*
	  sum_func_list is a list that has the fields list as a tail.
	  Because of this we have to update the element count also for this
	  list after expanding the '*' entry.
	*/
	sum_func_list->elements+= fields.elements - elem;
      }
      wild_num--;
    }
    else
      thd->lex->current_select->cur_pos_in_select_list++;
  }
  thd->lex->current_select->cur_pos_in_select_list= UNDEF_POS;
  if (arena)
  {
    /* make * substituting permanent */
    SELECT_LEX *select_lex= thd->lex->current_select;
    select_lex->with_wild= 0;
#ifdef HAVE_valgrind
    if (&select_lex->item_list != &fields)      // Avoid warning
#endif
    /*
      The assignment below is translated to memcpy() call (at least on some
      platforms). memcpy() expects that source and destination areas do not
      overlap. That problem was detected by valgrind.
    */
    if (&select_lex->item_list != &fields)
      select_lex->item_list= fields;

    thd->restore_active_arena(arena, &backup);
  }
  DBUG_RETURN(0);
}


bool Locked_tables_list::restore_lock(THD *thd, TABLE_LIST *dst_table_list,
                                      TABLE *table, MYSQL_LOCK *lock)
{
  MYSQL_LOCK *merged_lock;
  DBUG_ENTER("restore_lock");
  DBUG_ASSERT(!strcmp(dst_table_list->table_name, table->s->table_name.str));

  /* Ensure we have the memory to add the table back */
  if (!(merged_lock= mysql_lock_merge(thd->lock, lock)))
    DBUG_RETURN(1);
  thd->lock= merged_lock;

  /* Link to the new table */
  dst_table_list->table= table;
  /*
    The lock type may have changed (normally it should not as create
    table will lock the table in write mode
  */
  dst_table_list->lock_type= table->reginfo.lock_type;
  table->pos_in_locked_tables= dst_table_list;

  add_back_last_deleted_lock(dst_table_list);

  table->mdl_ticket->downgrade_lock(table->reginfo.lock_type >=
                                    TL_WRITE_ALLOW_WRITE ?
                                    MDL_SHARED_NO_READ_WRITE :
                                    MDL_SHARED_READ);

  DBUG_RETURN(0);
}


void switch_to_nullable_trigger_fields(List<Item> &items, TABLE *table)
{
  Field** field= table->field_to_fill();

 /* True if we have NOT NULL fields and BEFORE triggers */
  if (field != table->field)
  {
    List_iterator_fast<Item> it(items);
    Item *item;

    while ((item= it++))
      item->walk(&Item::switch_to_nullable_fields_processor, 1, field);
    table->triggers->reset_extra_null_bitmap();
  }
}


bool MDL_deadlock_handler::handle_condition(THD *,
                                            uint sql_errno,
                                            const char*,
                                            Sql_condition::enum_warning_level*,
                                            const char*,
                                            Sql_condition ** cond_hdl)
{
  *cond_hdl= NULL;
  if (! m_is_active && sql_errno == ER_LOCK_DEADLOCK)
  {
    /* Disable the handler to avoid infinite recursion. */
    m_is_active= TRUE;
    (void) m_ot_ctx->request_backoff_action(
             Open_table_context::OT_BACKOFF_AND_RETRY,
             NULL);
    m_is_active= FALSE;
    /*
      If the above back-off request failed, a new instance of
      ER_LOCK_DEADLOCK error was emitted. Thus the current
      instance of error condition can be treated as handled.
    */
    return TRUE;
  }
  return FALSE;
}


TABLE_LIST *find_table_in_list(TABLE_LIST *table,
                               TABLE_LIST *TABLE_LIST::*link,
                               const char *db_name,
                               const char *table_name)
{
  for (; table; table= table->*link )
  {
    if (strcmp(table->db, db_name) == 0 &&
        strcmp(table->table_name, table_name) == 0)
      break;
  }
  return table;
}


bool DML_prelocking_strategy::
handle_routine(THD *thd, Query_tables_list *prelocking_ctx,
               Sroutine_hash_entry *rt, sp_head *sp, bool *need_prelocking)
{
  /*
    We assume that for any "CALL proc(...)" statement sroutines_list will
    have 'proc' as first element (it may have several, consider e.g.
    "proc(sp_func(...)))". This property is currently guaranted by the
    parser.
  */

  if (rt != (Sroutine_hash_entry*)prelocking_ctx->sroutines_list.first ||
      rt->mdl_request.key.mdl_namespace() != MDL_key::PROCEDURE)
  {
    *need_prelocking= TRUE;
    sp_update_stmt_used_routines(thd, prelocking_ctx, &sp->m_sroutines,
                                 rt->belong_to_view);
    (void)sp->add_used_tables_to_table_list(thd,
                                            &prelocking_ctx->query_tables_last,
                                            rt->belong_to_view);
  }
  sp->propagate_attributes(prelocking_ctx);
  return FALSE;
}


bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array,
                  List<Item> &fields, enum_mark_columns mark_used_columns,
                  List<Item> *sum_func_list, List<Item> *pre_fix,
                  bool allow_sum_func)
{
  Item *item;
  enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
  nesting_map save_allow_sum_func= thd->lex->allow_sum_func;
  List_iterator<Item> it(fields);
  bool save_is_item_list_lookup;
  bool make_pre_fix= (pre_fix && (pre_fix->elements == 0));
  DBUG_ENTER("setup_fields");
  DBUG_PRINT("enter", ("ref_pointer_array: %p", ref_pointer_array.array()));

  thd->mark_used_columns= mark_used_columns;
  DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
  if (allow_sum_func)
    thd->lex->allow_sum_func|=
      (nesting_map)1 << thd->lex->current_select->nest_level;
  thd->where= THD::DEFAULT_WHERE;
  save_is_item_list_lookup= thd->lex->current_select->is_item_list_lookup;
  thd->lex->current_select->is_item_list_lookup= 0;

  /*
    To prevent fail on forward lookup we fill it with zerows,
    then if we got pointer on zero after find_item_in_list we will know
    that it is forward lookup.

    There is other way to solve problem: fill array with pointers to list,
    but it will be slower.

    TODO: remove it when (if) we made one list for allfields and
    ref_pointer_array
  */
  if (!ref_pointer_array.is_null())
  {
    DBUG_ASSERT(ref_pointer_array.size() >= fields.elements);
    memset(ref_pointer_array.array(), 0, sizeof(Item *) * fields.elements);
  }

  /*
    We call set_entry() there (before fix_fields() of the whole list of field
    items) because:
    1) the list of field items has same order as in the query, and the
       Item_func_get_user_var item may go before the Item_func_set_user_var:
          SELECT @a, @a := 10 FROM t;
    2) The entry->update_query_id value controls constantness of
       Item_func_get_user_var items, so in presence of Item_func_set_user_var
       items we have to refresh their entries before fixing of
       Item_func_get_user_var items.
  */
  List_iterator<Item_func_set_user_var> li(thd->lex->set_var_list);
  Item_func_set_user_var *var;
  while ((var= li++))
    var->set_entry(thd, FALSE);

  Ref_ptr_array ref= ref_pointer_array;
  thd->lex->current_select->cur_pos_in_select_list= 0;
  while ((item= it++))
  {
    if (make_pre_fix)
      pre_fix->push_back(item, thd->stmt_arena->mem_root);

    if ((!item->fixed && item->fix_fields(thd, it.ref())) ||
	(item= *(it.ref()))->check_cols(1))
    {
      thd->lex->current_select->is_item_list_lookup= save_is_item_list_lookup;
      thd->lex->allow_sum_func= save_allow_sum_func;
      thd->mark_used_columns= save_mark_used_columns;
      DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
      DBUG_RETURN(TRUE); /* purecov: inspected */
    }
    if (!ref.is_null())
    {
      ref[0]= item;
      ref.pop_front();
    }
    /*
      split_sum_func() must be called for Window Function items, see
      Item_window_func::split_sum_func.
    */
    if (sum_func_list &&
         ((item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) ||
          item->with_window_func))
    {
      item->split_sum_func(thd, ref_pointer_array, *sum_func_list,
                           SPLIT_SUM_SELECT);
    }
    thd->lex->current_select->select_list_tables|= item->used_tables();
    thd->lex->used_tables|= item->used_tables();
    thd->lex->current_select->cur_pos_in_select_list++;
  }
  thd->lex->current_select->is_item_list_lookup= save_is_item_list_lookup;
  thd->lex->current_select->cur_pos_in_select_list= UNDEF_POS;

  thd->lex->allow_sum_func= save_allow_sum_func;
  thd->mark_used_columns= save_mark_used_columns;
  DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
  DBUG_RETURN(MY_TEST(thd->is_error()));
}


int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order)
{
  if (select_lex->ftfunc_list->elements)
  {
    List_iterator<Item_func_match> li(*(select_lex->ftfunc_list));
    Item_func_match *ifm;
    DBUG_PRINT("info",("Performing FULLTEXT search"));

    while ((ifm=li++))
      if (unlikely(!ifm->fixed))
        /*
          it mean that clause where was FT function was removed, so we have
          to remove the function from the list.
        */
        li.remove();
      else
        ifm->init_search(thd, no_order);
  }
  return 0;
}


void switch_defaults_to_nullable_trigger_fields(TABLE *table)
{
  if (!table->default_field)
    return; // no defaults

  Field **trigger_field= table->field_to_fill();

 /* True if we have NOT NULL fields and BEFORE triggers */
  if (trigger_field != table->field)
  {
    for (Field **field_ptr= table->default_field; *field_ptr ; field_ptr++)
    {
      Field *field= (*field_ptr);
      field->default_value->expr->walk(&Item::switch_to_nullable_fields_processor, 1, trigger_field);
      *field_ptr= (trigger_field[field->field_index]);
    }
  }
}


bool No_such_table_error_handler::safely_trapped_errors()
{
  /*
    If m_unhandled_errors != 0, something else, unanticipated, happened,
    so the error is not trapped but returned to the caller.
    Multiple ER_NO_SUCH_TABLE can be raised in case of views.
  */
  return ((m_handled_errors > 0) && (m_unhandled_errors == 0));
}


  bool safely_trapped_errors()
  {
    /*
      Check for m_handled_errors is here for extra safety.
      It can be useful in situation when call to open_table()
      fails because some error which was suppressed by another
      error handler (e.g. in case of MDL deadlock which we
      decided to solve by back-off and retry).
    */
    return (m_handled_errors && (! m_unhandled_errors));
  }


fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values,
            bool ignore_errors, bool use_value)
{
  List_iterator_fast<Item> v(values);
  List<TABLE> tbl_list;
  Item *value;
  Field *field;
  bool abort_on_warning_saved= thd->abort_on_warning;
  uint autoinc_index= table->next_number_field
                        ? table->next_number_field->field_index
                        : ~0U;
  DBUG_ENTER("fill_record");

  if (!*ptr)
  {
    /* No fields to update, quite strange!*/
    DBUG_RETURN(0);
  }

  /*
    On INSERT or UPDATE fields are checked to be from the same table,
    thus we safely can take table from the first field.
  */
  DBUG_ASSERT((*ptr)->table == table);

  /*
    Reset the table->auto_increment_field_not_null as it is valid for
    only one row.
  */
  table->auto_increment_field_not_null= FALSE;
  while ((field = *ptr++) && ! thd->is_error())
  {
    /* Ensure that all fields are from the same table */
    DBUG_ASSERT(field->table == table);

    value=v++;
    if (field->field_index == autoinc_index)
      table->auto_increment_field_not_null= TRUE;
    if (field->vcol_info &&
        !value->vcol_assignment_allowed_value() &&
        table->s->table_category != TABLE_CATEGORY_TEMPORARY)
    {
      push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
                          ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN,
                          ER_THD(thd, ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN),
                          field->field_name, table->s->table_name.str);
    }

    if (use_value)
      value->save_val(field);
    else
      if (value->save_in_field(field, 0) < 0)
        goto err;
    field->set_has_explicit_value();
  }
  /* Update virtual fields */
  thd->abort_on_warning= FALSE;
  if (table->vfield &&
      table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_WRITE))
    goto err;
  thd->abort_on_warning= abort_on_warning_saved;
  DBUG_RETURN(thd->is_error());

err:
  thd->abort_on_warning= abort_on_warning_saved;
  table->auto_increment_field_not_null= FALSE;
  DBUG_RETURN(TRUE);
}


void Locked_tables_list::reset()
{
  free_root(&m_locked_tables_root, MYF(0));
  m_locked_tables= NULL;
  m_locked_tables_last= &m_locked_tables;
  m_reopen_array= NULL;
  m_locked_tables_count= 0;
  some_table_marked_for_reopen= 0;
}


static bool auto_repair_table(THD *thd, TABLE_LIST *table_list)
{
  TABLE_SHARE *share;
  TABLE *entry;
  bool result= TRUE;

  thd->clear_error();

  if (!(entry= (TABLE*)my_malloc(sizeof(TABLE), MYF(MY_WME))))
    return result;

  if (!(share= tdc_acquire_share(thd, table_list, GTS_TABLE)))
    goto end_free;

  DBUG_ASSERT(! share->is_view);

  if (open_table_from_share(thd, share, table_list->alias,
                            HA_OPEN_KEYFILE | HA_TRY_READ_ONLY,
                            EXTRA_RECORD,
                            ha_open_options | HA_OPEN_FOR_REPAIR,
                            entry, FALSE) || ! entry->file ||
      (entry->file->is_crashed() && entry->file->ha_check_and_repair(thd)))
  {
    /* Give right error message */
    thd->clear_error();
    my_error(ER_NOT_KEYFILE, MYF(0), share->table_name.str);
    sql_print_error("Couldn't repair table: %s.%s", share->db.str,
                    share->table_name.str);
    if (entry->file)
      closefrm(entry);
  }
  else
  {
    thd->clear_error();			// Clear error message
    closefrm(entry);
    result= FALSE;
  }

  tdc_release_share(share);
  /* Remove the repaired share from the table cache. */
  tdc_remove_table(thd, TDC_RT_REMOVE_ALL,
                   table_list->db, table_list->table_name,
                   FALSE);
end_free:
  my_free(entry);
  return result;
}


check_and_update_routine_version(THD *thd, Sroutine_hash_entry *rt,
                                 sp_head *sp)
{
  ulong spc_version= sp_cache_version();
  /* sp is NULL if there is no such routine. */
  ulong version= sp ? sp->sp_cache_version() : spc_version;
  /*
    If the version in the parse tree is stale,
    or the version in the cache is stale and sp is not used,
    we need to reprepare.
    Sic: version != spc_version <--> sp is not NULL.
  */
  if (rt->m_sp_cache_version != version ||
      (version != spc_version && !sp->is_invoked()))
  {
    if (thd->m_reprepare_observer &&
        thd->m_reprepare_observer->report_error(thd))
    {
      /*
        Version of the sp cache is different from the
        previous execution of the prepared statement, and it is
        unacceptable for this SQLCOM. Error has been reported.
      */
      DBUG_ASSERT(thd->is_error());
      return TRUE;
    }
    /* Always maintain the latest cache version. */
    rt->m_sp_cache_version= version;
  }
  return FALSE;
}


my_bool mysql_rm_tmp_tables(void)
{
  uint i, idx;
  char	filePath[FN_REFLEN], *tmpdir, filePathCopy[FN_REFLEN];
  MY_DIR *dirp;
  FILEINFO *file;
  TABLE_SHARE share;
  THD *thd;
  DBUG_ENTER("mysql_rm_tmp_tables");

  if (!(thd= new THD(0)))
    DBUG_RETURN(1);
  thd->thread_stack= (char*) &thd;
  thd->store_globals();

  for (i=0; i<=mysql_tmpdir_list.max; i++)
  {
    tmpdir=mysql_tmpdir_list.list[i];
    /* See if the directory exists */
    if (!(dirp = my_dir(tmpdir,MYF(MY_WME | MY_DONT_SORT))))
      continue;

    /* Remove all SQLxxx tables from directory */

    for (idx=0 ; idx < (uint) dirp->number_of_files ; idx++)
    {
      file=dirp->dir_entry+idx;

      if (!strncmp(file->name, tmp_file_prefix, tmp_file_prefix_length))
      {
        char *ext= fn_ext(file->name);
        uint ext_len= strlen(ext);
        uint filePath_len= my_snprintf(filePath, sizeof(filePath),
                                       "%s%c%s", tmpdir, FN_LIBCHAR,
                                       file->name);
        if (!strcmp(reg_ext, ext))
        {
          handler *handler_file= 0;
          /* We should cut file extention before deleting of table */
          memcpy(filePathCopy, filePath, filePath_len - ext_len);
          filePathCopy[filePath_len - ext_len]= 0;
          init_tmp_table_share(thd, &share, "", 0, "", filePathCopy);
          if (!open_table_def(thd, &share) &&
              ((handler_file= get_new_handler(&share, thd->mem_root,
                                              share.db_type()))))
          {
            handler_file->ha_delete_table(filePathCopy);
            delete handler_file;
          }
          free_table_share(&share);
        }
        /*
          File can be already deleted by tmp_table.file->delete_table().
          So we hide error messages which happnes during deleting of these
          files(MYF(0)).
        */
        (void) mysql_file_delete(key_file_misc, filePath, MYF(0));
      }
    }
    my_dirend(dirp);
  }
  delete thd;
  DBUG_RETURN(0);
}


void Locked_tables_list::mark_table_for_reopen(THD *thd, TABLE *table)
{
  TABLE_SHARE *share= table->s;

    for (TABLE_LIST *table_list= m_locked_tables;
       table_list; table_list= table_list->next_global)
  {
    if (table_list->table->s == share)
      table_list->table->internal_set_needs_reopen(true);
  }
  /* This is needed in the case where lock tables where not used */
  table->internal_set_needs_reopen(true);
  some_table_marked_for_reopen= 1;
}


test_if_string_in_list(const char *find, List<String> *str_list)
{
  List_iterator<String> str_list_it(*str_list);
  String *curr_str;
  size_t find_length= strlen(find);
  while ((curr_str= str_list_it++))
  {
    if (find_length != curr_str->length())
      continue;
    if (!my_strcasecmp(system_charset_info, find, curr_str->ptr()))
      return TRUE;
  }
  return FALSE;
}


bool Alter_table_prelocking_strategy::
handle_table(THD *thd, Query_tables_list *prelocking_ctx,
             TABLE_LIST *table_list, bool *need_prelocking)
{
  return FALSE;
}


static my_bool close_cached_tables_callback(TDC_element *element,
                                            close_cached_tables_arg *arg)
{
  mysql_mutex_lock(&element->LOCK_table_share);
  if (element->share && element->flushed &&
      element->version < arg->refresh_version)
  {
    /* wait_for_old_version() will unlock mutex and free share */
    arg->element= element;
    return TRUE;
  }
  mysql_mutex_unlock(&element->LOCK_table_share);
  return FALSE;
}


open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
                                 TABLE_LIST *tables_end, uint flags)
{
  TABLE_LIST *table;

  DBUG_ASSERT(thd->locked_tables_mode);

  for (table= tables_start; table && table != tables_end;
       table= table->next_global)
  {
    /*
      Check below needs to be updated if this function starts
      called for SRO locks.
    */
    DBUG_ASSERT(table->mdl_request.type != MDL_SHARED_READ_ONLY);
    if (table->mdl_request.type < MDL_SHARED_UPGRADABLE ||
        table->open_type == OT_TEMPORARY_ONLY ||
        (table->open_type == OT_TEMPORARY_OR_BASE && is_temporary_table(table)))
    {
      continue;
    }

    /*
      We don't need to do anything about the found TABLE instance as it
      will be handled later in open_tables(), we only need to check that
      an upgradable lock is already acquired. When we enter LOCK TABLES
      mode, SNRW locks are acquired before all other locks. So if under
      LOCK TABLES we find that there is TABLE instance with upgradeable
      lock, all other instances of TABLE for the same table will have the
      same ticket.

      Note that this works OK even for CREATE TABLE statements which
      request X type of metadata lock. This is because under LOCK TABLES
      such statements don't create the table but only check if it exists
      or, in most complex case, only insert into it.
      Thus SNRW lock should be enough.

      Note that find_table_for_mdl_upgrade() will report an error if
      no suitable ticket is found.
    */
    if (!find_table_for_mdl_upgrade(thd, table->db, table->table_name, NULL))
      return TRUE;
  }

  return FALSE;
}


int dynamic_column_error_message(enum_dyncol_func_result rc)
{
  switch (rc) {
  case ER_DYNCOL_YES:
  case ER_DYNCOL_OK:
  case ER_DYNCOL_TRUNCATED:
    break; // it is not an error
  case ER_DYNCOL_FORMAT:
    my_error(ER_DYN_COL_WRONG_FORMAT, MYF(0));
    break;
  case ER_DYNCOL_LIMIT:
    my_error(ER_DYN_COL_IMPLEMENTATION_LIMIT, MYF(0));
    break;
  case ER_DYNCOL_RESOURCE:
    my_error(ER_OUT_OF_RESOURCES, MYF(0));
    break;
  case ER_DYNCOL_DATA:
    my_error(ER_DYN_COL_DATA, MYF(0));
    break;
  case ER_DYNCOL_UNKNOWN_CHARSET:
    my_error(ER_DYN_COL_WRONG_CHARSET, MYF(0));
    break;
  }
  return rc;
}


static my_bool list_open_tables_callback(TDC_element *element,
                                         list_open_tables_arg *arg)
{
  char *db= (char*) element->m_key;
  char *table_name= (char*) element->m_key + strlen((char*) element->m_key) + 1;

  if (arg->db && my_strcasecmp(system_charset_info, arg->db, db))
    return FALSE;
  if (arg->wild && wild_compare(table_name, arg->wild, 0))
    return FALSE;

  /* Check if user has SELECT privilege for any column in the table */
  arg->table_list.db= db;
  arg->table_list.table_name= table_name;
  arg->table_list.grant.privilege= 0;

  if (check_table_access(arg->thd, SELECT_ACL, &arg->table_list, TRUE, 1, TRUE))
    return FALSE;

  if (!(*arg->start_list= (OPEN_TABLE_LIST *) arg->thd->alloc(
                    sizeof(**arg->start_list) + element->m_key_length)))
    return TRUE;

  strmov((*arg->start_list)->table=
         strmov(((*arg->start_list)->db= (char*) ((*arg->start_list) + 1)),
                db) + 1, table_name);
  (*arg->start_list)->in_use= 0;

  mysql_mutex_lock(&element->LOCK_table_share);
  All_share_tables_list::Iterator it(element->all_tables);
  TABLE *table;
  while ((table= it++))
    if (table->in_use)
      ++(*arg->start_list)->in_use;
  mysql_mutex_unlock(&element->LOCK_table_share);
  (*arg->start_list)->locked= 0;                   /* Obsolete. */
  arg->start_list= &(*arg->start_list)->next;
  *arg->start_list= 0;
  return FALSE;
}


bool table_already_fk_prelocked(TABLE_LIST *tl, LEX_STRING *db,
                                LEX_STRING *table, thr_lock_type lock_type)
{
  for (; tl; tl= tl->next_global )
  {
    if (tl->lock_type >= lock_type &&
        tl->prelocking_placeholder == TABLE_LIST::FK &&
        strcmp(tl->db, db->str) == 0 &&
        strcmp(tl->table_name, table->str) == 0)
      return true;
  }
  return false;
}


fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, Field **ptr,
                                     List<Item> &values, bool ignore_errors,
                                     enum trg_event_type event)
{
  bool result;
  Table_triggers_list *triggers= table->triggers;

  result= fill_record(thd, table, ptr, values, ignore_errors, FALSE);

  if (!result && triggers && *ptr)
    result= triggers->process_triggers(thd, event, TRG_ACTION_BEFORE, TRUE) ||
            not_null_fields_have_null_values(table);
  /*
    Re-calculate virtual fields to cater for cases when base columns are
    updated by the triggers.
  */
  if (!result && triggers && *ptr)
  {
    DBUG_ASSERT(table == (*ptr)->table);
    if (table->vfield)
      result= table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_WRITE);
  }
  return result;

}


open_system_table_for_update(THD *thd, TABLE_LIST *one_table)
{
  DBUG_ENTER("open_system_table_for_update");

  TABLE *table= open_ltable(thd, one_table, one_table->lock_type,
                            MYSQL_LOCK_IGNORE_TIMEOUT);
  if (table)
  {
    DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_SYSTEM);
    table->use_all_columns();
  }

  DBUG_RETURN(table);
}


Locked_tables_list::unlock_locked_table(THD *thd, MDL_ticket *mdl_ticket)
{
  /*
    Ensure we are in locked table mode.
    As this function is only called on error condition it's better
    to check this condition here than in the caller.
  */
  if (thd->locked_tables_mode != LTM_LOCK_TABLES)
    return;

  if (mdl_ticket)
  {
    /*
      Under LOCK TABLES we may have several instances of table open
      and locked and therefore have to remove several metadata lock
      requests associated with them.
    */
    thd->mdl_context.release_all_locks_for_name(mdl_ticket);
  }

  if (thd->lock->table_count == 0)
    unlock_locked_tables(thd);
}


fill_record_n_invoke_before_triggers(THD *thd, TABLE *table,
                                     List<Item> &fields,
                                     List<Item> &values, bool ignore_errors,
                                     enum trg_event_type event)
{
  bool result;
  Table_triggers_list *triggers= table->triggers;

  result= fill_record(thd, table, fields, values, ignore_errors,
                      event == TRG_EVENT_UPDATE);

  if (!result && triggers)
  {
    if (triggers->process_triggers(thd, event, TRG_ACTION_BEFORE,
                                    TRUE) ||
        not_null_fields_have_null_values(table))
      return TRUE;

    /*
      Re-calculate virtual fields to cater for cases when base columns are
      updated by the triggers.
    */
    if (table->vfield && fields.elements)
    {
      Item *fld= (Item_field*) fields.head();
      Item_field *item_field= fld->field_for_view_update();
      if (item_field)
      {
        DBUG_ASSERT(table == item_field->field->table);
        result|= table->update_virtual_fields(table->file,
                                              VCOL_UPDATE_FOR_WRITE);
      }
    }
  }
  return result;
}


TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db,
                                  const char *table_name, int *p_error)
{
  TABLE *tab= find_locked_table(thd->open_tables, db, table_name);
  int error;

  if (!tab)
  {
    error= ER_TABLE_NOT_LOCKED;
    goto err_exit;
  }

  /*
    It is not safe to upgrade the metadata lock without a global IX lock.
    This can happen with FLUSH TABLES <list> WITH READ LOCK as we in these
    cases don't take a global IX lock in order to be compatible with
    global read lock.
  */
  if (!thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, "", "",
                                      MDL_INTENTION_EXCLUSIVE))
  {
    error= ER_TABLE_NOT_LOCKED_FOR_WRITE;
    goto err_exit;
  }

  while (tab->mdl_ticket != NULL &&
         !tab->mdl_ticket->is_upgradable_or_exclusive() &&
         (tab= find_locked_table(tab->next, db, table_name)))
    continue;

  if (unlikely(!tab))
  {
    error= ER_TABLE_NOT_LOCKED_FOR_WRITE;
    goto err_exit;
  }

  return tab;

err_exit:
  if (p_error)
    *p_error= error;
  else
    my_error(error, MYF(0), table_name);

  return NULL;
}


void wrap_ident(THD *thd, Item **conds)
{
  Item_direct_ref_to_ident *wrapper;
  DBUG_ASSERT((*conds)->type() == Item::FIELD_ITEM || (*conds)->type() == Item::REF_ITEM);
  Query_arena *arena, backup;
  arena= thd->activate_stmt_arena_if_needed(&backup);
  if ((wrapper= new (thd->mem_root) Item_direct_ref_to_ident(thd, (Item_ident *) (*conds))))
    (*conds)= (Item*) wrapper;
  if (arena)
    thd->restore_active_arena(arena, &backup);
}


void Locked_tables_list::add_back_last_deleted_lock(TABLE_LIST *dst_table_list)
{
  /* Link the lock back in the locked tables list */
  dst_table_list->prev_global= m_locked_tables_last;
  *m_locked_tables_last= dst_table_list;
  m_locked_tables_last= &dst_table_list->next_global;
  dst_table_list->next_global= 0;
  m_locked_tables_count++;
}


open_table_get_mdl_lock(THD *thd, Open_table_context *ot_ctx,
                        MDL_request *mdl_request,
                        uint flags,
                        MDL_ticket **mdl_ticket)
{
  MDL_request mdl_request_shared;

  if (flags & (MYSQL_OPEN_FORCE_SHARED_MDL |
               MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL))
  {
    /*
      MYSQL_OPEN_FORCE_SHARED_MDL flag means that we are executing
      PREPARE for a prepared statement and want to override
      the type-of-operation aware metadata lock which was set
      in the parser/during view opening with a simple shared
      metadata lock.
      This is necessary to allow concurrent execution of PREPARE
      and LOCK TABLES WRITE statement against the same table.

      MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL flag means that we open
      the table in order to get information about it for one of I_S
      queries and also want to override the type-of-operation aware
      shared metadata lock which was set earlier (e.g. during view
      opening) with a high-priority shared metadata lock.
      This is necessary to avoid unnecessary waiting and extra
      ER_WARN_I_S_SKIPPED_TABLE warnings when accessing I_S tables.

      These two flags are mutually exclusive.
    */
    DBUG_ASSERT(!(flags & MYSQL_OPEN_FORCE_SHARED_MDL) ||
                !(flags & MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL));

    mdl_request_shared.init(&mdl_request->key,
                            (flags & MYSQL_OPEN_FORCE_SHARED_MDL) ?
                            MDL_SHARED : MDL_SHARED_HIGH_PRIO,
                            MDL_TRANSACTION);
    mdl_request= &mdl_request_shared;
  }

  if (flags & MYSQL_OPEN_FAIL_ON_MDL_CONFLICT)
  {
    /*
      When table is being open in order to get data for I_S table,
      we might have some tables not only open but also locked (e.g. when
      this happens under LOCK TABLES or in a stored function).
      As a result by waiting on a conflicting metadata lock to go away
      we may create a deadlock which won't entirely belong to the
      MDL subsystem and thus won't be detectable by this subsystem's
      deadlock detector.
      To avoid such situation we skip the trouble-making table if
      there is a conflicting lock.
    */
    if (thd->mdl_context.try_acquire_lock(mdl_request))
      return TRUE;
    if (mdl_request->ticket == NULL)
    {
      my_error(ER_WARN_I_S_SKIPPED_TABLE, MYF(0),
               mdl_request->key.db_name(), mdl_request->key.name());
      return TRUE;
    }
  }
  else
  {
    /*
      We are doing a normal table open. Let us try to acquire a metadata
      lock on the table. If there is a conflicting lock, acquire_lock()
      will wait for it to go away. Sometimes this waiting may lead to a
      deadlock, with the following results:
      1) If a deadlock is entirely within MDL subsystem, it is
         detected by the deadlock detector of this subsystem.
         ER_LOCK_DEADLOCK error is produced. Then, the error handler
         that is installed prior to the call to acquire_lock() attempts
         to request a back-off and retry. Upon success, ER_LOCK_DEADLOCK
         error is suppressed, otherwise propagated up the calling stack.
      2) Otherwise, a deadlock may occur when the wait-for graph
         includes edges not visible to the MDL deadlock detector.
         One such example is a wait on an InnoDB row lock, e.g. when:
         conn C1 gets SR MDL lock on t1 with SELECT * FROM t1
         conn C2 gets a row lock on t2 with  SELECT * FROM t2 FOR UPDATE
         conn C3 gets in and waits on C1 with DROP TABLE t0, t1
         conn C2 continues and blocks on C3 with SELECT * FROM t0
         conn C1 deadlocks by waiting on C2 by issuing SELECT * FROM
         t2 LOCK IN SHARE MODE.
         Such circular waits are currently only resolved by timeouts,
         e.g. @@innodb_lock_wait_timeout or @@lock_wait_timeout.
    */
    MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);

    thd->push_internal_handler(&mdl_deadlock_handler);
    bool result= thd->mdl_context.acquire_lock(mdl_request,
                                               ot_ctx->get_timeout());
    thd->pop_internal_handler();

    if (result && !ot_ctx->can_recover_from_failed_open())
      return TRUE;
  }
  *mdl_ticket= mdl_request->ticket;
  return FALSE;
}


uint get_table_def_key(const TABLE_LIST *table_list, const char **key)
{
  /*
    This call relies on the fact that TABLE_LIST::mdl_request::key object
    is properly initialized, so table definition cache can be produced
    from key used by MDL subsystem.
  */
  DBUG_ASSERT(!strcmp(table_list->get_db_name(),
                      table_list->mdl_request.key.db_name()) &&
              !strcmp(table_list->get_table_name(),
                      table_list->mdl_request.key.name()));

  *key= (const char*)table_list->mdl_request.key.ptr() + 1;
  return table_list->mdl_request.key.length() - 1;
}


TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l,
                                thr_lock_type lock_type, uint flags,
                                Prelocking_strategy *prelocking_strategy)
{
  TABLE_LIST *save_next_global;
  DBUG_ENTER("open_n_lock_single_table");

  /* Remember old 'next' pointer. */
  save_next_global= table_l->next_global;
  /* Break list. */
  table_l->next_global= NULL;

  /* Set requested lock type. */
  table_l->lock_type= lock_type;
  /* Allow to open real tables only. */
  table_l->required_type= FRMTYPE_TABLE;

  /* Open the table. */
  if (open_and_lock_tables(thd, table_l, FALSE, flags,
                           prelocking_strategy))
    table_l->table= NULL; /* Just to be sure. */

  /* Restore list. */
  table_l->next_global= save_next_global;

  DBUG_RETURN(table_l->table);
}


No_such_table_error_handler::handle_condition(THD *,
                                              uint sql_errno,
                                              const char*,
                                              Sql_condition::enum_warning_level *level,
                                              const char*,
                                              Sql_condition ** cond_hdl)
{
  *cond_hdl= NULL;
  if (sql_errno == ER_NO_SUCH_TABLE || sql_errno == ER_NO_SUCH_TABLE_IN_ENGINE)
  {
    m_handled_errors++;
    return TRUE;
  }

  if (*level == Sql_condition::WARN_LEVEL_ERROR)
    m_unhandled_errors++;
  return FALSE;
}


close_mysql_tables(THD *thd)
{
  if (! thd->in_sub_stmt)
    trans_commit_stmt(thd);
  close_thread_tables(thd);
  thd->release_transactional_locks();
}


close_system_tables(THD *thd, Open_tables_backup *backup)
{
  close_thread_tables(thd);
  thd->restore_backup_open_tables_state(backup);
}


Locked_tables_list::reopen_tables(THD *thd, bool need_reopen)
{
  Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN);
  size_t reopen_count= 0;
  MYSQL_LOCK *lock;
  MYSQL_LOCK *merged_lock;
  DBUG_ENTER("Locked_tables_list::reopen_tables");

  DBUG_ASSERT(some_table_marked_for_reopen || !need_reopen);

  /* Reset flag that some table was marked for reopen */
  some_table_marked_for_reopen= 0;

  for (TABLE_LIST *table_list= m_locked_tables;
       table_list; table_list= table_list->next_global)
  {
    if (need_reopen)
    {
      if (!table_list->table || !table_list->table->needs_reopen())
        continue;
      for (TABLE **prev= &thd->open_tables; *prev; prev= &(*prev)->next)
      {
        if (*prev == table_list->table)
        {
          thd->locked_tables_list.unlink_from_list(thd, table_list, false);
          mysql_lock_remove(thd, thd->lock, *prev);
          (*prev)->file->extra(HA_EXTRA_PREPARE_FOR_FORCED_CLOSE);
          close_thread_table(thd, prev);
          break;
        }
      }
      DBUG_ASSERT(table_list->table == NULL);
    }
    else
    {
      if (table_list->table)                      /* The table was not closed */
        continue;
    }

    DBUG_ASSERT(reopen_count < m_locked_tables_count);
    m_reopen_array[reopen_count++]= table_list;
  }
  if (reopen_count)
  {
    TABLE **tables= (TABLE**) my_alloca(reopen_count * sizeof(TABLE*));

    for (uint i= 0 ; i < reopen_count ; i++)
    {
      TABLE_LIST *table_list= m_reopen_array[i];
      /* Links into thd->open_tables upon success */
      if (open_table(thd, table_list, &ot_ctx))
      {
        unlink_all_closed_tables(thd, 0, i);
        my_afree((void*) tables);
        DBUG_RETURN(TRUE);
      }
      tables[i]= table_list->table;
      table_list->table->pos_in_locked_tables= table_list;
      /* See also the comment on lock type in init_locked_tables(). */
      table_list->table->reginfo.lock_type= table_list->lock_type;
    }

    thd->in_lock_tables= 1;
    /*
      We re-lock all tables with mysql_lock_tables() at once rather
      than locking one table at a time because of the case
      reported in Bug#45035: when the same table is present
      in the list many times, thr_lock.c fails to grant READ lock
      on a table that is already locked by WRITE lock, even if
      WRITE lock is taken by the same thread. If READ and WRITE
      lock are passed to thr_lock.c in the same list, everything
      works fine. Patching legacy code of thr_lock.c is risking to
      break something else.
    */
    lock= mysql_lock_tables(thd, tables, reopen_count,
                            MYSQL_OPEN_REOPEN);
    thd->in_lock_tables= 0;
    if (lock == NULL || (merged_lock=
                         mysql_lock_merge(thd->lock, lock)) == NULL)
    {
      unlink_all_closed_tables(thd, lock, reopen_count);
      if (! thd->killed)
        my_error(ER_LOCK_DEADLOCK, MYF(0));
      my_afree((void*) tables);
      DBUG_RETURN(TRUE);
    }
    thd->lock= merged_lock;
    my_afree((void*) tables);
  }
  DBUG_RETURN(FALSE);
}


bool wait_while_table_is_used(THD *thd, TABLE *table,
                              enum ha_extra_function function)
{
  DBUG_ENTER("wait_while_table_is_used");
  DBUG_ASSERT(!table->s->tmp_table);
  DBUG_PRINT("enter", ("table: '%s'  share: %p  db_stat: %u  version: %lld",
                       table->s->table_name.str, table->s,
                       table->db_stat, table->s->tdc->version));

  if (thd->mdl_context.upgrade_shared_lock(
             table->mdl_ticket, MDL_EXCLUSIVE,
             thd->variables.lock_wait_timeout))
    DBUG_RETURN(TRUE);

  tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN,
                   table->s->db.str, table->s->table_name.str,
                   FALSE);
  /* extra() call must come only after all instances above are closed */
  if (function != HA_EXTRA_NOT_USED)
    DBUG_RETURN(table->file->extra(function));
  DBUG_RETURN(FALSE);
}


bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags,
                                    uint dt_phases)
{
  DML_prelocking_strategy prelocking_strategy;
  uint counter;
  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
  DBUG_ENTER("open_normal_and_derived_tables");
  if (open_tables(thd, &tables, &counter, flags, &prelocking_strategy) ||
      mysql_handle_derived(thd->lex, dt_phases))
    goto end;

  DBUG_RETURN(0);
end:
  /*
    No need to commit/rollback the statement transaction: it's
    either not started or we're filling in an INFORMATION_SCHEMA
    table on the fly, and thus mustn't manipulate with the
    transaction of the enclosing statement.
  */
  DBUG_ASSERT(thd->transaction.stmt.is_empty() ||
              (thd->state_flags & Open_tables_state::BACKUPS_AVAIL));
  close_thread_tables(thd);
  /* Don't keep locks for a failed statement. */
  thd->mdl_context.rollback_to_savepoint(mdl_savepoint);

  DBUG_RETURN(TRUE); /* purecov: inspected */
}


close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
                          ha_extra_function extra,
                          TABLE *skip_table)
{
  DBUG_ASSERT(!share->tmp_table);

  char key[MAX_DBKEY_LENGTH];
  uint key_length= share->table_cache_key.length;
  const char *db= key;
  const char *table_name= db + share->db.length + 1;
  bool remove_from_locked_tables= extra != HA_EXTRA_NOT_USED;

  memcpy(key, share->table_cache_key.str, key_length);

  for (TABLE **prev= &thd->open_tables; *prev; )
  {
    TABLE *table= *prev;

    if (table->s->table_cache_key.length == key_length &&
        !memcmp(table->s->table_cache_key.str, key, key_length) &&
        table != skip_table)
    {
      thd->locked_tables_list.unlink_from_list(thd,
                                               table->pos_in_locked_tables,
                                               remove_from_locked_tables);
      /* Inform handler that there is a drop table or a rename going on */
      if (extra != HA_EXTRA_NOT_USED && table->db_stat)
      {
        table->file->extra(extra);
        extra= HA_EXTRA_NOT_USED;               // Call extra once!
      }

      /*
        Does nothing if the table is not locked.
        This allows one to use this function after a table
        has been unlocked, e.g. in partition management.
      */
      mysql_lock_remove(thd, thd->lock, table);
      close_thread_table(thd, prev);
    }
    else
    {
      /* Step to next entry in open_tables list. */
      prev= &table->next;
    }
  }
  if (skip_table == NULL)
  {
    /* Remove the table share from the cache. */
    tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db, table_name,
                     FALSE);
  }
}


TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
                   uint lock_flags)
{
  TABLE *table;
  Open_table_context ot_ctx(thd, lock_flags);
  bool error;
  DBUG_ENTER("open_ltable");

  /* Ignore temporary tables as they have already been opened. */
  if (table_list->table)
    DBUG_RETURN(table_list->table);

  /* should not be used in a prelocked_mode context, see NOTE above */
  DBUG_ASSERT(thd->locked_tables_mode < LTM_PRELOCKED);

  THD_STAGE_INFO(thd, stage_opening_tables);
  thd->current_tablenr= 0;
  /* open_ltable can be used only for BASIC TABLEs */
  table_list->required_type= FRMTYPE_TABLE;

  /* This function can't properly handle requests for such metadata locks. */
  DBUG_ASSERT(table_list->mdl_request.type < MDL_SHARED_UPGRADABLE);

  while ((error= open_table(thd, table_list, &ot_ctx)) &&
         ot_ctx.can_recover_from_failed_open())
  {
    /*
      Even though we have failed to open table we still need to
      call release_transactional_locks() to release metadata locks which
      might have been acquired successfully.
    */
    thd->mdl_context.rollback_to_savepoint(ot_ctx.start_of_statement_svp());
    table_list->mdl_request.ticket= 0;
    if (ot_ctx.recover_from_failed_open())
      break;
  }

  if (!error)
  {
    /*
      We can't have a view or some special "open_strategy" in this function
      so there should be a TABLE instance.
    */
    DBUG_ASSERT(table_list->table);
    table= table_list->table;
    if (table->file->ht->db_type == DB_TYPE_MRG_MYISAM)
    {
      /* A MERGE table must not come here. */
      /* purecov: begin tested */
      my_error(ER_WRONG_OBJECT, MYF(0), table->s->db.str,
               table->s->table_name.str, "BASE TABLE");
      table= 0;
      goto end;
      /* purecov: end */
    }

    table_list->lock_type= lock_type;
    table->grant= table_list->grant;
    if (thd->locked_tables_mode)
    {
      if (check_lock_and_start_stmt(thd, thd->lex, table_list))
	table= 0;
    }
    else
    {
      DBUG_ASSERT(thd->lock == 0);	// You must lock everything at once
      if ((table->reginfo.lock_type= lock_type) != TL_UNLOCK)
	if (! (thd->lock= mysql_lock_tables(thd, &table_list->table, 1,
                                            lock_flags)))
        {
          table= 0;
        }
    }
  }
  else
    table= 0;

end:
  if (table == NULL)
  {
    if (!thd->in_sub_stmt)
      trans_rollback_stmt(thd);
    close_thread_tables(thd);
  }
  THD_STAGE_INFO(thd, stage_after_opening_tables);

  thd_proc_info(thd, 0);
  DBUG_RETURN(table);
}


bool open_and_lock_tables(THD *thd, const DDL_options_st &options,
                          TABLE_LIST *tables,
                          bool derived, uint flags,
                          Prelocking_strategy *prelocking_strategy)
{
  uint counter;
  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
  DBUG_ENTER("open_and_lock_tables");
  DBUG_PRINT("enter", ("derived handling: %d", derived));

  if (open_tables(thd, options, &tables, &counter, flags, prelocking_strategy))
    goto err;

  DBUG_EXECUTE_IF("sleep_open_and_lock_after_open", {
                  const char *old_proc_info= thd->proc_info;
                  thd->proc_info= "DBUG sleep";
                  my_sleep(6000000);
                  thd->proc_info= old_proc_info;});

  if (lock_tables(thd, tables, counter, flags))
    goto err;

  (void) read_statistics_for_tables_if_needed(thd, tables);

  if (derived)
  {
    if (mysql_handle_derived(thd->lex, DT_INIT))
      goto err;
    if (thd->prepare_derived_at_open &&
        (mysql_handle_derived(thd->lex, DT_PREPARE)))
      goto err;
  }

  DBUG_RETURN(FALSE);
err:
  if (! thd->in_sub_stmt)
    trans_rollback_stmt(thd);  /* Necessary if derived handling failed. */
  close_thread_tables(thd);
  /* Don't keep locks for a failed statement. */
  thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
  DBUG_RETURN(TRUE);
}


bool setup_tables_and_check_access(THD *thd, Name_resolution_context *context,
                                   List<TABLE_LIST> *from_clause,
                                   TABLE_LIST *tables, List<TABLE_LIST> &leaves,
                                   bool select_insert, ulong want_access_first,
                                   ulong want_access, bool full_table_list)
{
  DBUG_ENTER("setup_tables_and_check_access");

  if (setup_tables(thd, context, from_clause, tables,
                   leaves, select_insert, full_table_list))
    DBUG_RETURN(TRUE);

  List_iterator<TABLE_LIST> ti(leaves);
  TABLE_LIST *table_list;
  ulong access= want_access_first;
  while ((table_list= ti++))
  {
    if (table_list->belong_to_view && !table_list->view &&
        check_single_table_access(thd, access, table_list, FALSE))
    {
      tables->hide_view_error(thd);
      DBUG_RETURN(TRUE);
    }
    access= want_access;
  }
  DBUG_RETURN(FALSE);
}


bool Alter_table_prelocking_strategy::
handle_view(THD *thd, Query_tables_list *prelocking_ctx,
            TABLE_LIST *table_list, bool *need_prelocking)
{
  return FALSE;
}


open_system_tables_for_read(THD *thd, TABLE_LIST *table_list,
                            Open_tables_backup *backup)
{
  Query_tables_list query_tables_list_backup;
  LEX *lex= thd->lex;

  DBUG_ENTER("open_system_tables_for_read");

  /*
    Besides using new Open_tables_state for opening system tables,
    we also have to backup and reset/and then restore part of LEX
    which is accessed by open_tables() in order to determine if
    prelocking is needed and what tables should be added for it.
    close_system_tables() doesn't require such treatment.
  */
  lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
  thd->reset_n_backup_open_tables_state(backup);
  thd->lex->sql_command= SQLCOM_SELECT;

  if (open_and_lock_tables(thd, table_list, FALSE,
                           MYSQL_OPEN_IGNORE_FLUSH |
                           MYSQL_LOCK_IGNORE_TIMEOUT))
  {
    lex->restore_backup_query_tables_list(&query_tables_list_backup);
    thd->restore_backup_open_tables_state(backup);
    DBUG_RETURN(TRUE);
  }

  for (TABLE_LIST *tables= table_list; tables; tables= tables->next_global)
  {
    DBUG_ASSERT(tables->table->s->table_category == TABLE_CATEGORY_SYSTEM);
    tables->table->use_all_columns();
  }
  lex->restore_backup_query_tables_list(&query_tables_list_backup);

  DBUG_RETURN(FALSE);
}


bool close_cached_connection_tables(THD *thd, LEX_STRING *connection)
{
  close_cached_connection_tables_arg argument;
  DBUG_ENTER("close_cached_connections");
  DBUG_ASSERT(thd);

  argument.thd= thd;
  argument.connection= connection;
  argument.tables= NULL;

  if (tdc_iterate(thd,
                  (my_hash_walk_action) close_cached_connection_tables_callback,
                  &argument))
    DBUG_RETURN(true);

  DBUG_RETURN(argument.tables ?
              close_cached_tables(thd, argument.tables, FALSE, LONG_TIMEOUT) :
              false);
}


open_log_table(THD *thd, TABLE_LIST *one_table, Open_tables_backup *backup)
{
  uint flags= ( MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK |
                MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY |
                MYSQL_OPEN_IGNORE_FLUSH |
                MYSQL_LOCK_IGNORE_TIMEOUT |
                MYSQL_LOCK_LOG_TABLE);
  TABLE *table;
  /* Save value that is changed in mysql_lock_tables() */
  ulonglong save_utime_after_lock= thd->utime_after_lock;
  DBUG_ENTER("open_log_table");

  thd->reset_n_backup_open_tables_state(backup);

  if ((table= open_ltable(thd, one_table, one_table->lock_type, flags)))
  {
    DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_LOG);
    /* Make sure all columns get assigned to a default value */
    table->use_all_columns();
    DBUG_ASSERT(table->no_replicate);
  }
  else
    thd->restore_backup_open_tables_state(backup);

  thd->utime_after_lock= save_utime_after_lock;
  DBUG_RETURN(table);
}


void drop_open_table(THD *thd, TABLE *table, const char *db_name,
                     const char *table_name)
{
  DBUG_ENTER("drop_open_table");
  if (table->s->tmp_table)
    thd->drop_temporary_table(table, NULL, true);
  else
  {
    DBUG_ASSERT(table == thd->open_tables);

    handlerton *table_type= table->s->db_type();
    table->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
    close_thread_table(thd, &thd->open_tables);
    /* Remove the table share from the table cache. */
    tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db_name, table_name,
                     FALSE);
    /* Remove the table from the storage engine and rm the .frm. */
    quick_rm_table(thd, table_type, db_name, table_name, 0);
 }
  DBUG_VOID_RETURN;
}