锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

2022-07-07 mysql/stonedb子查询流程分析

时间:2023-12-28 11:37:02 fce连接器

目录

摘要:

调用堆栈:

时序流程:

关键函数:

mysql_parse

mysql_execute_command:

execute_sqlcom_select:

do_select:

SDB_HandleSelect:

HandleSelect:

Execute:

Preexecute:

UpdateMultiIndex:

ApplyDescriptor:

EvaluatePackImpl:


摘要:

对mysql/stonedb分析查询器流程

调用堆栈:

 (gdb) bt #0  stonedb::core::Descriptor::EvaluatePackImpl (this=0x7fce10168040, mit=...) at /root/zsl/stonedb/storage/stonedb/core/descriptor.cpp:874 #1  0x0000000001ce8c78 in stonedb::core::Descriptor::EvaluatePack (this=0x7fce10168040, mit=...) at /root/zsl/stonedb/storage/stonedb/core/descriptor.cpp:988 #2  0x0000000001d51a2f in stonedb::core::ParameterizedFilter::ApplyDescriptor (this=0x7fce10157f40, desc_number=1, limit=-1)     at /root/zsl/stonedb/storage/stonedb/core/parameterized_filter.cpp:1340 #3  0x0000000001d5005b in stonedb::core::ParameterizedFilter::UpdateMultiIndex (this=0x7fce10157f40, count_only=false, limit=-1)     at /root/zsl/stonedb/storage/stonedb/core/parameterized_filter.cpp:1053 #4  0x00000000019e9800 in stonedb::core::Query::Preexecute (this=0x7fd1a84a3b80, qu=..., sender=0x7fce10140d70, display_now=true)     at /root/zsl/stonedb/storage/stonedb/core/query.cpp:776 #5  0x00000000019b845f in stonedb::core::Engine::Execute (this=0x3661870, thd=0x3616030, lex=0x3617df0, result_output=0x7fce10135f90, unit_for_union=0x0)     at /root/zsl/stonedb/storage/stonedb/core/engine_execute.cpp:408 #6  0x00000000019b77e6 in stonedb::core::Engine::HandleSelect (this=0x3661870, thd=0x3616030, lex=0x3617df0, result=@0x7fd1a84a40a0: 0x7fce10135f90,      setup_tables_done_option=0, res=@0x7fd1a84a409c: 0, optimize_after_sdb=@0x7fd1a84a4094: 1, sdb_free_join=@0x7fd1a84a4098: 1, with_insert=0)     at /root/zsl/stonedb/storage/stonedb/core/engine_execute.cpp:232 #7  0x0000000001aad103 in stonedb::dbhandler::SDB_HandleSelect (thd=0x3616030, lex=0x3617df0, result=@0x7fd1a84a40a0: 0x7fce10135f90, setup_tables_done_option=0,      res=@0x7fd1a84a409c: 0, optimize_after_sdb=@0x7fd1a84a4094: 1, sdb_free_join=@0x7fd1a84a4098: 1, with_insert=0)     at /root/zsl/stonedb/storage/stonedb/handler/ha_rcengine.cpp:82 #8  0x000000000137b2b0 in execute_sqlcom_select (thd=0x3616030, all_tables=0x7fce100052e0) #9  0x00000000013740c1 in mysql_execute_command (thd=0x3616030) #10 0x000000000137dd0c in mysql_parse (thd=0x3616030,      rawbuf=0x7fce10004bc0 "select\n    o_orderpriority,\n    count(*) as order_count\nfrom\n    orders\nwhere\n    o_orderdate >= date '1993-07-01'\n    and o_orderdate < date '1993-07-01'   interval '3' month\n    and exists (\n       "..., length=414, parser_state=0x7fd1a84a5170) #11 0x00000000013712bd in dispatch_command (command=COM_QUERY, thd=0x3616030,      packet=0x50062f1 "select\n    o_orderpriority,\n    count(*) as order_count\nfrom\n    orders\nwhere\n    o_orderdate >= date '1993-07-01'\n    and o_orderdate < date '1993-07-01'   interval '3' month\n    and exists (\n       "..., packet_length=414) #12 0x00000000013703f7 in do_command (thd=0x3616030) #13 0x000000000133c063 in do_handle_one_connection (thd_arg=0x3616030) at /root/zsl/stonedb/sql/sql_connect.cc:982 #14 0x000000000133bbc6 in handle_one_connection (arg=0x3616030) at /root/zsl/stonedb/sql/sql_connect.cc:898 #15 0x00000000019532c2 in pfs_spawn_thread (arg=0x4fc3cf0) at /root/zsl/stonedb/storage/perfschema/pfs.cc:1860 #16 0x00007fd1b17b4ea5 in start_thread () from /lib64/libpthread.so.0 #17 0x00007fd1b02c0b0d in clone () from /lib64/libc.so.6 

时序流程:

关键函数:

mysql_parse



/*
  When you modify mysql_parse(), you may need to mofify
  mysql_test_parse_for_slave() in this same file.
*/

/**
  Parse a query.

  @param       thd     Current thread
  @param       rawbuf  Begining of the query text
  @param       length  Length of the query text
  @param[out]  found_semicolon For multi queries, position of the character of
                               the next query in the query text.
*/

void mysql_parse(THD *thd, char *rawbuf, uint length,
                 Parser_state *parser_state)
{
  int error __attribute__((unused));
  DBUG_ENTER("mysql_parse");

  DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on(););

  /*
    Warning.
    The purpose of query_cache_send_result_to_client() is to lookup the
    query in the query cache first, to avoid parsing and executing it.
    So, the natural implementation would be to:
    - first, call query_cache_send_result_to_client,
    - second, if caching failed, initialise the lexical and syntactic parser.
    The problem is that the query cache depends on a clean initialization
    of (among others) lex->safe_to_cache_query and thd->server_status,
    which are reset respectively in
    - lex_start()
    - mysql_reset_thd_for_next_command()
    So, initializing the lexical analyser *before* using the query cache
    is required for the cache to work properly.
    FIXME: cleanup the dependencies in the code to simplify this.
  */
  lex_start(thd);
  mysql_reset_thd_for_next_command(thd);

  if (query_cache_send_result_to_client(thd, rawbuf, length) <= 0)
  {
    LEX *lex= thd->lex;

    bool err= parse_sql(thd, parser_state, NULL);

    const char *found_semicolon= parser_state->m_lip.found_semicolon;
    size_t      qlen= found_semicolon
                      ? (found_semicolon - thd->query())
                      : thd->query_length();

    if (!err)
    {
      /*
        See whether we can do any query rewriting. opt_log_raw only controls
        writing to the general log, so rewriting still needs to happen because
        the other logs (binlog, slow query log, ...) can not be set to raw mode
        for security reasons.
        Query-cache only handles SELECT, which we don't rewrite, so it's no
        concern of ours.
        We're not general-logging if we're the slave, or if we've already
        done raw-logging earlier.
        Sub-routines of mysql_rewrite_query() should try to only rewrite when
        necessary (e.g. not do password obfuscation when query contains no
        password), but we can optimize out even those necessary rewrites when
        no logging happens at all. If rewriting does not happen here,
        thd->rewritten_query is still empty from being reset in alloc_query().
      */
      if (!(opt_log_raw || thd->slave_thread) || opt_slow_log || opt_bin_log)
      {
        mysql_rewrite_query(thd);

        if (thd->rewritten_query.length())
          lex->safe_to_cache_query= false; // see comments below
      }

      if (!(opt_log_raw || thd->slave_thread))
      {
        if (thd->rewritten_query.length())
          general_log_write(thd, COM_QUERY, thd->rewritten_query.c_ptr_safe(),
                                            thd->rewritten_query.length());
        else
          general_log_write(thd, COM_QUERY, thd->query(), qlen);
      }
    }

    if (!err)
    {
      thd->m_statement_psi= MYSQL_REFINE_STATEMENT(thd->m_statement_psi,
                                                   sql_statement_info[thd->lex->sql_command].m_key);

#ifndef NO_EMBEDDED_ACCESS_CHECKS
      if (mqh_used && thd->get_user_connect() &&
	  check_mqh(thd, lex->sql_command))
      {
	thd->net.error = 0;
      }
      else
#endif
      {
	if (! thd->is_error())
	{
          /*
            Binlog logs a string starting from thd->query and having length
            thd->query_length; so we set thd->query_length correctly (to not
            log several statements in one event, when we executed only first).
            We set it to not see the ';' (otherwise it would get into binlog
            and Query_log_event::print() would give ';;' output).
            This also helps display only the current query in SHOW
            PROCESSLIST.
            Note that we don't need LOCK_thread_count to modify query_length.
          */
          if (found_semicolon && (ulong) (found_semicolon - thd->query()))
            thd->set_query_inner(thd->query(),
                                 (uint32) (found_semicolon -
                                           thd->query() - 1),
                                 thd->charset());
          /* Actually execute the query */
          if (found_semicolon)
          {
            lex->safe_to_cache_query= 0;
            thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
          }
          lex->set_trg_event_type_for_tables();
          MYSQL_QUERY_EXEC_START(thd->query(),
                                 thd->thread_id,
                                 (char *) (thd->db ? thd->db : ""),
                                 &thd->security_ctx->priv_user[0],
                                 (char *) thd->security_ctx->host_or_ip,
                                 0);
          if (unlikely(thd->security_ctx->password_expired &&
                       !lex->is_change_password &&
                       lex->sql_command != SQLCOM_SET_OPTION))
          {
            my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
            error= 1;
          }
          else
            error= mysql_execute_command(thd);
          if (error == 0 &&
              thd->variables.gtid_next.type == GTID_GROUP &&
              thd->owned_gtid.sidno != 0 &&
              (thd->lex->sql_command == SQLCOM_COMMIT ||
               stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END) ||
               thd->lex->sql_command == SQLCOM_CREATE_TABLE ||
               thd->lex->sql_command == SQLCOM_DROP_TABLE))
          {
            /*
              This ensures that an empty transaction is logged if
              needed. It is executed at the end of an implicitly or
              explicitly committing statement, or after CREATE
              TEMPORARY TABLE or DROP TEMPORARY TABLE.

              CREATE/DROP TEMPORARY do not count as implicitly
              committing according to stmt_causes_implicit_commit(),
              but are written to the binary log as DDL (not between
              BEGIN/COMMIT). Thus we need special cases for these
              statements in the condition above. Hence the clauses for
              for SQLCOM_CREATE_TABLE and SQLCOM_DROP_TABLE above.

              Thus, for base tables, SQLCOM_[CREATE|DROP]_TABLE match
              both the stmt_causes_implicit_commit clause and the
              thd->lex->sql_command == SQLCOM_* clause; for temporary
              tables they match only thd->lex->sql_command ==
              SQLCOM_*.
            */
            error= gtid_empty_group_log_and_cleanup(thd);
          }
          MYSQL_QUERY_EXEC_DONE(error);
	}
      }
    }
    else
    {
      /* Instrument this broken statement as "statement/sql/error" */
      thd->m_statement_psi= MYSQL_REFINE_STATEMENT(thd->m_statement_psi,
                                                   sql_statement_info[SQLCOM_END].m_key);

      DBUG_ASSERT(thd->is_error());
      DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
			 thd->is_fatal_error));

      query_cache_abort(&thd->query_cache_tls);
    }

    THD_STAGE_INFO(thd, stage_freeing_items);
    sp_cache_enforce_limit(thd->sp_proc_cache, stored_program_cache_size);
    sp_cache_enforce_limit(thd->sp_func_cache, stored_program_cache_size);
    thd->end_statement();
    thd->cleanup_after_query();
    DBUG_ASSERT(thd->change_list.is_empty());
  }
  else
  {
    /*
      Query cache hit. We need to write the general log here if
      we haven't already logged the statement earlier due to --log-raw.
      Right now, we only cache SELECT results; if the cache ever
      becomes more generic, we should also cache the rewritten
      query-string together with the original query-string (which
      we'd still use for the matching) when we first execute the
      query, and then use the obfuscated query-string for logging
      here when the query is given again.
    */
    thd->m_statement_psi= MYSQL_REFINE_STATEMENT(thd->m_statement_psi,
                                                 sql_statement_info[SQLCOM_SELECT].m_key);
    if (!opt_log_raw)
      general_log_write(thd, COM_QUERY, thd->query(), thd->query_length());
    parser_state->m_lip.found_semicolon= NULL;
  }

  DBUG_VOID_RETURN;
}

