bool mysql_unpack_partition(THD *thd,
                            char *part_buf, uint part_info_len,
                            TABLE* table, bool is_create_table_ind,
                            handlerton *default_db_type,
                            bool *work_part_info_used)
{
  bool result= TRUE;
  partition_info *part_info;
  CHARSET_INFO *old_character_set_client= thd->variables.character_set_client;
  LEX *old_lex= thd->lex;
  LEX lex;
  PSI_statement_locker *parent_locker= thd->m_statement_psi;
  DBUG_ENTER("mysql_unpack_partition");

  thd->variables.character_set_client= system_charset_info;

  Parser_state parser_state;
  if (unlikely(parser_state.init(thd, part_buf, part_info_len)))
    goto end;

  if (unlikely(init_lex_with_single_table(thd, table, &lex)))
    goto end;

  *work_part_info_used= FALSE;

  if (unlikely(!(lex.part_info= new partition_info())))
    goto end;

  lex.part_info->table= table;       /* Indicates MYSQLparse from this place */
  part_info= lex.part_info;
  DBUG_PRINT("info", ("Parse: %s", part_buf));

  thd->m_statement_psi= NULL;
  if (unlikely(parse_sql(thd, & parser_state, NULL)) ||
      unlikely(part_info->fix_parser_data(thd)))
  {
    thd->free_items();
    thd->m_statement_psi= parent_locker;
    goto end;
  }
  thd->m_statement_psi= parent_locker;
  /*
    The parsed syntax residing in the frm file can still contain defaults.
    The reason is that the frm file is sometimes saved outside of this
    MySQL Server and used in backup and restore of clusters or partitioned
    tables. It is not certain that the restore will restore exactly the
    same default partitioning.
    
    The easiest manner of handling this is to simply continue using the
    part_info we already built up during mysql_create_table if we are
    in the process of creating a table. If the table already exists we
    need to discover the number of partitions for the default parts. Since
    the handler object hasn't been created here yet we need to postpone this
    to the fix_partition_func method.
  */

  DBUG_PRINT("info", ("Successful parse"));
  DBUG_PRINT("info", ("default engine = %s, default_db_type = %s",
             ha_resolve_storage_engine_name(part_info->default_engine_type),
             ha_resolve_storage_engine_name(default_db_type)));
  if (is_create_table_ind && old_lex->sql_command == SQLCOM_CREATE_TABLE)
  {
    /*
      When we come here we are doing a create table. In this case we
      have already done some preparatory work on the old part_info
      object. We don't really need this new partition_info object.
      Thus we go back to the old partition info object.
      We need to free any memory objects allocated on item_free_list
      by the parser since we are keeping the old info from the first
      parser call in CREATE TABLE.

      This table object can not be used any more. However, since
      this is CREATE TABLE, we know that it will be destroyed by the
      caller, and rely on that.
    */
    thd->free_items();
    part_info= thd->work_part_info;
    *work_part_info_used= true;
  }
  table->part_info= part_info;
  part_info->table= table;
  table->file->set_part_info(part_info);
  if (!part_info->default_engine_type)
    part_info->default_engine_type= default_db_type;
  DBUG_ASSERT(part_info->default_engine_type == default_db_type);
  DBUG_ASSERT(part_info->default_engine_type->db_type != DB_TYPE_UNKNOWN);
  DBUG_ASSERT(part_info->default_engine_type != partition_hton);
  result= FALSE;
end:
  end_lex_with_single_table(thd, table, old_lex);
  thd->variables.character_set_client= old_character_set_client;
  DBUG_RETURN(result);
}