summaryrefslogtreecommitdiff
path: root/sql/sql_select.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_select.cc')
-rw-r--r--sql/sql_select.cc209
1 files changed, 207 insertions, 2 deletions
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 2a5629237e5..ccccd69516c 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1430,7 +1430,7 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num,
}
}
- if (setup_fields(thd, ref_ptrs, fields_list, MARK_COLUMNS_READ,
+ if (setup_fields(thd, ref_ptrs, fields_list, select_lex->item_list_usage,
&all_fields, &select_lex->pre_fix, 1))
DBUG_RETURN(-1);
thd->lex->current_select->context_analysis_place= save_place;
@@ -1720,6 +1720,8 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num,
if (!procedure && result && result->prepare(fields_list, unit_arg))
goto err; /* purecov: inspected */
+ select_lex->where_cond_after_prepare= conds;
+
unit= unit_arg;
if (prepare_stage2())
goto err;
@@ -29028,7 +29030,8 @@ static bool get_range_limit_read_cost(const JOIN_TAB *tab,
@note
This function takes into account table->opt_range_condition_rows statistic
(that is calculated by the make_join_statistics function).
- However, single table procedures such as mysql_update() and mysql_delete()
+ However, single table procedures such as Sql_cmd_update:update_single_table()
+ and Sql_cmd_delete::delete_single_table()
never call make_join_statistics, so they have to update it manually
(@see get_index_for_order()).
*/
@@ -30463,6 +30466,208 @@ static bool process_direct_rownum_comparison(THD *thd, SELECT_LEX_UNIT *unit,
}
+static void MYSQL_DML_START(THD *thd)
+{
+ switch (thd->lex->sql_command) {
+
+ case SQLCOM_UPDATE:
+ MYSQL_UPDATE_START(thd->query());
+ break;
+ case SQLCOM_UPDATE_MULTI:
+ MYSQL_MULTI_UPDATE_START(thd->query());
+ break;
+ case SQLCOM_DELETE:
+ MYSQL_DELETE_START(thd->query());
+ break;
+ case SQLCOM_DELETE_MULTI:
+ MYSQL_MULTI_DELETE_START(thd->query());
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+}
+
+
+static void MYSQL_DML_DONE(THD *thd, int rc)
+{
+ switch (thd->lex->sql_command) {
+
+ case SQLCOM_UPDATE:
+ MYSQL_UPDATE_DONE(
+ rc,
+ (rc ? 0 :
+ ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result()))
+ ->num_found()),
+ (rc ? 0 :
+ ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result()))
+ ->num_updated()));
+ break;
+ case SQLCOM_UPDATE_MULTI:
+ MYSQL_MULTI_UPDATE_DONE(
+ rc,
+ (rc ? 0 :
+ ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result()))
+ ->num_found()),
+ (rc ? 0 :
+ ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result()))
+ ->num_updated()));
+ break;
+ case SQLCOM_DELETE:
+ MYSQL_DELETE_DONE(rc, (rc ? 0 : (ulong) (thd->get_row_count_func())));
+ break;
+ case SQLCOM_DELETE_MULTI:
+ MYSQL_MULTI_DELETE_DONE(
+ rc,
+ (rc ? 0 :
+ ((multi_delete*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result()))
+ ->num_deleted()));
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+}
+
+bool Sql_cmd_dml::prepare(THD *thd)
+{
+ lex= thd->lex;
+ SELECT_LEX_UNIT *unit= &lex->unit;
+
+ DBUG_ASSERT(!is_prepared());
+
+ // Perform a coarse statement-specific privilege check.
+ if (precheck(thd))
+ goto err;
+
+ MYSQL_DML_START(thd);
+
+ lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED;
+
+ if (open_tables_for_query(thd, lex->query_tables, &table_count, 0,
+ get_dml_prelocking_strategy()))
+ {
+ if (thd->is_error())
+ goto err;
+ (void)unit->cleanup();
+ return true;
+ }
+
+ if (prepare_inner(thd))
+ goto err;
+
+ lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED;
+
+ set_prepared();
+ unit->set_prepared();
+
+ return false;
+
+err:
+ DBUG_ASSERT(thd->is_error());
+ DBUG_PRINT("info", ("report_error: %d", thd->is_error()));
+
+ (void)unit->cleanup();
+
+ return true;
+}
+
+bool Sql_cmd_dml::execute(THD *thd)
+{
+ lex = thd->lex;
+ bool res;
+
+ SELECT_LEX_UNIT *unit = &lex->unit;
+ SELECT_LEX *select_lex= lex->first_select_lex();
+
+ if (!is_prepared())
+ {
+ if (prepare(thd)) // this will call open_tables_for_query()
+ goto err;
+ }
+ else
+ {
+ if (precheck(thd))
+ goto err;
+
+ MYSQL_DML_START(thd);
+
+ if (open_tables_for_query(thd, lex->query_tables, &table_count, 0,
+ get_dml_prelocking_strategy()))
+ goto err;
+ }
+
+ THD_STAGE_INFO(thd, stage_init);
+
+ /*
+ Locking of tables is done after preparation but before optimization.
+ This allows to do better partition pruning and avoid locking unused
+ partitions. As a consequence, in such a case, prepare stage can rely only
+ on metadata about tables used and not data from them.
+ */
+ if (!is_empty_query())
+ {
+ if (lock_tables(thd, lex->query_tables, table_count, 0))
+ goto err;
+ }
+
+ unit->set_limit(select_lex);
+
+ // Perform statement-specific execution
+ res = execute_inner(thd);
+
+ if (res)
+ goto err;
+
+ res= unit->cleanup();
+
+ // "unprepare" this object since unit->cleanup actually unprepares
+ unprepare(thd);
+
+ THD_STAGE_INFO(thd, stage_end);
+
+ MYSQL_DML_DONE(thd, res);
+
+ return res;
+
+err:
+ DBUG_ASSERT(thd->is_error() || thd->killed);
+ MYSQL_DML_DONE(thd, 1);
+ THD_STAGE_INFO(thd, stage_end);
+ (void)unit->cleanup();
+
+ return thd->is_error();
+}
+
+
+bool Sql_cmd_dml::execute_inner(THD *thd)
+{
+ SELECT_LEX_UNIT *unit = &lex->unit;
+ SELECT_LEX *select_lex= unit->first_select();
+ JOIN *join= select_lex->join;
+
+ if (join->optimize())
+ goto err;
+
+ if (thd->lex->describe & DESCRIBE_EXTENDED)
+ {
+ join->conds_history= join->conds;
+ join->having_history= (join->having?join->having:join->tmp_having);
+ }
+
+ if (unlikely(thd->is_error()))
+ goto err;
+
+ join->exec();
+
+ if (thd->lex->describe & DESCRIBE_EXTENDED)
+ {
+ select_lex->where= join->conds_history;
+ select_lex->having= join->having_history;
+ }
+
+err:
+ return join->error;
+}
+
/**
@} (end of group Query_Optimizer)