mysql_execute_command:

execute_sqlcom_select:


static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
{
  LEX	*lex= thd->lex;
  select_result *result= lex->result;
  bool res;
  /* assign global limit variable if limit is not given */
  {
    SELECT_LEX *param= lex->unit.global_parameters;
    if (!param->explicit_limit)
      param->select_limit=
        new Item_int((ulonglong) thd->variables.select_limit);
  }

    // FIXME: open_and_lock_tables does not fill derived tables before a call to StoneDB engine
  if (!(res= open_normal_and_derived_tables(thd, all_tables, 0)))
  {
    if (lex->describe)
    {
      /*
        We always use select_send for EXPLAIN, even if it's an EXPLAIN
        for SELECT ... INTO OUTFILE: a user application should be able
        to prepend EXPLAIN to any query and receive output for it,
        even if the query itself redirects the output.
      */
      if (!(result= new select_send()))
        return 1;                               /* purecov: inspected */
      res= explain_query_expression(thd, result);
      delete result;
    }
    else
    {
      if (!result && !(result= new select_send()))
        return 1;                               /* purecov: inspected */
      select_result *save_result= result;
      select_result *analyse_result= NULL;
      if (lex->proc_analyse)
      {
        if ((result= analyse_result=
               new select_analyse(result, lex->proc_analyse)) == NULL)
          return true;
      }
      //res= handle_select(thd, result, 0);

      // StoneDB tries to handle a query
      // if StoneDB leaves the query for MySQL engine
      // then the derived tables are filled and the query gets into the original MySQL execution path
      int sdb_res, free_join_from_sdb, optimize_after_sdb;
      if (stonedb::dbhandler::SDB_HandleSelect(thd, lex, result, (ulong) 0, 
                          sdb_res, optimize_after_sdb, free_join_from_sdb) == 0) {
        res= handle_select(thd, result, 0, optimize_after_sdb, free_join_from_sdb);
      }
      else
        res= sdb_res;

      delete analyse_result;
      if (save_result != lex->result)
        delete save_result;
    }
  }
  DEBUG_SYNC(thd, "after_table_open");
  return res;
}

do_select:



/**
  Make a join of all tables and write it on socket or to table.

  @retval
    0  if ok
  @retval
    1  if error is sent
  @retval
    -1  if error should be sent
*/

static int
do_select(JOIN *join)
{
  int rc= 0;
  enum_nested_loop_state error= NESTED_LOOP_OK;
  DBUG_ENTER("do_select");

  join->send_records=0;
  if (join->plan_is_const() && !join->need_tmp)
  {
    Next_select_func end_select= setup_end_select_func(join, NULL);
    /*
      HAVING will be checked after processing aggregate functions,
      But WHERE should checkd here (we alredy have read tables)

      @todo: consider calling end_select instead of duplicating code
    */
    if (!join->conds || join->conds->val_int())
    {
      // HAVING will be checked by end_select
      error= (*end_select)(join, 0, 0);
      if (error >= NESTED_LOOP_OK)
	error= (*end_select)(join, 0, 1);

      /*
        If we don't go through evaluate_join_record(), do the counting
        here.  join->send_records is increased on success in end_send(),
        so we don't touch it here.
      */
      join->examined_rows++;
      DBUG_ASSERT(join->examined_rows <= 1);
    }
    else if (join->send_row_on_empty_set())
    {
      table_map save_nullinfo= 0;
      /*
        If this is a subquery, we need to save and later restore
        the const table NULL info before clearing the tables
        because the following executions of the subquery do not
        reevaluate constant fields. @see save_const_null_info
        and restore_const_null_info
      */
      if (join->select_lex->master_unit()->item && join->const_tables)
        save_const_null_info(join, &save_nullinfo);

      // Calculate aggregate functions for no rows
      List_iterator_fast it(*join->fields);
      Item *item;
      while ((item= it++))
        item->no_rows_in_result();

      // Mark tables as containing only NULL values
      join->clear();

      if (!join->having || join->having->val_int())
        rc= join->result->send_data(*join->fields);

      if (save_nullinfo)
        restore_const_null_info(join, save_nullinfo);
    }
    /*
      An error can happen when evaluating the conds 
      (the join condition and piece of where clause 
      relevant to this join table).
    */
    if (join->thd->is_error())
      error= NESTED_LOOP_ERROR;
  }
  else
  {
    JOIN_TAB *join_tab= join->join_tab + join->const_tables;
    DBUG_ASSERT(join->primary_tables);
    error= join->first_select(join,join_tab,0);
    if (error >= NESTED_LOOP_OK)
      error= join->first_select(join,join_tab,1);
  }

  join->thd->limit_found_rows= join->send_records;
  /*
    For "order by with limit", we cannot rely on send_records, but need
    to use the rowcount read originally into the join_tab applying the
    filesort. There cannot be any post-filtering conditions, nor any
    following join_tabs in this case, so this rowcount properly represents
    the correct number of qualifying rows.
  */
  if (join->order)
  {
    // Save # of found records prior to cleanup
    JOIN_TAB *sort_tab;
    JOIN_TAB *join_tab= join->join_tab;
    uint const_tables= join->const_tables;

    // Take record count from first non constant table or from last tmp table
    if (join->tmp_tables > 0)
      sort_tab= join_tab + join->primary_tables + join->tmp_tables - 1;
    else
    {
      DBUG_ASSERT(!join->plan_is_const());
      sort_tab= join_tab + const_tables;
    }
    if (sort_tab->filesort &&
        join->select_options & OPTION_FOUND_ROWS &&
        sort_tab->filesort->sortorder &&
        sort_tab->filesort->limit != HA_POS_ERROR)
    {
      join->thd->limit_found_rows= sort_tab->records;
    }
  }

  {
    /*
      The following will unlock all cursors if the command wasn't an
      update command
    */
    join->join_free();			// Unlock all cursors
  }
  if (error == NESTED_LOOP_OK)
  {
    /*
      Sic: this branch works even if rc != 0, e.g. when
      send_data above returns an error.
    */
    if (join->result->send_eof())
      rc= 1;                                  // Don't send error
    DBUG_PRINT("info",("%ld records output", (long) join->send_records));
  }
  else
    rc= -1;
#ifndef DBUG_OFF
  if (rc)
  {
    DBUG_PRINT("error",("Error: do_select() failed"));
  }
#endif
  rc= join->thd->is_error() ? -1 : rc;
  DBUG_RETURN(rc);
}

SDB_HandleSelect:


