In order to call mysql_execute_command (the function that executes a statement) for a prepared statement and not damage its parse tree, we backup and restore the active Query_arena of THD.
We don't want the garbage created during execution to be left in the permanent arena of the statement. To ensure that, every statement is executed in the runtime arena of
THD. In other words, the arena which was active when
mysql_stmt_executewas called is used as the runtime arena of the statement during its execution.
allocate thd->querywith parameter markers ('?') replaced with their values: the new query is allocated in the runtime arena. We'll need this query for general, binary, error and slow logs.
The execution plan created at prepare stage is not saved (see Section 17.2, “Preparation of a Prepared Statement”), and at execute we simply create a new set of JOINs and then prepare and optimize it. During the first execution of the prepared statement the server may perform non-destructive transformations of statement's parsed tree: normally that would belong to a separate step executed at statement prepare, but once again, this haven't been done in 4.1 or 5.0. Such transformations absolutely must use the permanent arena of the prepared statement (saved in
thd->stmt_arena). Whenever we need to perform a permanent transformation, we first call
THD::activate_stmt_arena_if_neededto make the permanent arena active, transform the tree, and restore the runtime arena. To avoid double transformations in such cases, we track current state of the parsed tree in
This state may be one of the following:
INITIALIZED— we're in statement
INITIALIZED_FOR_SP— we're in first execution of a stored procedure statement.
PREPARED— we're in first execution of a prepared statement.
EXECUTED— we're in a subsequent execution of a prepared statement or a stored procedure statement.
CONVENTIONAL_EXECUTION— we're executing a pre-4.1 query.
One can use helper methods of
Query_arenato check this state (
st_select_lex_unit::first_executioncontains a flag for the state of each subquery in a complex statement. A separate variable is needed because not all subqueries may get executed during the first execution of a statement.
Some optimizations damage the parsed tree, for example, replace leaves and subtrees of items with other items or leave item objects cluttered with runtime data. To allow re-execution of a prepared statement the following mechanisms are currently employed:
A hierarchy of
st_select_lex::cleanup()methods to restore the parsed tree to the condition of right-after-parse. These cleanups are called in
Prepared_statement::cleanup_stmt()after the statement has been executed.
In order to roll back destructive transformations of the parsed tree, every replacement of one item with another is registered in
THD::change_item_tree(). In the end of execution all such changes are rolled back in reverse order.
if (!(fld= new Item_field(from_field))) goto error; thd->change_item_tree(reference, fld);
If a transformation is a non-destructive, it should not be registered, but performed only once in the permanent memory root. Additionally, be careful to not supply a pointer to stack as the first argument of
change_item_tree(); that will lead to stack corruption when a tree is restored.
ONclauses are created anew for each execution. It was easier to implement in 4.1, and the approach with change record list used in (b) could not have been used for
ORtransformations, because these transformations not only replace one item with another, but also can remove a complete subtree. Leafs of
ORsubtrees are not copied by this mechanism because currently they are not damaged by the transformation. For details, see
No other mechanism exists in the server at the moment to allow re-execution. If the code that you're adding transforms the parsed tree, you must use one of the mechanisms described above, or propose and implement a better approach.
When execution is done, we rollback the damage of the parsed tree.