SQLRETURN do_query()

in driver/execute.cc [46:318]


SQLRETURN do_query(STMT *stmt, std::string query)
{
    if (stmt && stmt->dbc && stmt->dbc->fh) {
      stmt->dbc->fh->invoke_start_time();
    }

    assert(stmt);

    SQLRETURN error = SQL_ERROR;
    int native_error = 0;
    SQLULEN query_length = query.length();
    bool trigger_failover_upon_error = true;

    LOCK_STMT_DEFER(stmt);

    if (query.empty())
    {
      /* Probably error from insert_param */
      goto exit;
    }

    if(!SQL_SUCCEEDED(set_sql_select_limit(stmt->dbc,
                      stmt->stmt_options.max_rows, TRUE)))
    {
      /* The error is set for DBC, copy it into STMT */
      stmt->set_error(stmt->dbc->error.sqlstate.c_str(),
                     stmt->dbc->error.message.c_str(),
                     stmt->dbc->error.native_error);

      /* if setting sql_select_limit fails, the query will probably fail anyway too */
      goto exit;
    }

    if (query_length == 0)
    {
      query_length= strlen(query.c_str());
    }

    MYLOG_STMT_TRACE(stmt, query.c_str());
    DO_LOCK_STMT();

    if ( !is_server_alive( stmt->dbc ) )
    {
      stmt->set_error("08S01" /* "HYT00" */,
                      stmt->dbc->connection_proxy->error(),
                      stmt->dbc->connection_proxy->error_code());

      translate_error((char*)stmt->error.sqlstate.c_str(), MYERR_08S01 /* S1000 */,
                      stmt->dbc->connection_proxy->error_code());
      goto exit;
    }

    /* Simplifying task so far - we will do "LIMIT" scrolling forward only
     * and when no musltiple statements is allowed - we can't now parse query
     * that well to detect multiple queries.
     */
    if (stmt->dbc->ds->opt_PREFETCH > 0
        && !stmt->dbc->ds->opt_MULTI_STATEMENTS
        && stmt->stmt_options.cursor_type == SQL_CURSOR_FORWARD_ONLY
        && scrollable(stmt, query.c_str(), query.c_str() + query_length)
        && !ssps_used(stmt))
    {
      /* we might want to read primary key info at this point, but then we have to
         know if we have a select from a single table...
       */
      ssps_close(stmt);
      stmt->scroller.reset();

      stmt->scroller.row_count= calc_prefetch_number(
                                      stmt->dbc->ds->opt_PREFETCH,
                                      stmt->ard->array_size,
                                      stmt->stmt_options.max_rows);

      scroller_create(stmt, query.c_str(), query_length);
      scroller_move(stmt);
      MYLOG_STMT_TRACE(stmt, stmt->scroller.query);

      SQLRETURN rc = stmt->dbc->execute_query(stmt->scroller.query,
                               static_cast<unsigned long>(stmt->scroller.query_len), false);
      if (!SQL_SUCCEEDED(rc))
      {
          native_error = stmt->dbc->error.native_error;
          trigger_failover_upon_error = false; // possible failover was already handled in execute_query()
      }
    }
      /* Not using ssps for scroller so far. Relaxing a bit condition
       if MULTI_STATEMENTS option selected by primitive check if
       this is a batch of queries */
    else if (ssps_used(stmt))
    {

      // This assertion will need to be revisited later.
      // The situation when we can have at most one attribute is temporary.
      assert(
        (stmt->param_count == stmt->query_attr_names.size())
        || (1+stmt->param_count == stmt->query_attr_names.size())
      );

      bool bind_failed = false;

      // FIXME: What if runtime client library version does not agree with version used here?

#if MYSQL_VERSION_ID >= 80300
      // For older servers that don't support named params
      // we just don't count them and specify the number of unnamed params.
      unsigned int p_number = is_minimum_version(stmt->dbc->connection_proxy->get_server_version(), "8.3.0") ?
        stmt->query_attr_names.size() : stmt->param_count;

      if (p_number)
      {
        bind_failed = stmt->dbc->connection_proxy->stmt_bind_named_param(stmt->ssps,
          stmt->param_bind.data(), p_number, stmt->query_attr_names.data());
      }

#else
      if (stmt->param_bind.size() && stmt->param_count)
      {
        bind_failed = stmt->dbc->connection_proxy->stmt_bind_param(stmt->ssps, &stmt->param_bind[0]);
      }
#endif

      if (!bind_failed)
      {
        native_error = stmt->dbc->connection_proxy->stmt_execute(stmt->ssps);
      }
      else
      {
        stmt->set_error("HY000",
                       stmt->dbc->connection_proxy->stmt_error(stmt->ssps),
                       stmt->dbc->connection_proxy->stmt_errno(stmt->ssps));

        /* For some errors - translating to more appropriate status */
        translate_error((char*)stmt->error.sqlstate.c_str(), MYERR_S1000,
                        stmt->error.native_error);
        error = stmt->error.retcode;
        goto exit;
      }
      MYLOG_STMT_TRACE(stmt, "ssps has been executed");
    }
    else
    {
      MYLOG_STMT_TRACE(stmt, "Using direct execution");
      /* Need to close ps handler if it is open as our result will be generated
         by direct execution. and ps handler may create some chaos */
      ssps_close(stmt);

      if (stmt->bind_query_attrs(false) == SQL_ERROR)
      {
        error = SQL_ERROR;
        goto exit;
      }

      SQLRETURN rc = stmt->dbc->execute_query(query.c_str(), query_length, false);
      if (SQL_SUCCEEDED(rc))
      {
          const std::vector<std::string> statements = parse_query_into_statements(query.c_str());
          for (int i = statements.size() - 1; i >= 0; i--)
          {
              std::string statement = statements[i];
              for (auto& c : statement) c = toupper(c);

              if (statement == "START TRANSACTION" || statement == "BEGIN")
              {
                  stmt->dbc->transaction_open = true;
                  break;
              }
              else if (statement == "COMMIT" || statement == "ROLLBACK")
              {
                  stmt->dbc->transaction_open = false;
                  break;
              }
          }
      }
      else
      {
          native_error = stmt->dbc->error.native_error;
          trigger_failover_upon_error = false; // possible failover was already handled in execute_query()
      }
    }

    MYLOG_STMT_TRACE(stmt, "query has been executed");

    if (native_error)
    {
      const auto error_code = stmt->dbc->connection_proxy->error_code();
      if (error_code)
      {
          MYLOG_STMT_TRACE(stmt, stmt->dbc->connection_proxy->error());
          stmt->set_error("HY000");

          // For some errors - translating to more appropriate status
          translate_error((char*)stmt->error.sqlstate.c_str(), MYERR_S1000, error_code);
      }
      else
      {
          MYLOG_STMT_TRACE(stmt, stmt->dbc->error.message.c_str());
          stmt->set_error(stmt->dbc->error.sqlstate.c_str(),
              stmt->dbc->error.message.c_str(),
              stmt->dbc->error.native_error);
      }

      goto exit;
    }

    if (!get_result_metadata(stmt, FALSE))
    {
      /* Query was supposed to return result, but result is NULL*/
      if (returned_result(stmt))
      {
        error = stmt->set_error(MYERR_S1000);
        goto exit;
      }
      else /* Query was not supposed to return a result */
      {
        error= SQL_SUCCESS;     /* no result set */
        stmt->state= ST_EXECUTED;
        update_affected_rows(stmt);
        // The query without results can end spans here.
        stmt->telemetry.span_end(stmt);
        goto exit;
      }
    }

    if (bind_result(stmt) || get_result(stmt))
    {
        error = stmt->set_error(MYERR_S1000);
        goto exit;
    }
    /* Caching row counts for queries returning resultset as well */
    //update_affected_rows(stmt);
    fix_result_types(stmt);

    /* If the only resultset is OUT params, then we can only detect
       corresponding server_status right after execution.
       If the RS is OUT params - we do not need to do store_result obviously */
    if (IS_PS_OUT_PARAMS(stmt))
    {
      /* This status(SERVER_PS_OUT_PARAMS) can be only if we used PS */
      ssps_get_out_params(stmt);

      if (stmt->out_params_state == OPS_STREAMS_PENDING)
      {
        error= SQL_PARAM_DATA_AVAILABLE;
        goto exit;
      }
    }


    error= SQL_SUCCESS;

exit:
    if (!SQL_SUCCEEDED(error)) {
      stmt->telemetry.set_error(stmt, stmt->error.message);
    }

    if (trigger_failover_upon_error && error == SQL_ERROR) {
      const char *error_code, *error_msg;
      if (stmt->dbc->fh->trigger_failover_if_needed(stmt->error.sqlstate.c_str(), error_code, error_msg))
        stmt->set_error(error_code, error_msg, 0);
    }

    /*
      If the original query was modified, we reset stmt->query so that the
      next execution re-starts with the original query.
    */
    if (GET_QUERY(&stmt->orig_query))
    {
      stmt->query = stmt->orig_query;
      stmt->orig_query.reset(NULL, NULL, NULL);
    }

    return error;
}