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;
}