mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
Merge 10.4 into 10.5
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
|
||||
Copyright (c) 2009, 2021, MariaDB
|
||||
Copyright (c) 2009, 2022, MariaDB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -320,6 +320,7 @@ struct st_connection
|
||||
char *name;
|
||||
size_t name_len;
|
||||
MYSQL_STMT* stmt;
|
||||
MYSQL_BIND *ps_params;
|
||||
/* Set after send to disallow other queries before reap */
|
||||
my_bool pending;
|
||||
|
||||
@@ -394,6 +395,10 @@ enum enum_commands {
|
||||
Q_ENABLE_PREPARE_WARNINGS, Q_DISABLE_PREPARE_WARNINGS,
|
||||
Q_RESET_CONNECTION,
|
||||
Q_OPTIMIZER_TRACE,
|
||||
Q_PS_PREPARE,
|
||||
Q_PS_BIND,
|
||||
Q_PS_EXECUTE,
|
||||
Q_PS_CLOSE,
|
||||
Q_UNKNOWN, /* Unknown command. */
|
||||
Q_COMMENT, /* Comments, ignored. */
|
||||
Q_COMMENT_WITH_COMMAND,
|
||||
@@ -507,6 +512,10 @@ const char *command_names[]=
|
||||
"disable_prepare_warnings",
|
||||
"reset_connection",
|
||||
"optimizer_trace",
|
||||
"PS_prepare",
|
||||
"PS_bind",
|
||||
"PS_execute",
|
||||
"PS_close",
|
||||
0
|
||||
};
|
||||
|
||||
@@ -7900,6 +7909,15 @@ static void handle_no_active_connection(struct st_command *command,
|
||||
var_set_errno(2006);
|
||||
}
|
||||
|
||||
/* handler functions to execute prepared statement calls in client C API */
|
||||
void run_prepare_stmt(struct st_connection *cn, struct st_command *command, const char *query,
|
||||
size_t query_len, DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings);
|
||||
void run_bind_stmt(struct st_connection *cn, struct st_command *command, const char *query,
|
||||
size_t query_len, DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings);
|
||||
void run_execute_stmt(struct st_connection *cn, struct st_command *command, const char *query,
|
||||
size_t query_len, DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings);
|
||||
void run_close_stmt(struct st_connection *cn, struct st_command *command, const char *query,
|
||||
size_t query_len, DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings);
|
||||
|
||||
/*
|
||||
Run query using MySQL C API
|
||||
@@ -7931,6 +7949,32 @@ void run_query_normal(struct st_connection *cn, struct st_command *command,
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
/* handle prepared statement commands */
|
||||
switch (command->type) {
|
||||
case Q_PS_PREPARE:
|
||||
run_prepare_stmt(cn, command, query, query_len, ds, ds_warnings);
|
||||
flags &= ~QUERY_SEND_FLAG;
|
||||
goto end;
|
||||
break;
|
||||
case Q_PS_BIND:
|
||||
run_bind_stmt(cn, command, query, query_len, ds, ds_warnings);
|
||||
flags &= ~QUERY_SEND_FLAG;
|
||||
goto end;
|
||||
break;
|
||||
case Q_PS_EXECUTE:
|
||||
run_execute_stmt(cn, command, query, query_len, ds, ds_warnings);
|
||||
flags &= ~QUERY_SEND_FLAG;
|
||||
goto end;
|
||||
break;
|
||||
case Q_PS_CLOSE:
|
||||
run_close_stmt(cn, command, query, query_len, ds, ds_warnings);
|
||||
flags &= ~QUERY_SEND_FLAG;
|
||||
goto end;
|
||||
break;
|
||||
default: /* not a prepared statement command */
|
||||
break;
|
||||
}
|
||||
|
||||
if (flags & QUERY_SEND_FLAG)
|
||||
{
|
||||
/*
|
||||
@@ -8486,6 +8530,411 @@ end:
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
/*
|
||||
prepare query using prepared statement C API
|
||||
|
||||
SYNPOSIS
|
||||
run_prepare_stmt
|
||||
mysql - mysql handle
|
||||
command - current command pointer
|
||||
query - query string to execute
|
||||
query_len - length query string to execute
|
||||
ds - output buffer where to store result form query
|
||||
|
||||
RETURN VALUE
|
||||
error - function will not return
|
||||
*/
|
||||
|
||||
void run_prepare_stmt(struct st_connection *cn, struct st_command *command, const char *query, size_t query_len, DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings)
|
||||
{
|
||||
|
||||
MYSQL *mysql= cn->mysql;
|
||||
MYSQL_STMT *stmt;
|
||||
DYNAMIC_STRING ds_prepare_warnings;
|
||||
DBUG_ENTER("run_prepare_stmt");
|
||||
DBUG_PRINT("query", ("'%-.60s'", query));
|
||||
|
||||
/*
|
||||
Init a new stmt if it's not already one created for this connection
|
||||
*/
|
||||
if(!(stmt= cn->stmt))
|
||||
{
|
||||
if (!(stmt= mysql_stmt_init(mysql)))
|
||||
die("unable to init stmt structure");
|
||||
cn->stmt= stmt;
|
||||
}
|
||||
|
||||
/* Init dynamic strings for warnings */
|
||||
if (!disable_warnings)
|
||||
{
|
||||
init_dynamic_string(&ds_prepare_warnings, NULL, 0, 256);
|
||||
}
|
||||
|
||||
/*
|
||||
Prepare the query
|
||||
*/
|
||||
char* PS_query= command->first_argument;
|
||||
size_t PS_query_len= command->end - command->first_argument;
|
||||
if (do_stmt_prepare(cn, PS_query, PS_query_len))
|
||||
{
|
||||
handle_error(command, mysql_stmt_errno(stmt),
|
||||
mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/*
|
||||
Get the warnings from mysql_stmt_prepare and keep them in a
|
||||
separate string
|
||||
*/
|
||||
if (!disable_warnings)
|
||||
append_warnings(&ds_prepare_warnings, mysql);
|
||||
end:
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
/*
|
||||
bind parameters for a prepared statement C API
|
||||
|
||||
SYNPOSIS
|
||||
run_bind_stmt
|
||||
mysql - mysql handle
|
||||
command - current command pointer
|
||||
query - query string to execute
|
||||
query_len - length query string to execute
|
||||
ds - output buffer where to store result form query
|
||||
|
||||
RETURN VALUE
|
||||
error - function will not return
|
||||
*/
|
||||
|
||||
void run_bind_stmt(struct st_connection *cn, struct st_command *command,
|
||||
const char *query, size_t query_len, DYNAMIC_STRING *ds,
|
||||
DYNAMIC_STRING *ds_warnings
|
||||
)
|
||||
{
|
||||
MYSQL_STMT *stmt= cn->stmt;
|
||||
DBUG_ENTER("run_bind_stmt");
|
||||
DBUG_PRINT("query", ("'%-.60s'", query));
|
||||
MYSQL_BIND *ps_params= cn->ps_params;
|
||||
if (ps_params)
|
||||
{
|
||||
for (size_t i=0; i<stmt->param_count; i++)
|
||||
{
|
||||
my_free(ps_params[i].buffer);
|
||||
ps_params[i].buffer= NULL;
|
||||
}
|
||||
my_free(ps_params);
|
||||
ps_params= NULL;
|
||||
}
|
||||
|
||||
/* Init PS-parameters. */
|
||||
cn->ps_params= ps_params = (MYSQL_BIND*)my_malloc(PSI_NOT_INSTRUMENTED,
|
||||
sizeof(MYSQL_BIND) *
|
||||
stmt->param_count,
|
||||
MYF(MY_WME));
|
||||
bzero((char *) ps_params, sizeof(MYSQL_BIND) * stmt->param_count);
|
||||
|
||||
int i=0;
|
||||
char *c;
|
||||
long *l;
|
||||
double *d;
|
||||
|
||||
char *p= strtok((char*)command->first_argument, " ");
|
||||
while (p != nullptr)
|
||||
{
|
||||
(void)strtol(p, &c, 10);
|
||||
if (!*c)
|
||||
{
|
||||
ps_params[i].buffer_type= MYSQL_TYPE_LONG;
|
||||
l= (long*)my_malloc(PSI_NOT_INSTRUMENTED, sizeof(long), MYF(MY_WME));
|
||||
*l= strtol(p, &c, 10);
|
||||
ps_params[i].buffer= (void*)l;
|
||||
ps_params[i].buffer_length= 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
(void)strtod(p, &c);
|
||||
if (!*c)
|
||||
{
|
||||
ps_params[i].buffer_type= MYSQL_TYPE_DECIMAL;
|
||||
d= (double*)my_malloc(PSI_NOT_INSTRUMENTED, sizeof(double),
|
||||
MYF(MY_WME));
|
||||
*d= strtod(p, &c);
|
||||
ps_params[i].buffer= (void*)d;
|
||||
ps_params[i].buffer_length= 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
ps_params[i].buffer_type= MYSQL_TYPE_STRING;
|
||||
ps_params[i].buffer= strdup(p);
|
||||
ps_params[i].buffer_length= (unsigned long)strlen(p);
|
||||
}
|
||||
}
|
||||
|
||||
p= strtok(nullptr, " ");
|
||||
i++;
|
||||
}
|
||||
|
||||
int rc= mysql_stmt_bind_param(stmt, ps_params);
|
||||
if (rc)
|
||||
{
|
||||
die("mysql_stmt_bind_param() failed': %d %s",
|
||||
mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
|
||||
}
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
/*
|
||||
execute query using prepared statement C API
|
||||
|
||||
SYNPOSIS
|
||||
run_axecute_stmt
|
||||
mysql - mysql handle
|
||||
command - current command pointer
|
||||
query - query string to execute
|
||||
query_len - length query string to execute
|
||||
ds - output buffer where to store result form query
|
||||
|
||||
RETURN VALUE
|
||||
error - function will not return
|
||||
*/
|
||||
|
||||
void run_execute_stmt(struct st_connection *cn, struct st_command *command,
|
||||
const char *query, size_t query_len, DYNAMIC_STRING *ds,
|
||||
DYNAMIC_STRING *ds_warnings
|
||||
)
|
||||
{
|
||||
MYSQL_RES *res= NULL; /* Note that here 'res' is meta data result set */
|
||||
MYSQL *mysql= cn->mysql;
|
||||
MYSQL_STMT *stmt= cn->stmt;
|
||||
DYNAMIC_STRING ds_execute_warnings;
|
||||
DBUG_ENTER("run_execute_stmt");
|
||||
DBUG_PRINT("query", ("'%-.60s'", query));
|
||||
|
||||
/* Init dynamic strings for warnings */
|
||||
if (!disable_warnings)
|
||||
{
|
||||
init_dynamic_string(&ds_execute_warnings, NULL, 0, 256);
|
||||
}
|
||||
|
||||
#if MYSQL_VERSION_ID >= 50000
|
||||
if (cursor_protocol_enabled)
|
||||
{
|
||||
/*
|
||||
Use cursor when retrieving result
|
||||
*/
|
||||
ulong type= CURSOR_TYPE_READ_ONLY;
|
||||
if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type))
|
||||
die("mysql_stmt_attr_set(STMT_ATTR_CURSOR_TYPE) failed': %d %s",
|
||||
mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
Execute the query
|
||||
*/
|
||||
if (do_stmt_execute(cn))
|
||||
{
|
||||
handle_error(command, mysql_stmt_errno(stmt),
|
||||
mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/*
|
||||
When running in cursor_protocol get the warnings from execute here
|
||||
and keep them in a separate string for later.
|
||||
*/
|
||||
if (cursor_protocol_enabled && !disable_warnings)
|
||||
append_warnings(&ds_execute_warnings, mysql);
|
||||
|
||||
/*
|
||||
We instruct that we want to update the "max_length" field in
|
||||
mysql_stmt_store_result(), this is our only way to know how much
|
||||
buffer to allocate for result data
|
||||
*/
|
||||
{
|
||||
my_bool one= 1;
|
||||
if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &one))
|
||||
die("mysql_stmt_attr_set(STMT_ATTR_UPDATE_MAX_LENGTH) failed': %d %s",
|
||||
mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
|
||||
}
|
||||
|
||||
/*
|
||||
If we got here the statement succeeded and was expected to do so,
|
||||
get data. Note that this can still give errors found during execution!
|
||||
Store the result of the query if if will return any fields
|
||||
*/
|
||||
if (mysql_stmt_field_count(stmt) && mysql_stmt_store_result(stmt))
|
||||
{
|
||||
handle_error(command, mysql_stmt_errno(stmt),
|
||||
mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* If we got here the statement was both executed and read successfully */
|
||||
handle_no_error(command);
|
||||
if (!disable_result_log)
|
||||
{
|
||||
/*
|
||||
Not all statements creates a result set. If there is one we can
|
||||
now create another normal result set that contains the meta
|
||||
data. This set can be handled almost like any other non prepared
|
||||
statement result set.
|
||||
*/
|
||||
if ((res= mysql_stmt_result_metadata(stmt)) != NULL)
|
||||
{
|
||||
/* Take the column count from meta info */
|
||||
MYSQL_FIELD *fields= mysql_fetch_fields(res);
|
||||
uint num_fields= mysql_num_fields(res);
|
||||
|
||||
if (display_metadata)
|
||||
append_metadata(ds, fields, num_fields);
|
||||
|
||||
if (!display_result_vertically)
|
||||
append_table_headings(ds, fields, num_fields);
|
||||
|
||||
append_stmt_result(ds, stmt, fields, num_fields);
|
||||
|
||||
mysql_free_result(res); /* Free normal result set with meta data */
|
||||
|
||||
/*
|
||||
Normally, if there is a result set, we do not show warnings from the
|
||||
prepare phase. This is because some warnings are generated both during
|
||||
prepare and execute; this would generate different warning output
|
||||
between normal and ps-protocol test runs.
|
||||
|
||||
The --enable_prepare_warnings command can be used to change this so
|
||||
that warnings from both the prepare and execute phase are shown.
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
This is a query without resultset
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
Fetch info before fetching warnings, since it will be reset
|
||||
otherwise.
|
||||
*/
|
||||
if (!disable_info)
|
||||
append_info(ds, mysql_stmt_affected_rows(stmt), mysql_info(mysql));
|
||||
|
||||
if (display_session_track_info)
|
||||
append_session_track_info(ds, mysql);
|
||||
|
||||
|
||||
if (!disable_warnings)
|
||||
{
|
||||
/* Get the warnings from execute */
|
||||
|
||||
/* Append warnings to ds - if there are any */
|
||||
if (append_warnings(&ds_execute_warnings, mysql) ||
|
||||
ds_execute_warnings.length ||
|
||||
ds_warnings->length)
|
||||
{
|
||||
dynstr_append_mem(ds, "Warnings:\n", 10);
|
||||
if (ds_warnings->length)
|
||||
dynstr_append_mem(ds, ds_warnings->str,
|
||||
ds_warnings->length);
|
||||
if (ds_execute_warnings.length)
|
||||
dynstr_append_mem(ds, ds_execute_warnings.str,
|
||||
ds_execute_warnings.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
if (!disable_warnings)
|
||||
{
|
||||
dynstr_free(&ds_execute_warnings);
|
||||
}
|
||||
|
||||
/*
|
||||
We save the return code (mysql_stmt_errno(stmt)) from the last call sent
|
||||
to the server into the mysqltest builtin variable $mysql_errno. This
|
||||
variable then can be used from the test case itself.
|
||||
*/
|
||||
|
||||
var_set_errno(mysql_stmt_errno(stmt));
|
||||
|
||||
revert_properties();
|
||||
|
||||
/* Close the statement if reconnect, need new prepare */
|
||||
{
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
my_bool reconnect;
|
||||
mysql_get_option(mysql, MYSQL_OPT_RECONNECT, &reconnect);
|
||||
if (reconnect)
|
||||
#else
|
||||
if (mysql->reconnect)
|
||||
#endif
|
||||
{
|
||||
if (cn->ps_params)
|
||||
{
|
||||
for (size_t i=0; i<stmt->param_count; i++)
|
||||
{
|
||||
my_free(cn->ps_params[i].buffer);
|
||||
cn->ps_params[i].buffer= NULL;
|
||||
}
|
||||
my_free(cn->ps_params);
|
||||
}
|
||||
mysql_stmt_close(stmt);
|
||||
cn->stmt= NULL;
|
||||
cn->ps_params= NULL;
|
||||
}
|
||||
}
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
/*
|
||||
close a prepared statement C API
|
||||
|
||||
SYNPOSIS
|
||||
run_close_stmt
|
||||
mysql - mysql handle
|
||||
command - current command pointer
|
||||
query - query string to execute
|
||||
query_len - length query string to execute
|
||||
ds - output buffer where to store result form query
|
||||
|
||||
RETURN VALUE
|
||||
error - function will not return
|
||||
*/
|
||||
|
||||
void run_close_stmt(struct st_connection *cn, struct st_command *command,
|
||||
const char *query, size_t query_len, DYNAMIC_STRING *ds,
|
||||
DYNAMIC_STRING *ds_warnings
|
||||
)
|
||||
{
|
||||
MYSQL_STMT *stmt= cn->stmt;
|
||||
DBUG_ENTER("run_close_stmt");
|
||||
DBUG_PRINT("query", ("'%-.60s'", query));
|
||||
|
||||
if (cn->ps_params)
|
||||
{
|
||||
|
||||
for (size_t i=0; i<stmt->param_count; i++)
|
||||
{
|
||||
my_free(cn->ps_params[i].buffer);
|
||||
cn->ps_params[i].buffer= NULL;
|
||||
}
|
||||
my_free(cn->ps_params);
|
||||
}
|
||||
|
||||
/* Close the statement */
|
||||
if (stmt)
|
||||
{
|
||||
mysql_stmt_close(stmt);
|
||||
cn->stmt= NULL;
|
||||
}
|
||||
cn->ps_params= NULL;
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
@@ -9526,6 +9975,10 @@ int main(int argc, char **argv)
|
||||
/* fall through */
|
||||
case Q_QUERY:
|
||||
case Q_REAP:
|
||||
case Q_PS_PREPARE:
|
||||
case Q_PS_BIND:
|
||||
case Q_PS_EXECUTE:
|
||||
case Q_PS_CLOSE:
|
||||
{
|
||||
my_bool old_display_result_vertically= display_result_vertically;
|
||||
/* Default is full query, both reap and send */
|
||||
|
Reference in New Issue
Block a user