1
0
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:
Marko Mäkelä
2022-03-29 12:59:18 +03:00
86 changed files with 2875 additions and 251 deletions

View File

@@ -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 */