mirror of
https://github.com/MariaDB/server.git
synced 2025-12-24 11:21:21 +03:00
Implement WL#2661 "Prepared Statements: Dynamic SQL in Stored Procedures".
The idea of the patch is to separate statement processing logic, such as parsing, validation of the parsed tree, execution and cleanup, from global query processing logic, such as logging, resetting priorities of a thread, resetting stored procedure cache, resetting thread count of errors and warnings. This makes PREPARE and EXECUTE behave similarly to the rest of SQL statements and allows their use in stored procedures. This patch contains a change in behaviour: until recently for each SQL prepared statement command, 2 queries were written to the general log, e.g. [Query] prepare stmt from @stmt_text; [Prepare] select * from t1 <-- contents of @stmt_text The chagne was necessary to prevent [Prepare] commands from being written to the general log when executing a stored procedure with Dynamic SQL. We should consider whether the old behavior is preferrable and probably restore it. This patch refixes Bug#7115, Bug#10975 (partially), Bug#10605 (various bugs in Dynamic SQL reported before it was disabled).
This commit is contained in:
135
sql/sql_parse.cc
135
sql/sql_parse.cc
@@ -1644,7 +1644,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
||||
}
|
||||
case COM_STMT_PREPARE:
|
||||
{
|
||||
mysql_stmt_prepare(thd, packet, packet_length, 0);
|
||||
mysql_stmt_prepare(thd, packet, packet_length);
|
||||
break;
|
||||
}
|
||||
case COM_STMT_CLOSE:
|
||||
@@ -1664,6 +1664,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
||||
char *packet_end= thd->query + thd->query_length;
|
||||
mysql_log.write(thd,command,"%s",thd->query);
|
||||
DBUG_PRINT("query",("%-.4096s",thd->query));
|
||||
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
|
||||
|
||||
mysql_parse(thd,thd->query, thd->query_length);
|
||||
|
||||
while (!thd->killed && thd->lex->found_semicolon && !thd->net.report_error)
|
||||
@@ -2220,7 +2224,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
|
||||
TRUE error; In this case thd->fatal_error is set
|
||||
*/
|
||||
|
||||
bool alloc_query(THD *thd, char *packet, ulong packet_length)
|
||||
bool alloc_query(THD *thd, const char *packet, uint packet_length)
|
||||
{
|
||||
packet_length--; // Remove end null
|
||||
/* Remove garbage at start and end of query */
|
||||
@@ -2229,7 +2233,7 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length)
|
||||
packet++;
|
||||
packet_length--;
|
||||
}
|
||||
char *pos=packet+packet_length; // Point at end null
|
||||
const char *pos= packet + packet_length; // Point at end null
|
||||
while (packet_length > 0 &&
|
||||
(pos[-1] == ';' || my_isspace(thd->charset() ,pos[-1])))
|
||||
{
|
||||
@@ -2250,8 +2254,6 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length)
|
||||
thd->packet.shrink(thd->variables.net_buffer_length);
|
||||
thd->convert_buffer.shrink(thd->variables.net_buffer_length);
|
||||
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -2466,112 +2468,17 @@ mysql_execute_command(THD *thd)
|
||||
}
|
||||
case SQLCOM_PREPARE:
|
||||
{
|
||||
char *query_str;
|
||||
uint query_len;
|
||||
if (lex->prepared_stmt_code_is_varref)
|
||||
{
|
||||
/* This is PREPARE stmt FROM @var. */
|
||||
String str;
|
||||
CHARSET_INFO *to_cs= thd->variables.collation_connection;
|
||||
bool need_conversion;
|
||||
user_var_entry *entry;
|
||||
String *pstr= &str;
|
||||
uint32 unused;
|
||||
/*
|
||||
Convert @var contents to string in connection character set. Although
|
||||
it is known that int/real/NULL value cannot be a valid query we still
|
||||
convert it for error messages to uniform.
|
||||
*/
|
||||
if ((entry=
|
||||
(user_var_entry*)hash_search(&thd->user_vars,
|
||||
(byte*)lex->prepared_stmt_code.str,
|
||||
lex->prepared_stmt_code.length))
|
||||
&& entry->value)
|
||||
{
|
||||
my_bool is_var_null;
|
||||
pstr= entry->val_str(&is_var_null, &str, NOT_FIXED_DEC);
|
||||
/*
|
||||
NULL value of variable checked early as entry->value so here
|
||||
we can't get NULL in normal conditions
|
||||
*/
|
||||
DBUG_ASSERT(!is_var_null);
|
||||
if (!pstr)
|
||||
goto error;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
variable absent or equal to NULL, so we need to set variable to
|
||||
something reasonable to get readable error message during parsing
|
||||
*/
|
||||
str.set("NULL", 4, &my_charset_latin1);
|
||||
}
|
||||
|
||||
need_conversion=
|
||||
String::needs_conversion(pstr->length(), pstr->charset(),
|
||||
to_cs, &unused);
|
||||
|
||||
query_len= need_conversion? (pstr->length() * to_cs->mbmaxlen) :
|
||||
pstr->length();
|
||||
if (!(query_str= alloc_root(thd->mem_root, query_len+1)))
|
||||
goto error;
|
||||
|
||||
if (need_conversion)
|
||||
{
|
||||
uint dummy_errors;
|
||||
query_len= copy_and_convert(query_str, query_len, to_cs,
|
||||
pstr->ptr(), pstr->length(),
|
||||
pstr->charset(), &dummy_errors);
|
||||
}
|
||||
else
|
||||
memcpy(query_str, pstr->ptr(), pstr->length());
|
||||
query_str[query_len]= 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
query_str= lex->prepared_stmt_code.str;
|
||||
query_len= lex->prepared_stmt_code.length;
|
||||
DBUG_PRINT("info", ("PREPARE: %.*s FROM '%.*s' \n",
|
||||
lex->prepared_stmt_name.length,
|
||||
lex->prepared_stmt_name.str,
|
||||
query_len, query_str));
|
||||
}
|
||||
thd->command= COM_STMT_PREPARE;
|
||||
if (!(res= mysql_stmt_prepare(thd, query_str, query_len + 1,
|
||||
&lex->prepared_stmt_name)))
|
||||
send_ok(thd, 0L, 0L, "Statement prepared");
|
||||
mysql_sql_stmt_prepare(thd);
|
||||
break;
|
||||
}
|
||||
case SQLCOM_EXECUTE:
|
||||
{
|
||||
DBUG_PRINT("info", ("EXECUTE: %.*s\n",
|
||||
lex->prepared_stmt_name.length,
|
||||
lex->prepared_stmt_name.str));
|
||||
mysql_sql_stmt_execute(thd, &lex->prepared_stmt_name);
|
||||
lex->prepared_stmt_params.empty();
|
||||
mysql_sql_stmt_execute(thd);
|
||||
break;
|
||||
}
|
||||
case SQLCOM_DEALLOCATE_PREPARE:
|
||||
{
|
||||
Statement* stmt;
|
||||
DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s\n",
|
||||
lex->prepared_stmt_name.length,
|
||||
lex->prepared_stmt_name.str));
|
||||
/* We account deallocate in the same manner as mysql_stmt_close */
|
||||
statistic_increment(thd->status_var.com_stmt_close, &LOCK_status);
|
||||
if ((stmt= thd->stmt_map.find_by_name(&lex->prepared_stmt_name)))
|
||||
{
|
||||
thd->stmt_map.erase(stmt);
|
||||
send_ok(thd);
|
||||
}
|
||||
else
|
||||
{
|
||||
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0),
|
||||
lex->prepared_stmt_name.length,
|
||||
lex->prepared_stmt_name.str,
|
||||
"DEALLOCATE PREPARE");
|
||||
goto error;
|
||||
}
|
||||
mysql_sql_stmt_close(thd);
|
||||
break;
|
||||
}
|
||||
case SQLCOM_DO:
|
||||
@@ -4124,7 +4031,7 @@ end_with_restore_list:
|
||||
}
|
||||
#endif
|
||||
if (lex->sphead->m_type == TYPE_ENUM_FUNCTION &&
|
||||
!lex->sphead->m_has_return)
|
||||
!(lex->sphead->m_flags & sp_head::HAS_RETURN))
|
||||
{
|
||||
my_error(ER_SP_NORETURN, MYF(0), name);
|
||||
delete lex->sphead;
|
||||
@@ -4213,15 +4120,31 @@ end_with_restore_list:
|
||||
ha_rows select_limit;
|
||||
/* bits that should be cleared in thd->server_status */
|
||||
uint bits_to_be_cleared= 0;
|
||||
/*
|
||||
Check that the stored procedure doesn't contain Dynamic SQL
|
||||
and doesn't return result sets: such stored procedures can't
|
||||
be called from a function or trigger.
|
||||
*/
|
||||
if (thd->in_sub_stmt)
|
||||
{
|
||||
const char *where= (thd->in_sub_stmt & SUB_STMT_TRIGGER ?
|
||||
"trigger" : "function");
|
||||
if (sp->is_not_allowed_in_function(where))
|
||||
goto error;
|
||||
}
|
||||
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
my_bool nsok= thd->net.no_send_ok;
|
||||
thd->net.no_send_ok= TRUE;
|
||||
#endif
|
||||
if (sp->m_multi_results)
|
||||
if (sp->m_flags & sp_head::MULTI_RESULTS)
|
||||
{
|
||||
if (! (thd->client_capabilities & CLIENT_MULTI_RESULTS))
|
||||
{
|
||||
/*
|
||||
The client does not support multiple result sets being sent
|
||||
back
|
||||
*/
|
||||
my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str);
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
thd->net.no_send_ok= nsok;
|
||||
@@ -4265,7 +4188,7 @@ end_with_restore_list:
|
||||
thd->row_count_func= 0;
|
||||
|
||||
/*
|
||||
We never write CALL statements int binlog:
|
||||
We never write CALL statements into binlog:
|
||||
- If the mode is non-prelocked, each statement will be logged
|
||||
separately.
|
||||
- If the mode is prelocked, the invoking statement will care
|
||||
|
||||
Reference in New Issue
Block a user