int SDB_HandleSelect(THD *thd, LEX *lex, select_result *&result, ulong setup_tables_done_option, int &res,
                     int &optimize_after_sdb, int &sdb_free_join, int with_insert) {
  int ret = RCBASE_QUERY_ROUTE;
  try {
    // handle_select_ret is introduced here because in case of some exceptions
    // (e.g. thrown from ForbiddenMySQLQueryPath) we want to return
    // RCBASE_QUERY_ROUTE
    int handle_select_ret = rceng->HandleSelect(thd, lex, result, setup_tables_done_option, res, optimize_after_sdb,
                                                sdb_free_join, with_insert);
    if (handle_select_ret == RETURN_QUERY_TO_MYSQL_ROUTE && AtLeastOneSDBTableInvolved(lex) &&
        ForbiddenMySQLQueryPath(lex)) {
      my_message(static_cast(common::ErrorCode::UNKNOWN_ERROR),
                 "The query includes syntax that is not supported by the storage engine. \
Either restructure the query with supported syntax, or enable the MySQL core::Query Path in config file to execute the query with reduced performance.",
                 MYF(0));
      handle_select_ret = RCBASE_QUERY_ROUTE;
    }
    ret = handle_select_ret;
  } catch (std::exception &e) {
    my_message(static_cast(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
    STONEDB_LOG(LogCtl_Level::ERROR, "HandleSelect Error: %s", e.what());
  } catch (...) {
    my_message(static_cast(common::ErrorCode::UNKNOWN_ERROR), "An unknown system exception error caught.", MYF(0));
    STONEDB_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
  }
  return ret;
}

int SDB_LoadData(THD *thd, sql_exchange *ex, TABLE

HandleSelect:


/*
Handles a single query
If an error appears during query preparation/optimization
query structures are cleaned up and the function returns information about the
error through res'. If the query can not be compiled by StoneDB engine
RETURN_QUERY_TO_MYSQL_ROUTE is returned and MySQL engine continues query
execution.
*/
int Engine::HandleSelect(THD *thd, LEX *lex, select_result *&result, ulong setup_tables_done_option, int &res,
                         int &optimize_after_sdb, int &sdb_free_join, int with_insert) {
  KillTimer timer(thd, stonedb_sysvar_max_execution_time);

  int in_case_of_failure_can_go_to_mysql;

  optimize_after_sdb = FALSE;
  sdb_free_join = 0;

  SELECT_LEX_UNIT *unit = NULL;
  SELECT_LEX *select_lex = NULL;
  select_export *se = NULL;

  thd->variables.engine_condition_pushdown = stonedb_sysvar_pushdown;
  if (!IsSDBRoute(thd, lex->query_tables, &lex->select_lex, in_case_of_failure_can_go_to_mysql, with_insert)) {
    return RETURN_QUERY_TO_MYSQL_ROUTE;
  }

  if (lock_tables(thd, thd->lex->query_tables, thd->lex->table_count, 0)) {
    STONEDB_LOG(LogCtl_Level::ERROR, "Failed to lock tables for query '%s'", thd_query_string(thd)->str);
    return RCBASE_QUERY_ROUTE;
  }
  /*
    Only register query in cache if it tables were locked above.
    Tables must be locked before storing the query in the query cache.
  */
  query_cache_store_query(thd, thd->lex->query_tables);

  stonedb_stat.select++;

  // at this point all tables are in RCBase engine, so we can proceed with the
  // query and we know that if the result goes to the file, the SDB_DATAFORMAT is
  // one of SDB formats
  int route = RCBASE_QUERY_ROUTE;
  SELECT_LEX *save_current_select = lex->current_select;
  List derived_optimized;  // collection to remember derived
                                               // tables that are optimized
  if (thd->fill_derived_tables() && lex->derived_tables) {
    // Derived tables are processed completely in the function
    // open_and_lock_tables(...). To avoid execution of derived tables in
    // open_and_lock_tables(...) the function mysql_derived_filling(..)
    // optimizing and executing derived tables is passed over, then optimization
    // of derived tables must go here.
    res = FALSE;
    int free_join = FALSE;
    lex->thd->derived_tables_processing = TRUE;
    for (SELECT_LEX *sl = lex->all_selects_list; sl; sl = sl->next_select_in_list())        // for all selects
      for (TABLE_LIST *cursor = sl->get_table_list(); cursor; cursor = cursor->next_local)  // for all tables
        if (cursor->table && cursor->derived) {  // data source (view or FROM subselect)
          // optimize derived table
          SELECT_LEX *first_select = cursor->derived->first_select();
          if (first_select->next_select() && first_select->next_select()->linkage == UNION_TYPE) {  //?? only if union
            if (cursor->derived->describe || cursor->derived->item) {  //??called for explain
              // OR there is subselect(?)
              route = RETURN_QUERY_TO_MYSQL_ROUTE;
              goto ret_derived;
            }
            if (!cursor->derived->executed || cursor->derived->uncacheable) {  //??not already executed (not
                                                                               // materialized?)
              // OR not cacheable (meaning not yet in cache, i.e. not
              // materialized it seems to boil down to NOT MATERIALIZED(?)
              res = cursor->derived->optimize_for_stonedb();  //===exec()
              derived_optimized.push_back(cursor->derived);
            }
          } else {  //??not union
            cursor->derived->set_limit(first_select);
            if (cursor->derived->select_limit_cnt == HA_POS_ERROR) first_select->options &= ~OPTION_FOUND_ROWS;
            lex->current_select = first_select;
            int optimize_derived_after_sdb = FALSE;
            res = optimize_select(
                thd, (TABLE_LIST *)first_select->table_list.first, first_select->with_wild, first_select->item_list,
                first_select->where, (first_select->order_list.elements + first_select->group_list.elements),
                (ORDER *)first_select->order_list.first, (ORDER *)first_select->group_list.first, first_select->having,
                ulong(first_select->options | thd->variables.option_bits | SELECT_NO_UNLOCK), cursor->derived_result,
                cursor->derived, first_select, optimize_derived_after_sdb, free_join);
            if (optimize_derived_after_sdb) derived_optimized.push_back(cursor->derived);
          }
          lex->current_select = save_current_select;
          if (!res && free_join)  // no error &
            route = RETURN_QUERY_TO_MYSQL_ROUTE;
          if (res || route == RETURN_QUERY_TO_MYSQL_ROUTE) goto ret_derived;
        }
    lex->thd->derived_tables_processing = FALSE;
  }

  se = dynamic_cast(result);
  if (se != NULL) result = new exporter::select_sdb_export(se);
  // prepare, optimize and execute the main query
  select_lex = &lex->select_lex;
  unit = &lex->unit;
  if (select_lex->next_select()) {  // it is union
    if (!(res = unit->prepare(thd, result, SELECT_NO_UNLOCK | setup_tables_done_option))) {
      // similar to mysql_union(...) from sql_union.cpp

      /* FIXME: create_table is private in mysql5.6
         select_create* sc = dynamic_cast(result);
         if (sc && sc->create_table->table && sc->create_table->table->db_stat
         != 0) { my_error(ER_TABLE_EXISTS_ERROR, MYF(0),
         sc->create_table->table_name); res = 1; } else
       */
      if (unit->describe || unit->item)  // explain or sth was already computed - go to mysql
        route = RETURN_QUERY_TO_MYSQL_ROUTE;
      else {
        int old_executed = unit->executed;
        res = unit->optimize_for_stonedb();  //====exec()
        optimize_after_sdb = TRUE;
        if (!res) {
          try {
            route = rceng->Execute(unit->thd, unit->thd->lex, result, unit);
            if (route == RETURN_QUERY_TO_MYSQL_ROUTE) {
              if (in_case_of_failure_can_go_to_mysql)
                unit->executed = old_executed;
              else {
                const char *err_msg =
                    "Error: Query syntax not implemented in StoneDB, can "
                    "export "
                    "only to MySQL format (set SDB_DATAFORMAT to 'MYSQL').";
                STONEDB_LOG(LogCtl_Level::ERROR, err_msg);
                my_message(ER_SYNTAX_ERROR, err_msg, MYF(0));
                throw ReturnMeToMySQLWithError();
              }
            }
          } catch (ReturnMeToMySQLWithError &) {
            route = RCBASE_QUERY_ROUTE;
            res = TRUE;
          }
        }
      }
    }
    if (res || route == RCBASE_QUERY_ROUTE) {
      res |= (int)unit->cleanup();
      optimize_after_sdb = FALSE;
    }
  } else {
    unit->set_limit(unit->global_parameters);  // the fragment of original
                                               // handle_select(...)
    //(until the first part of optimization)
    // used for non-union select

    //'options' of mysql_select will be set in JOIN, as far as JOIN for
    // every PS/SP execution new, we will not need reset this flag if
    // setup_tables_done_option changed for next rexecution

    int err;
    err = optimize_select(thd, (TABLE_LIST *)select_lex->table_list.first, select_lex->with_wild, select_lex->item_list,
                          select_lex->where, select_lex->order_list.elements + select_lex->group_list.elements,
                          (ORDER *)select_lex->order_list.first, (ORDER *)select_lex->group_list.first,
                          select_lex->having,
                          ulong(select_lex->options | thd->variables.option_bits | setup_tables_done_option), result,
                          unit, select_lex, optimize_after_sdb, sdb_free_join);
    // RCBase query engine entry point
    if (!err) {
      try {
        route = Execute(thd, lex, result);
        if (route == RETURN_QUERY_TO_MYSQL_ROUTE && !in_case_of_failure_can_go_to_mysql) {
          STONEDB_LOG(LogCtl_Level::ERROR,
                      "Error: Query syntax not implemented in StoneDB, can export "
                      "only to MySQL format (set SDB_DATAFORMAT to 'MYSQL').");
          my_message(ER_SYNTAX_ERROR,
                     "Query syntax not implemented in StoneDB, can export only "
                     "to MySQL "
                     "format (set SDB_DATAFORMAT to 'MYSQL').",
                     MYF(0));
          throw ReturnMeToMySQLWithError();
        }
      } catch (ReturnMeToMySQLWithError &) {
        route = RCBASE_QUERY_ROUTE;
        err = TRUE;
      }
    }
    if (sdb_free_join) {  // there was a join created in an upper function
      // so an upper function will do the cleanup
      if (err || route == RCBASE_QUERY_ROUTE) {
        thd->proc_info = "end";
        err |= (int)select_lex->cleanup();
        optimize_after_sdb = FALSE;
        sdb_free_join = 0;
      }
      res = (err || thd->is_error());
    } else
      res = select_lex->join->error;
  }
  if (select_lex->join && Query::IsLOJ(select_lex->join->join_list))
    optimize_after_sdb = 2;     // optimize partially (part=4), since part of LOJ
                                // optimization was already done
  res |= (int)thd->is_error();  // the ending of original handle_select(...) */
  if (unlikely(res)) {
    // If we had a another error reported earlier then this will be ignored //
    result->send_error(ER_UNKNOWN_ERROR, ER(ER_UNKNOWN_ERROR));
    result->abort_result_set();
  }
  if (se != NULL) {
    // free the sdb export object,
    // restore the original mysql export object
    // and prepare if it is expected to be prepared
    if (!select_lex->next_select() && select_lex->join != 0 && select_lex->join->result == result) {
      select_lex->join->result = se;
      if (((exporter::select_sdb_export *)result)->IsPrepared()) se->prepare(select_lex->join->fields_list, unit);
    }
    delete result;
    result = se;
  }
ret_derived:
  // if the query is redirected to MySQL engine
  // optimization of derived tables must be completed
  // and derived tables must be filled
  if (route == RETURN_QUERY_TO_MYSQL_ROUTE) {
    for (SELECT_LEX *sl = lex->all_selects_list; sl; sl = sl->next_select_in_list())
      for (TABLE_LIST *cursor = sl->get_table_list(); cursor; cursor = cursor->next_local)
        if (cursor->table && cursor->derived) {
          lex->thd->derived_tables_processing = TRUE;
          cursor->derived->optimize_after_stonedb();
        }
    lex->current_select = save_current_select;
  }
  lex->thd->derived_tables_processing = FALSE;

  return route;
}

Execute:

int Engine::Execute(THD *thd, LEX *lex, select_result *result_output, SELECT_LEX_UNIT *unit_for_union) {
  DEBUG_ASSERT(thd->lex == lex);
  SELECT_LEX *selects_list = &lex->select_lex;
  SELECT_LEX *last_distinct = NULL;
  if (unit_for_union != NULL) last_distinct = unit_for_union->union_distinct;

  int is_dumpfile = 0;
  const char *export_file_name = GetFilename(selects_list, is_dumpfile);
  if (is_dumpfile) {
    push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR,
                 "Dumpfile not implemented in StoneDB, executed by MySQL engine.");
    return RETURN_QUERY_TO_MYSQL_ROUTE;
  }

  Query query(current_tx);
  CompiledQuery cqu;

  current_tx->ResetDisplay();  // switch display on
  query.SetRoughQuery(selects_list->options & SELECT_ROUGHLY);

  try {
    if (!query.Compile(&cqu, selects_list, last_distinct)) {
      push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR,
                   "Query syntax not implemented in StoneDB, executed by MySQL engine.");
      return RETURN_QUERY_TO_MYSQL_ROUTE;
    }
  } catch (common::Exception const &x) {
    STONEDB_LOG(LogCtl_Level::ERROR, "Query Compile Error: %s", x.what());
    my_message(ER_UNKNOWN_ERROR, (std::string("StoneDB compile specific error: ") + x.what()).c_str(), MYF(0));
    throw ReturnMeToMySQLWithError();
  }

  std::unique_ptr sender;

  FunctionExecutor lock_and_unlock_pack_info(std::bind(&Query::LockPackInfoForUse, &query),
                                             std::bind(&Query::UnlockPackInfoFromUse, &query));

  try {
    std::shared_ptr rct;
    if (lex->sql_command == SQLCOM_INSERT_SELECT &&
        Engine::IsSDBTable(((Query_tables_list *)lex)->query_tables->table)) {
      std::string table_path = Engine::GetTablePath(((Query_tables_list *)lex)->query_tables->table);
      rct = current_tx->GetTableByPathIfExists(table_path);
    }
    if (unit_for_union != NULL) {
      int res = result_output->prepare(unit_for_union->item_list, unit_for_union);
      if (res) {
        STONEDB_LOG(LogCtl_Level::ERROR, "Error: Unsupported UNION");
        my_message(ER_UNKNOWN_ERROR, "StoneDB: unsupported UNION", MYF(0));
        throw ReturnMeToMySQLWithError();
      }
      if (export_file_name)
        sender.reset(new ResultExportSender(unit_for_union->thd, result_output, unit_for_union->item_list));
      else
        sender.reset(new ResultSender(unit_for_union->thd, result_output, unit_for_union->item_list));
    } else {
      if (export_file_name)
        sender.reset(new ResultExportSender(selects_list->join->thd, result_output, selects_list->item_list));
      else
        sender.reset(new ResultSender(selects_list->join->thd, result_output, selects_list->item_list));
    }

    TempTable *result = query.Preexecute(cqu, sender.get());
    ASSERT(result != NULL, "Query execution returned no result object");
    if (query.IsRoughQuery())
      result->RoughMaterialize(false, sender.get());
    else
      result->Materialize(false, sender.get());

    sender->Finalize(result);

    if (rct) {
      // in this case if this is an insert to RCTable from select based on the
      // same table RCTable object for this table can't be deleted in TempTable
      // destructor It will be deleted in RefreshTables method that will be
      // called on commit
      result->RemoveFromManagedList(rct.get());
      query.RemoveFromManagedList(rct);
      rct.reset();
    }
    sender.reset();
  } catch (...) {
    bool with_error = false;
    if (sender) {
      if (sender->SentRows() > 0) {
        sender->CleanUp();
        with_error = true;
      } else
        sender->CleanUp();
    }
    return (handle_exceptions(thd, current_tx, with_error));
  }
  return RCBASE_QUERY_ROUTE;
}

Preexecute:


TempTable *Query::Preexecute(CompiledQuery &qu, ResultSender *sender, [[maybe_unused]] bool display_now) {
  if (STONEDB_LOGCHECK(LogCtl_Level::DEBUG)) {
    qu.Print(this);
  }
  std::vector conds(qu.NoConds());

  TempTable *output_table = NULL;  // NOTE: this pointer will be returned by the function

  ta.resize(qu.NoTabs());
  auto global_limits = qu.GetGlobalLimit();

  cq = &qu;
  // Execution itself
  for (int i = 0; i < qu.NoSteps(); i++) {
    CompiledQuery::CQStep step = qu.Step(i);
    std::shared_ptr t1_ptr, t2_ptr, t3_ptr;

    if (step.t1.n != common::NULL_VALUE_32) {
      if (step.t1.n >= 0)
        t1_ptr = Table(step.t1.n);  // normal table
      else {
        t1_ptr = ta[-step.t1.n - 1];  // TempTable
      }
    }
    if (step.t2.n != common::NULL_VALUE_32) {
      if (step.t2.n >= 0)
        t2_ptr = Table(step.t2.n);  // normal table
      else {
        t2_ptr = ta[-step.t2.n - 1];  // TempTable
      }
    }
    if (step.t3.n != common::NULL_VALUE_32) {
      if (step.t3.n >= 0)
        t3_ptr = Table(step.t3.n);  // normal table
      else {
        t3_ptr = ta[-step.t3.n - 1];  // TempTable
      }
    }
    // Some technical information
    if (step.alias && std::strcmp(step.alias, "roughstats") == 0) {
      // magical word (passed as table alias) to display statistics
      ((TempTable *)ta[-step.t1.n - 1].get())->DisplayRSI();
    }

    if (step.alias && std::strcmp(step.alias, "roughattrstats") == 0) {
      // magical word (passed as table alias) to display attr. statistics
      m_conn->SetDisplayAttrStats();
    }

    // Implementation of steps
    try {
      switch (step.type) {
        case CompiledQuery::StepType::TABLE_ALIAS:
          ta[-step.t1.n - 1] = t2_ptr;
          break;
        case CompiledQuery::StepType::TMP_TABLE:
          DEBUG_ASSERT(step.t1.n < 0);
          ta[-step.t1.n - 1] = step.n1
                                   ? TempTable::Create(ta[-step.tables1[0].n - 1].get(), step.tables1[0].n, this, true)
                                   : TempTable::Create(ta[-step.tables1[0].n - 1].get(), step.tables1[0].n, this);
          ((TempTable *)ta[-step.t1.n - 1].get())->ReserveVirtColumns(qu.NoVirtualColumns(step.t1));
          break;
        case CompiledQuery::StepType::CREATE_CONDS:
          DEBUG_ASSERT(step.t1.n < 0);
          step.e1.vc = (step.e1.vc_id != common::NULL_VALUE_32)
                           ? ((TempTable *)ta[-step.t1.n - 1].get())->GetVirtualColumn(step.e1.vc_id)
                           : NULL;
          step.e2.vc = (step.e2.vc_id != common::NULL_VALUE_32)
                           ? ((TempTable *)ta[-step.t1.n - 1].get())->GetVirtualColumn(step.e2.vc_id)
                           : NULL;
          step.e3.vc = (step.e3.vc_id != common::NULL_VALUE_32)
                           ? ((TempTable *)ta[-step.t1.n - 1].get())->GetVirtualColumn(step.e3.vc_id)
                           : NULL;
          if (step.n1 != static_cast(CondType::OR_SUBTREE)) {  // on result = false
            conds[step.c1.n] = new Condition();
            if (step.c2.IsNull()) {
              conds[step.c1.n]->AddDescriptor(
                  step.e1, step.op, step.e2, step.e3, (TempTable *)ta[-step.t1.n - 1].get(), qu.GetNoDims(step.t1),
                  (step.op == common::Operator::O_LIKE || step.op == common::Operator::O_NOT_LIKE) ? char(step.n2)
                                                                                                   : '\\');
            } else {
              DEBUG_ASSERT(conds[step.c2.n]->IsType_Tree());
              conds[step.c1.n]->AddDescriptor(static_cast(conds[step.c2.n])->GetTree(),
                                              (TempTable *)ta[-step.t1.n - 1].get(), qu.GetNoDims(step.t1));
            }
          } else {  // on result = true
            if (step.c2.IsNull())
              conds[step.c1.n] =
                  new SingleTreeCondition(step.e1, step.op, step.e2, step.e3, (TempTable *)ta[-step.t1.n - 1].get(),
                                          qu.GetNoDims(step.t1), char(step.n2));
            else {
              DEBUG_ASSERT(conds[step.c2.n]->IsType_Tree());
              conds[step.c1.n] = new Condition();
              conds[step.c1.n]->AddDescriptor(((SingleTreeCondition *)conds[step.c2.n])->GetTree(),
                                              (TempTable *)ta[-step.t1.n - 1].get(), qu.GetNoDims(step.t1));
            }
          }
          break;
        case CompiledQuery::StepType::AND_F:
        case CompiledQuery::StepType::OR_F:
          if (!conds[step.c2.n]->IsType_Tree()) {
            ASSERT(step.type == CompiledQuery::StepType::AND_F);
            auto cond2 = conds[step.c2.n];
            for (size_t i = 0; i < cond2->Size(); i++) {
              auto &desc = (*cond2)[i];
              if (conds[step.c1.n]->IsType_Tree()) {
                TempTable *temptb = (TempTable *)ta[-qu.GetTableOfCond(step.c2).n - 1].get();
                int no_dims = qu.GetNoDims(qu.GetTableOfCond(step.c2));
                if (desc.op == common::Operator::O_OR_TREE) {
                  static_cast(conds[step.c1.n])
                      ->AddTree(common::LogicalOperator::O_AND, desc.tree, no_dims);
                } else {
                  static_cast(conds[step.c1.n])
                      ->AddDescriptor(common::LogicalOperator::O_AND, desc.attr, desc.op, desc.val1, desc.val2, temptb,
                                      no_dims, desc.like_esc);
                }
              } else {
                conds[step.c1.n]->AddDescriptor(desc);
              }
            }
          } else if (conds[step.c1.n]->IsType_Tree()) {  // on result = false
            DEBUG_ASSERT(conds[step.c2.n]->IsType_Tree());
            common::LogicalOperator lop = (step.type == CompiledQuery::StepType::AND_F ? common::LogicalOperator::O_AND
                                                                                       : common::LogicalOperator::O_OR);
            static_cast(conds[step.c1.n])
                ->AddTree(lop, static_cast(conds[step.c2.n])->GetTree(), qu.GetNoDims(step.t1));
          } else {
            DEBUG_ASSERT(conds[step.c2.n]->IsType_Tree());
            conds[step.c1.n]->AddDescriptor(static_cast(conds[step.c2.n])->GetTree(),
                                            (TempTable *)ta[-qu.GetTableOfCond(step.c1).n - 1].get(),
                                            qu.GetNoDims(qu.GetTableOfCond(step.c1)));
          }
          break;
        case CompiledQuery::StepType::OR_DESC:
        case CompiledQuery::StepType::AND_DESC: {
          common::LogicalOperator lop =
              (step.type == CompiledQuery::StepType::AND_DESC ? common::LogicalOperator::O_AND
                                                              : common::LogicalOperator::O_OR);
          step.e1.vc = (step.e1.vc_id != common::NULL_VALUE_32)
                           ? ((TempTable *)ta[-step.t1.n - 1].get())->GetVirtualColumn(step.e1.vc_id)
                           : NULL;
          step.e2.vc = (step.e2.vc_id != common::NULL_VALUE_32)
                           ? ((TempTable *)ta[-step.t1.n - 1].get())->GetVirtualColumn(step.e2.vc_id)
                           : NULL;
          step.e3.vc = (step.e3.vc_id != common::NULL_VALUE_32)
                           ? ((TempTable *)ta[-step.t1.n - 1].get())->GetVirtualColumn(step.e3.vc_id)
                           : NULL;
          if (!conds[step.c1.n]->IsType_Tree()) {
            DEBUG_ASSERT(conds[step.c1.n]);
            conds[step.c1.n]->AddDescriptor(
                step.e1, step.op, step.e2, step.e3, (TempTable *)ta[-step.t1.n - 1].get(), qu.GetNoDims(step.t1),
                (step.op == common::Operator::O_LIKE || step.op == common::Operator::O_NOT_LIKE) ? char(step.n2)
                                                                                                 : '\\');
          } else
            static_cast(conds[step.c1.n])
                ->AddDescriptor(lop, step.e1, step.op, step.e2, step.e3, (TempTable *)ta[-step.t1.n - 1].get(),
                                qu.GetNoDims(step.t1),
                                (step.op == common::Operator::O_LIKE || step.op == common::Operator::O_NOT_LIKE)
                                    ? char(step.n2)
                                    : '\\');
          break;
        }
        case CompiledQuery::StepType::T_MODE:
          DEBUG_ASSERT(step.t1.n < 0 && ta[-step.t1.n - 1]->TableType() == TType::TEMP_TABLE);
          ((TempTable *)ta[-step.t1.n - 1].get())->SetMode(step.tmpar, step.n1, step.n2);
          break;
        case CompiledQuery::StepType::JOIN_T:
          DEBUG_ASSERT(step.t1.n < 0 && ta[-step.t1.n - 1]->TableType() == TType::TEMP_TABLE);
          ((TempTable *)ta[-step.t1.n - 1].get())->JoinT(t2_ptr.get(), step.t2.n, step.jt);
          break;
        case CompiledQuery::StepType::ADD_CONDS: {
          DEBUG_ASSERT(step.t1.n < 0 && ta[-step.t1.n - 1]->TableType() == TType::TEMP_TABLE);
          if (step.c1.n == common::NULL_VALUE_32) break;
          if (step.n1 != static_cast(CondType::HAVING_COND)) conds[step.c1.n]->Simplify();
          ((TempTable *)ta[-step.t1.n - 1].get())->AddConds(conds[step.c1.n], (CondType)step.n1);
          break;
        }
        case CompiledQuery::StepType::LEFT_JOIN_ON: {
          DEBUG_ASSERT(step.t1.n < 0 && ta[-step.t1.n - 1]->TableType() == TType::TEMP_TABLE);
          if (step.c1.n == common::NULL_VALUE_32) break;
          ((TempTable *)ta[-step.t1.n - 1].get())->AddLeftConds(conds[step.c1.n], step.tables1, step.tables2);
          break;
        }
        case CompiledQuery::StepType::INNER_JOIN_ON: {
          DEBUG_ASSERT(step.t1.n < 0 && ta[-step.t1.n - 1]->TableType() == TType::TEMP_TABLE);
          if (step.c1.n == common::NULL_VALUE_32) break;
          ((TempTable *)ta[-step.t1.n - 1].get())->AddInnerConds(conds[step.c1.n], step.tables1);
          break;
        }
        case CompiledQuery::StepType::APPLY_CONDS: {
          int64_t cur_limit = -1;
          if (qu.FindDistinct(step.t1.n))
            ((TempTable *)ta[-step.t1.n - 1].get())->SetMode(TMParameter::TM_DISTINCT, 0, 0);
          if (qu.NoAggregationOrderingAndDistinct(step.t1.n)) cur_limit = qu.FindLimit(step.t1.n);

          if (cur_limit != -1 && ((TempTable *)ta[-step.t1.n - 1].get())->GetFilterP()->NoParameterizedDescs())
            cur_limit = -1;

          ParameterizedFilter *filter = ((TempTable *)ta[-step.t1.n - 1].get())->GetFilterP();
          std::set used_dims = qu.GetUsedDims(step.t1, ta);

          // no need any more to check WHERE for not used dims
          bool is_simple_filter = true;  // qu.IsSimpleFilter(step.c1);
          if (used_dims.size() == 1 && used_dims.find(common::NULL_VALUE_32) != used_dims.end())
            is_simple_filter = false;
          for (int i = 0; i < filter->mind->NoDimensions(); i++) {
            if (used_dims.find(i) == used_dims.end() && is_simple_filter)
              filter->mind->ResetUsedInOutput(i);
            else
              filter->mind->SetUsedInOutput(i);
          }

          if (IsRoughQuery()) {
            ((TempTable *)ta[-step.t1.n - 1].get())->GetFilterP()->RoughUpdateParamFilter();
          } else
            ((TempTable *)ta[-step.t1.n - 1].get())
                ->GetFilterP()
                ->UpdateMultiIndex(qu.CountColumnOnly(step.t1), cur_limit);
          break;
        }
        case CompiledQuery::StepType::ADD_COLUMN: {
          DEBUG_ASSERT(step.t1.n < 0 && ta[-step.t1.n - 1]->TableType() == TType::TEMP_TABLE);
          CQTerm e(step.e1);
          if (e.vc_id != common::NULL_VALUE_32)
            e.vc =
                ((TempTable *)ta[-step.t1.n - 1].get())->GetVirtualColumn(step.e1.vc_id);  // vc must have been created
          step.a1.n = ((TempTable *)ta[-step.t1.n - 1].get())
                          ->AddColumn(e, step.cop, step.alias, step.n1 ? true : false, step.si);
          break;
        }
        case CompiledQuery::StepType::CREATE_VC: {
          DEBUG_ASSERT(step.t1.n < 0 && ta[-step.t1.n - 1]->TableType() == TType::TEMP_TABLE);
          TempTable *t = (TempTable *)ta[-step.t1.n - 1].get();

          DEBUG_ASSERT(t);
          if (step.mysql_expr.size() > 0) {
            // vcolumn::VirtualColumn for Expression
            DEBUG_ASSERT(step.mysql_expr.size() == 1);
            MultiIndex *mind = (step.t2.n == step.t1.n) ? t->GetOutputMultiIndexP() : t->GetMultiIndexP();
            int c = ((TempTable *)ta[-step.t1.n - 1].get())
                        ->AddVirtColumn(CreateColumnFromExpression(step.mysql_expr, t, step.t1.n, mind), step.a1.n);
            ASSERT(c == step.a1.n, "AddVirtColumn failed");
          } else if (step.virt_cols.size() > 0) {
            // vcolumn::VirtualColumn for IN
            ColumnType ct;
            if (step.a2.n != common::NULL_VALUE_32)
              ct = ((TempTable *)ta[-step.t1.n - 1].get())->GetVirtualColumn(step.a2.n)->Type();
            std::vector vcs;
            for (uint i = 0; i < step.virt_cols.size(); i++)
              vcs.push_back(((TempTable *)ta[-step.t1.n - 1].get())->GetVirtualColumn(step.virt_cols[i]));
            int c = ((TempTable *)ta[-step.t1.n - 1].get())
                        ->AddVirtColumn(new vcolumn::InSetColumn(ct, t->GetMultiIndexP(), vcs), step.a1.n);
            ASSERT(c == step.a1.n, "AddVirtColumn failed");
          } else if (step.a2.n != common::NULL_VALUE_32) {
            // vcolumn::VirtualColumn for PhysicalColumn
            JustATable *t_src = ta[-step.t2.n - 1].get();
            PhysicalColumn *phc;
            MultiIndex *mind = (step.t2.n == step.t1.n) ? t->GetOutputMultiIndexP() : t->GetMultiIndexP();
            int dim = (step.t2.n == step.t1.n) ? 0 : t->GetDimension(step.t2);
            phc = (PhysicalColumn *)t_src->GetColumn(step.a2.n >= 0 ? step.a2.n : -step.a2.n - 1);
            int c = ((TempTable *)ta[-step.t1.n - 1].get())
                        ->AddVirtColumn(
                            new vcolumn::SingleColumn(phc, mind, step.t2.n, step.a2.n, ta[-step.t2.n - 1].get(), dim),
                            step.a1.n);
            ASSERT(c == step.a1.n, "AddVirtColumn failed");
          } else {
            // vcolumn::VirtualColumn for Subquery
            DEBUG_ASSERT(ta[-step.t2.n - 1]->TableType() == TType::TEMP_TABLE);
            int c =
                ((TempTable *)ta[-step.t1.n - 1].get())
                    ->AddVirtColumn(new vcolumn::SubSelectColumn(
                                        dynamic_cast(ta[-step.t2.n - 1].get()),
                                        step.n1 == 1 ? t->GetOutputMultiIndexP() : t->GetMultiIndexP(), t, step.t1.n),
                                    step.a1.n);
            ASSERT(c == step.a1.n, "AddVirtColumn failed");
          }
          break;
        }
        case CompiledQuery::StepType::ADD_ORDER: {
          DEBUG_ASSERT(step.t1.n < 0 && ta[-step.t1.n - 1]->TableType() == TType::TEMP_TABLE && step.n1 >= 0 &&
                       step.n1 < 2);
          DEBUG_ASSERT(step.a1.n >= 0 && step.a1.n < qu.NoVirtualColumns(step.t1));
          TempTable *loc_t = (TempTable *)ta[-step.t1.n - 1].get();
          loc_t->AddOrder(loc_t->GetVirtualColumn(step.a1.n),
                          (int)step.n1);  // step.n1 = 0 for asc, 1 for desc
          break;
        }
        case CompiledQuery::StepType::UNION:
          DEBUG_ASSERT(step.t1.n < 0 && step.t2.n < 0 && step.t3.n < 0);
          DEBUG_ASSERT(ta[-step.t2.n - 1]->TableType() == TType::TEMP_TABLE &&
                       (step.t3.n == common::NULL_VALUE_32 || ta[-step.t3.n - 1]->TableType() == TType::TEMP_TABLE));
          if (step.t1.n != step.t2.n)
            ta[-step.t1.n - 1] = TempTable::Create(*(TempTable *)ta[-step.t2.n - 1].get(), false);
          if (IsRoughQuery()) {
            if (step.t3.n == common::NULL_VALUE_32)
              ((TempTable *)ta[-step.t1.n - 1].get())->RoughUnion(NULL, qu.IsResultTable(step.t1) ? sender : NULL);
            else
              ((TempTable *)ta[-step.t1.n - 1].get())
                  ->RoughUnion((TempTable *)ta[-step.t3.n - 1].get(), qu.IsResultTable(step.t1) ? sender : NULL);
          } else if (qu.IsResultTable(step.t1) && !qu.IsOrderedBy(step.t1) && step.n1)
            ((TempTable *)ta[-step.t1.n - 1].get())
                ->Union((TempTable *)ta[-step.t3.n - 1].get(), (int)step.n1, sender, global_limits.first,
                        global_limits.second);
          else {
            if (step.t3.n == common::NULL_VALUE_32)
              ((TempTable *)ta[-step.t1.n - 1].get())->Union(NULL, (int)step.n1);
            else {
              ((TempTable *)ta[-step.t1.n - 1].get())->Union((TempTable *)ta[-step.t3.n - 1].get(), (int)step.n1);
              ta[-step.t3.n - 1].reset();
            }
          }
          break;
        case CompiledQuery::StepType::RESULT:
          DEBUG_ASSERT(step.t1.n < 0 && static_cast(-step.t1.n - 1) < ta.size() &&
                       ta[-step.t1.n - 1]->TableType() == TType::TEMP_TABLE);
          output_table = (TempTable *)ta[-step.t1.n - 1].get();
          break;
        case CompiledQuery::StepType::STEP_ERROR:
          rccontrol.lock(m_conn->GetThreadID()) << "ERROR in step " << step.alias << system::unlock;
          break;
        default:
          rccontrol.lock(m_conn->GetThreadID())
              << "ERROR: unsupported type of CQStep (" << static_cast(step.type) << ")" << system::unlock;
      }
    } catch (...) {
      for (auto &c : conds) delete c;
      throw;
    }
  }

  for (auto &c : conds) delete c;

  // NOTE: output_table is sent out of this function and should be managed
  // elsewhere. before deleting all TempTables but output_table those have to be
  // detected there are used by output_table

  return output_table;
}

UpdateMultiIndex:


void ParameterizedFilter::UpdateMultiIndex(bool count_only, int64_t limit) {
  MEASURE_FET("ParameterizedFilter::UpdateMultiIndex(...)");

  thd_proc_info(mind->ConnInfo().Thd(), "update multi-index");

  if (descriptors.Size() < 1) {
    PrepareRoughMultiIndex();
    rough_mind->ClearLocalDescFilters();
    return;
  }
  SyntacticalDescriptorListPreprocessing();

  bool empty_cannot_grow = true;  // if false (e.g. outer joins), then do not
                                  // optimize empty multiindex as empty result

  for (uint i = 0; i < descriptors.Size(); i++)
    if (descriptors[i].IsOuter()) empty_cannot_grow = false;

  // special cases
  bool nonempty = true;

  DescriptorListOrdering();

  // descriptor display
  if (rccontrol.isOn()) {
    rccontrol.lock(mind->m_conn->GetThreadID()) << "Initial execution plan (non-join):" << system::unlock;
    for (uint i = 0; i < descriptors.Size(); i++)
      if (!descriptors[i].done && !descriptors[i].IsType_Join() && descriptors[i].IsInner()) {
        char buf[1000];
        std::strcpy(buf, " ");
        descriptors[i].ToString(buf, 1000);
        if (descriptors[i].IsDelayed())
          rccontrol.lock(mind->m_conn->GetThreadID())
              << "Delayed: " << buf << " \t(" << int(descriptors[i].evaluation * 100) / 100.0 << ")" << system::unlock;
        else
          rccontrol.lock(mind->m_conn->GetThreadID())
              << "Cnd(" << i << "):  " << buf << " \t(" << int(descriptors[i].evaluation * 100) / 100.0 << ")"
              << system::unlock;
      }
  }

  // end now if the multiindex is empty
  if (mind->ZeroTuples() && empty_cannot_grow) {
    mind->Empty();
    PrepareRoughMultiIndex();
    rough_mind->ClearLocalDescFilters();
    return;
  }

  // Prepare execution - rough set part
  for (uint i = 0; i < descriptors.Size(); i++) {
    if (!descriptors[i].done && descriptors[i].IsInner()) {
      if (descriptors[i].IsTrue()) {
        descriptors[i].done = true;

        continue;
      } else if (descriptors[i].IsFalse()) {
        if (descriptors[i].attr.vc) {
          descriptors[i].done = true;
          if (empty_cannot_grow) {
            mind->Empty();
            PrepareRoughMultiIndex();
            rough_mind->ClearLocalDescFilters();
            return;
          } else {
            DimensionVector dims(mind->NoDimensions());
            descriptors[i].attr.vc->MarkUsedDims(dims);
            mind->MakeCountOnly(0, dims);
          }
        }
      }
    }
  }
  if (rough_mind) rough_mind->ClearLocalDescFilters();
  PrepareRoughMultiIndex();
  nonempty = RoughUpdateMultiIndex();  // calculate all rough conditions,

  if ((!nonempty && empty_cannot_grow) || mind->m_conn->Explain()) {
    mind->Empty();  // nonempty==false if the whole result is empty (outer joins
                    // considered)
    rough_mind->ClearLocalDescFilters();
    return;
  }
  PropagateRoughToMind();  // exclude common::RSValue::RS_NONE from mind

  // count other types of conditions, e.g. joins (i.e. conditions using
  // attributes from two
  // dimensions)
  int no_of_join_conditions = 0;  // count also one-dimensional outer join conditions
  int no_of_delayed_conditions = 0;
  for (uint i = 0; i < descriptors.Size(); i++) {
    if (!descriptors[i].done)
      if (descriptors[i].IsType_Join() || descriptors[i].IsDelayed() || descriptors[i].IsOuter()) {
        if (!descriptors[i].IsDelayed())
          no_of_join_conditions++;
        else
          no_of_delayed_conditions++;
      }
  }

  // Apply all one-dimensional filters (after where, i.e. without
  // outer joins)
  int last_desc_dim = -1;
  int cur_dim = -1;

  int no_desc = 0;
  for (uint i = 0; i < descriptors.Size(); i++)
    if (!descriptors[i].done && descriptors[i].IsInner() && !descriptors[i].IsType_Join() &&
        !descriptors[i].IsDelayed())
      ++no_desc;

  int desc_no = 0;
  for (uint i = 0; i < descriptors.Size(); i++) {
    if (!descriptors[i].done && descriptors[i].IsInner() && !descriptors[i].IsType_Join() &&
        !descriptors[i].IsDelayed()) {
      ++desc_no;
      if (descriptors[i].attr.vc) {
        cur_dim = descriptors[i].attr.vc->GetDim();
      }
      if (last_desc_dim != -1 && cur_dim != -1 && last_desc_dim != cur_dim) {
        // Make all possible projections to other dimensions
        RoughMakeProjections(cur_dim, false);
      }

      // limit should be applied only for the last descriptor
      ApplyDescriptor(i, (desc_no != no_desc || no_of_delayed_conditions > 0 || no_of_join_conditions) ? -1 : limit);
      if (!descriptors[i].attr.vc) continue;  // probably desc got simplified and is true or false
      if (cur_dim >= 0 && mind->GetFilter(cur_dim) && mind->GetFilter(cur_dim)->IsEmpty() && empty_cannot_grow) {
        mind->Empty();
        if (rccontrol.isOn()) {
          rccontrol.lock(mind->m_conn->GetThreadID())
              << "Empty result set after non-join condition evaluation (WHERE)" << system::unlock;
        }
        rough_mind->ClearLocalDescFilters();
        return;
      }
      last_desc_dim = cur_dim;
    }
  }
  rough_mind->UpdateReducedDimension();
  mind->UpdateNoTuples();
  for (int i = 0; i < mind->NoDimensions(); i++)
    if (mind->GetFilter(i))
      table->SetVCDistinctVals(i,
                               mind->GetFilter(i)->NoOnes());  // distinct values - not more than the
                                                               // number of rows after WHERE
  rough_mind->ClearLocalDescFilters();

  // Some displays

  if (rccontrol.isOn()) {
    int pack_full = 0, pack_some = 0, pack_all = 0;
    rccontrol.lock(mind->m_conn->GetThreadID())
        << "Packrows after exact evaluation (execute WHERE end):" << system::unlock;
    for (uint i = 0; i < (uint)mind->NoDimensions(); i++)
      if (mind->GetFilter(i)) {
        Filter *f = mind->GetFilter(i);
        pack_full = 0;
        pack_some = 0;
        pack_all = (int)((mind->OrigSize(i) + ((1 << mind->NoPower()) - 1)) >> mind->NoPower());
        for (int b = 0; b < pack_all; b++) {
          if (f->IsFull(b))
            pack_full++;
          else if (!f->IsEmpty(b))
            pack_some++;
        }
        rccontrol.lock(mind->m_conn->GetThreadID())
            << "(t" << i << "): " << pack_all << " all packrows, " << pack_full + pack_some << " to open (including "
            << pack_full << " full)" << system::unlock;
      }
  }

  DescriptorJoinOrdering();

  // descriptor display for joins
  if (rccontrol.isOn()) {
    bool first_time = true;
    for (uint i = 0; i < descriptors.Size(); i++)
      if (!descriptors[i].done && (descriptors[i].IsType_Join() || descriptors[i].IsOuter())) {
        if (first_time) {
          rccontrol.lock(mind->m_conn->GetThreadID()) << "Join execution plan:" << system::unlock;
          first_time = false;
        }
        char buf[1000];
        std::strcpy(buf, " ");
        descriptors[i].ToString(buf, 1000);
        if (descriptors[i].IsDelayed())
          rccontrol.lock(mind->m_conn->GetThreadID())
              << "Delayed: " << buf << " \t(" << int(descriptors[i].evaluation * 100) / 100.0 << ")" << system::unlock;
        else
          rccontrol.lock(mind->m_conn->GetThreadID())
              << "Cnd(" << i << "):  " << buf << " \t(" << int(descriptors[i].evaluation * 100) / 100.0 << ")"
              << system::unlock;
      }
  }

  bool join_or_delayed_present = false;
  for (uint i = 0; i < descriptors.Size(); i++) {
    if (mind->ZeroTuples() && empty_cannot_grow) {
      // set all following descriptors to done
      for (uint j = i; j < descriptors.Size(); j++) descriptors[j].done = true;
      break;
    }
    if (!descriptors[i].done && !descriptors[i].IsDelayed()) {
      // Merging join conditions
      Condition join_desc;
      PrepareJoiningStep(join_desc, descriptors, i,
                         *mind);  // group together all join conditions for one step
      no_of_join_conditions -= join_desc.Size();
      JoinTips join_tips(*mind);

      // Optimization: Check whether there exists "a is null" delayed condition
      // for an outer join
      if (join_desc[0].IsOuter()) {
        for (uint i = 0; i < descriptors.Size(); i++) {
          if (descriptors[i].IsDelayed() && !descriptors[i].done && descriptors[i].op == common::Operator::O_IS_NULL &&
              join_desc[0].right_dims.Get(descriptors[i].attr.vc->GetDim()) &&
              !descriptors[i].attr.vc->IsNullsPossible()) {
            for (int j = 0; j < join_desc[0].right_dims.Size(); j++) {
              if (join_desc[0].right_dims[j] == true) join_tips.null_only[j] = true;
            }
            descriptors[i].done = true;  // completed inside joining algorithms
            no_of_delayed_conditions--;
          }
        }
      }

      if (no_of_join_conditions == 0 &&  // optimizations used only for the last group of conditions
          no_of_delayed_conditions == 0 && parametrized_desc.Size() == 0) {
        // Optimization: count_only is true => do not materialize multiindex
        // (just counts tuples). WARNING: in this case cannot use multiindex for
        // any operations other than NoTuples().
        if (count_only) join_tips.count_only = true;
        join_tips.limit = limit;
        // only one dim used in distinct context?
        int distinct_dim = table->DimInDistinctContext();
        int dims_in_output = 0;
        for (int dim = 0; dim < mind->NoDimensions(); dim++)
          if (mind->IsUsedInOutput(dim)) dims_in_output++;
        if (distinct_dim != -1 && dims_in_output == 1) join_tips.distinct_only[distinct_dim] = true;
      }

      // Optimization: Check whether all dimensions are really used
      DimensionVector dims_used(mind->NoDimensions());
      for (uint jj = 0; jj < descriptors.Size(); jj++) {
        if (jj != i && !descriptors[jj].done) descriptors[jj].DimensionUsed(dims_used);
      }
      // can't utilize not_used_dims in case there are parameterized descs left
      if (parametrized_desc.Size() == 0) {
        for (int dim = 0; dim < mind->NoDimensions(); dim++)
          if (!mind->IsUsedInOutput(dim) && dims_used[dim] == false) join_tips.forget_now[dim] = true;
      }

      // Joining itself
      UpdateJoinCondition(join_desc, join_tips);
    }
  }

  // Execute all delayed conditions
  for (uint i = 0; i < descriptors.Size(); i++) {
    if (!descriptors[i].done) {
      rccontrol.lock(mind->m_conn->GetThreadID()) << "Executing delayed Cnd(" << i << ")" << system::unlock;
      descriptors[i].CoerceColumnTypes();
      descriptors[i].Simplify();
      ApplyDescriptor(i);
      join_or_delayed_present = true;
    }
  }
  if (join_or_delayed_present) rough_mind->MakeDimensionSuspect();  // no common::RSValue::RS_ALL packs
  mind->UpdateNoTuples();
}

ApplyDescriptor:


void ParameterizedFilter::ApplyDescriptor(int desc_number, int64_t limit)
// desc_number = -1 => switch off the rough part
{
  Descriptor &desc = descriptors[desc_number];
  if (desc.op == common::Operator::O_TRUE) {
    desc.done = true;
    return;
  }
  if (desc.op == common::Operator::O_FALSE) {
    mind->Empty();
    desc.done = true;
    return;
  }

  DimensionVector dims(mind->NoDimensions());
  desc.DimensionUsed(dims);
  mind->MarkInvolvedDimGroups(dims);  // create iterators on whole groups (important for
                                      // multidimensional updatable iterators)
  int no_dims = dims.NoDimsUsed();
  if (no_dims == 0 && !desc.IsDeterministic()) dims.SetAll();
  // Check the easy case (one-dim, parallelizable)
  int one_dim = -1;
  common::RSValue *rf = NULL;
  if (no_dims == 1) {
    for (int i = 0; i < mind->NoDimensions(); i++) {
      if (dims[i]) {
        if (mind->GetFilter(i)) one_dim = i;  // exactly one filter (non-join or join with forgotten dims)
        break;
      }
    }
  }
  if (one_dim != -1)
    rf = rough_mind->GetLocalDescFilter(one_dim, desc_number,
                                        true);  // "true" here means that we demand an existing local rough
                                                // filter

  int packs_no = (int)((mind->OrigSize(one_dim) + ((1 << mind->NoPower()) - 1)) >> mind->NoPower());
  int pack_all = rough_mind->NoPacks(one_dim);
  int pack_some = 0;
  for (int b = 0; b < pack_all; b++) {
    if (rough_mind->GetPackStatus(one_dim, b) != common::RSValue::RS_NONE) pack_some++;
  }
  MIUpdatingIterator mit(mind, dims);
  desc.CopyDesCond(mit);
  if (desc.EvaluateOnIndex(mit, limit) == common::ErrorCode::SUCCESS) {
    rccontrol.lock(mind->m_conn->GetThreadID())
        << "EvaluateOnIndex done, desc number " << desc_number << system::unlock;
  } else {
    int poolsize = rceng->query_thread_pool.size();
    if ((stonedb_sysvar_threadpoolsize > 0) && (packs_no / poolsize > 0) && !desc.IsType_Subquery() &&
        !desc.ExsitTmpTable()) {
      int step = 0;
      int task_num = 0;
      /*Partition task slice*/
      if (pack_some <= poolsize) {
        task_num = poolsize;
      } else {
        step = pack_some / poolsize;
        task_num = packs_no / step;
      }
      int mod = packs_no % task_num;
      int num = packs_no / task_num;

      desc.InitParallel(task_num, mit);

      std::vector mis;
      mis.reserve(task_num);

      std::vector taskIterator;
      taskIterator.reserve(task_num);

      for (int i = 0; i < task_num; ++i) {
        auto &mi = mis.emplace_back(*mind, true);
        int pstart = ((i == 0) ? 0 : mod + i * num);
        int pend = mod + (i + 1) * num - 1;

        auto &mii = taskIterator.emplace_back(&mi, dims);
        mii.SetTaskNum(task_num);
        mii.SetTaskId(i);
        mii.SetNoPacksToGo(pend);
        mii.RewindToPack(pstart);
      }

      utils::result_set res;
      for (int i = 0; i < task_num; ++i) {
        res.insert(rceng->query_thread_pool.add_task(&ParameterizedFilter::TaskProcessPacks, this, &taskIterator[i],
                                                     current_tx, rf, &dims, desc_number, limit, one_dim));
      }
      res.get_all_with_except();

      if (mind->m_conn->Killed()) throw common::KilledException("catch thread pool Exception: TaskProcessPacks");
      mind->UpdateNoTuples();

    } else {

      std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();

      common::RSValue cur_roughval;
      uint64_t passed = 0;
      int pack = -1;
      int index = 0;
      while (mit.IsValid()) {
        if (limit != -1 && rf) {  // rf - not null if there is one dim only
                                  // (otherwise packs make no sense)
          if (passed >= (uint64_t)limit) {
            mit.ResetCurrentPack();
            mit.NextPackrow();
            continue;
          }
          if (mit.PackrowStarted()) {
            if (pack != -1) passed += mit.NoOnesUncommited(pack);
            pack = mit.GetCurPackrow(one_dim);
          }
        }

        std::chrono::high_resolution_clock::time_point __start = std::chrono::high_resolution_clock::now();

        if (rf && mit.GetCurPackrow(one_dim) >= 0)
          cur_roughval = rf[mit.GetCurPackrow(one_dim)];
        else
          cur_roughval = common::RSValue::RS_SOME;

        if (cur_roughval == common::RSValue::RS_NONE) {
          mit.ResetCurrentPack();
          mit.NextPackrow();
        } else if (cur_roughval == common::RSValue::RS_ALL) {
          mit.NextPackrow();
        } else {
          // common::RSValue::RS_SOME or common::RSValue::RS_UNKNOWN
          desc.EvaluatePack(mit);
        }
        if (mind->m_conn->Killed()) throw common::KilledException();

        auto diff =
            std::chrono::duration_cast>(std::chrono::high_resolution_clock::now() - __start);
        STONEDB_LOG(LogCtl_Level::INFO, "Timer %f : %s index: %d", diff.count(), " __SUBQUERY ", index);
        
        index++;
      }
      mit.Commit();

      auto diff =
          std::chrono::duration_cast>(std::chrono::high_resolution_clock::now() - start);
      STONEDB_LOG(LogCtl_Level::INFO, "Timer %f : %s", diff.count(), " __SUBQUERY over");

    }
  }
  desc.done = true;
  if (one_dim != -1 && mind->GetFilter(one_dim)) {  // update global rough part
    Filter *f = mind->GetFilter(one_dim);
    for (int p = 0; p < rough_mind->NoPacks(one_dim); p++)
      if (f->IsEmpty(p)) {
        rough_mind->SetPackStatus(one_dim, p, common::RSValue::RS_NONE);
      }
  }
  desc.UpdateVCStatistics();
  return;
}

EvaluatePackImpl:


void Descriptor::EvaluatePackImpl(MIUpdatingIterator &mit) {
  MEASURE_FET("Descriptor::EvaluatePackImpl(...)");

  std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();

  // Check if we can delegate evaluation of descriptor to physical column
  if (encoded)
    attr.vc->EvaluatePack(mit, *this);
  else if (IsType_OrTree() && (GetParallelSize() == 0)) {
    // single thread Prepare rough values to be stored inside the tree
    tree->root->ClearRoughValues();
    tree->root->EvaluateRoughlyPack(mit);

    std::chrono::high_resolution_clock::time_point __start = std::chrono::high_resolution_clock::now();

    tree->root->EvaluatePack(mit);

    auto __diff =
        std::chrono::duration_cast>(std::chrono::high_resolution_clock::now() - __start);
    STONEDB_LOG(LogCtl_Level::INFO, "Timer %f : %s", __diff.count(), 
        " __SUBQUERY EvaluatePackImpl tree->root->EvaluatePack (IsType_OrTree() && (GetParallelSize() == 0))");

  } else if (IsType_OrTree() && GetParallelSize()) {
    // muti thread for IsType_OrTree; single reserved
    tree->root->MClearRoughValues(mit.GetTaskId());
    tree->root->MEvaluateRoughlyPack(mit, mit.GetTaskId());

    std::chrono::high_resolution_clock::time_point __start = std::chrono::high_resolution_clock::now();

    tree->root->MEvaluatePack(mit, mit.GetTaskId());
  
    auto __diff =
        std::chrono::duration_cast>(std::chrono::high_resolution_clock::now() - __start);
    STONEDB_LOG(LogCtl_Level::INFO, "Timer %f : %s", __diff.count(),
                " __SUBQUERY EvaluatePackImpl tree->root->MEvaluatePack (IsType_OrTree() && (GetParallelSize()))");
  
  } else if (types::RequiresUTFConversions(collation)) {

    std::chrono::high_resolution_clock::time_point __start = std::chrono::high_resolution_clock::now();

    std::scoped_lock guard(mtx);
    while (mit.IsValid()) {
      if (CheckCondition_UTF(mit) == false) mit.ResetCurrent();
      ++mit;
      if (mit.PackrowStarted()) break;
    }

    auto __diff =
        std::chrono::duration_cast>(std::chrono::high_resolution_clock::now() - __start);
    STONEDB_LOG(LogCtl_Level::INFO, "Timer %f : %s", __diff.count(),
                " __SUBQUERY EvaluatePackImpl types::RequiresUTFConversions(collation)");

  } else {
    if (IsType_Subquery() && op != common::Operator::O_OR_TREE) {
      // pack based optimization of corr. subq. by using RoughQuery
      common::Tribool res = RoughCheckSubselectCondition(mit, SubSelectOptimizationType::PACK_BASED);
      if (res == false)
        mit.ResetCurrentPack();
      else if (res == common::TRIBOOL_UNKNOWN) {
        // int true_c = 0, false_c = 0, unkn_c = 0;

        std::chrono::high_resolution_clock::time_point __start = std::chrono::high_resolution_clock::now();

        int index = 0;
        auto check_cast = std::chrono::duration(0);
        auto reset_cast = std::chrono::duration(0);

        while (mit.IsValid()) {
          index++;

          std::chrono::high_resolution_clock::time_point __start 
              = std::chrono::high_resolution_clock::now();

          // row based optimization of corr. subq. by using RoughQuery
          res = RoughCheckSubselectCondition(mit, SubSelectOptimizationType::ROW_BASED);
          
          {
            auto __diff = std::chrono::duration_cast>(
                std::chrono::high_resolution_clock::now() - __start);
            // STONEDB_LOG(LogCtl_Level::INFO, "Timer %f : %s", __diff.count(), " __SUBQUERY EvaluatePackImpl
            // CheckCondition");

            check_cast += __diff;
          }

          // if(res == false)
          //	false_c++;
          // else if(res == true)
          //	true_c++;
          // else
          //	unkn_c++;

          __start = std::chrono::high_resolution_clock::now();

          if (res == false)
            mit.ResetCurrent();
          else if (res == common::TRIBOOL_UNKNOWN && CheckCondition(mit) == false)
            mit.ResetCurrent();

          
           auto __diff = std::chrono::duration_cast>(
                std::chrono::high_resolution_clock::now() - __start);
            // STONEDB_LOG(LogCtl_Level::INFO, "Timer %f : %s", __diff.count(), " __SUBQUERY EvaluatePackImpl
            // mit.ResetCurrent");

           reset_cast += __diff;

          ++mit;
          if (mit.PackrowStarted()) break;
        }
        // cout << "# of skipped subqueries: " << true_c << "/" << false_c <<
        // "/" << unkn_c
        // << " -> " << (true_c + false_c) << " / " << (true_c + false_c +
        // unkn_c) << endl;

        auto __diff = std::chrono::duration_cast>(
            std::chrono::high_resolution_clock::now() - __start);
        STONEDB_LOG(LogCtl_Level::INFO, "Timer %f : %s, index: %d , check_cast: %f , reset_cast :%f",
            __diff.count(), 
            " __SUBQUERY EvaluatePackImpl common::TRIBOOL_UNKNOWN", 
            index, check_cast.count(), reset_cast.count());

      }
    } else {

      std::chrono::high_resolution_clock::time_point __start = std::chrono::high_resolution_clock::now();

      std::scoped_lock guard(mtx);

      int index = 0;
      auto check_cast = std::chrono::duration (0);
      auto reset_cast = std::chrono::duration(0);
      while (mit.IsValid()) {

        index++;
     
        std::chrono::high_resolution_clock::time_point __start = std::chrono::high_resolution_clock::now();
        
        bool cond = CheckCondition(mit);

        {
          auto __diff = std::chrono::duration_cast>(
              std::chrono::high_resolution_clock::now() - __start);
          // STONEDB_LOG(LogCtl_Level::INFO, "Timer %f : %s", __diff.count(), " __SUBQUERY EvaluatePackImpl CheckCondition");

          check_cast += __diff;
        }

        if (cond == false) {

          std::chrono::high_resolution_clock::time_point __start = std::chrono::high_resolution_clock::now();

          mit.ResetCurrent();

          auto __diff = std::chrono::duration_cast>(
              std::chrono::high_resolution_clock::now() - __start);
          // STONEDB_LOG(LogCtl_Level::INFO, "Timer %f : %s", __diff.count(), " __SUBQUERY EvaluatePackImpl mit.ResetCurrent");

          reset_cast += __diff;

        };

        ++mit;
        if (mit.PackrowStarted()) break;
      }

      auto __diff =
          std::chrono::duration_cast>(std::chrono::high_resolution_clock::now() - __start);
      STONEDB_LOG(LogCtl_Level::INFO, "Timer %f : %s, index: %d, check_cast: %f , reset_cast: %f", 
          __diff.count(),
          " __SUBQUERY EvaluatePackImpl else else", 
          index, check_cast.count(), reset_cast.count());

    }
  }

  auto diff =
      std::chrono::duration_cast>(std::chrono::high_resolution_clock::now() - start);
  STONEDB_LOG(LogCtl_Level::INFO, "Timer %f : %s", 
      diff.count(), " __SUBQUERY EvaluatePackImpl");
}

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章