Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions mysql-test/main/delete_use_source.result
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 8 100.00 Using where
1 PRIMARY t2 ref a a 5 test.t2.c1 1 100.00 Using index; FirstMatch(t2)
Warnings:
Note 1003 delete from `test`.`t1` `t2` using (`test`.`t2`) where `test`.`t2`.`a` = `test`.`t2`.`c1`
Note 1003 delete from `t2` using (`test`.`t2`) where `test`.`t2`.`a` = `test`.`t2`.`c1`
delete from t1 t2 where t2.c1 in (select a from t2);
select * from t1;
c1 c2 c3
Expand All @@ -368,7 +368,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t_x ALL NULL NULL NULL NULL 7 100.00 Using where
1 PRIMARY t_y ref a a 5 test.t_x.c2 1 100.00 Using index; FirstMatch(t_x)
Warnings:
Note 1003 delete from `test`.`t1` `t_x` using (`test`.`t2` `t_y`) where `test`.`t_y`.`a` = `test`.`t_x`.`c2`
Note 1003 delete from `t_x` using (`test`.`t2` `t_y`) where `test`.`t_y`.`a` = `test`.`t_x`.`c2`
delete from t1 t_x where t_x.c2 IN (select a from t2 as t_y);
select * from t1;
c1 c2 c3
Expand All @@ -383,7 +383,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t_x ALL NULL NULL NULL NULL 4 100.00 Using where
1 PRIMARY t_x ref a a 5 test.t_x.c3 1 100.00 Using index; FirstMatch(t_x)
Warnings:
Note 1003 delete from `test`.`t1` `t_x` using (`test`.`t2` `t_x`) where `test`.`t_x`.`a` = `test`.`t_x`.`c3`
Note 1003 delete from `t_x` using (`test`.`t2` `t_x`) where `test`.`t_x`.`a` = `test`.`t_x`.`c3`
delete from t1 as t_x where t_x.c3 IN (select a from t2 t_x);
select * from t1;
c1 c2 c3
Expand All @@ -397,7 +397,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 3 100.00
1 PRIMARY t1 index NULL a 5 NULL 3 33.33 Using where; Using index; FirstMatch(t2)
Warnings:
Note 1003 delete from `test`.`t1` `t2` using (`test`.`t2` `t1`) where `test`.`t2`.`c1` = `test`.`t1`.`a` - 1
Note 1003 delete from `t2` using (`test`.`t2` `t1`) where `test`.`t2`.`c1` = `test`.`t1`.`a` - 1
delete from t1 t2 where t2.c1 IN (select a -1 from t2 as t1);
select * from t1;
c1 c2 c3
Expand Down
12 changes: 6 additions & 6 deletions mysql-test/main/opt_hints_join_order.result
Original file line number Diff line number Diff line change
Expand Up @@ -893,7 +893,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY ta1 eq_ref PRIMARY PRIMARY 4 test.t2.f1 1 100.00 Using where
1 PRIMARY ta2 ALL NULL NULL NULL NULL 3 100.00
Warnings:
Note 1003 delete from `test`.`t1` `ta1` using `test`.`t2` semi join (`test`.`t3`) left join (`test`.`t1` `ta1` join `test`.`t1` `ta2`) on(`test`.`ta1`.`f1` = `test`.`t2`.`f1` and 1 and `test`.`t2`.`f1` is not null) where `test`.`t3`.`f1` = 9
Note 1003 delete from `ta1` using `test`.`t2` semi join (`test`.`t3`) left join (`test`.`t1` `ta1` join `test`.`t1` `ta2`) on(`test`.`ta1`.`f1` = `test`.`t2`.`f1` and 1 and `test`.`t2`.`f1` is not null) where `test`.`t3`.`f1` = 9
# Applicable hint
EXPLAIN EXTENDED DELETE /*+ JOIN_PREFIX(t2, t3, ta2) */
FROM ta1.* USING t1 AS ta1 JOIN t1 AS ta2 ON 1
Expand All @@ -906,7 +906,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY ta1 eq_ref PRIMARY PRIMARY 4 test.t2.f1 1 100.00
2 MATERIALIZED t3 ALL NULL NULL NULL NULL 3 33.33 Using where
Warnings:
Note 1003 delete /*+ JOIN_PREFIX(@`select#1` `t2`,`t3`,`ta2`) */ from `test`.`t1` `ta1` using `test`.`t2` semi join (`test`.`t3`) left join (`test`.`t1` `ta1` join `test`.`t1` `ta2`) on(`test`.`ta1`.`f1` = `test`.`t2`.`f1` and 1 and `test`.`t2`.`f1` is not null) where `test`.`t3`.`f1` = 9
Note 1003 delete /*+ JOIN_PREFIX(@`select#1` `t2`,`t3`,`ta2`) */ from `ta1` using `test`.`t2` semi join (`test`.`t3`) left join (`test`.`t1` `ta1` join `test`.`t1` `ta2`) on(`test`.`ta1`.`f1` = `test`.`t2`.`f1` and 1 and `test`.`t2`.`f1` is not null) where `test`.`t3`.`f1` = 9
# Applicable hint
EXPLAIN EXTENDED DELETE /*+ JOIN_PREFIX(t2, t3, ta1, ta2) */
FROM ta1.* USING t1 AS ta1 JOIN t1 AS ta2 ON 1
Expand All @@ -919,7 +919,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY ta2 ALL NULL NULL NULL NULL 3 100.00
2 MATERIALIZED t3 ALL NULL NULL NULL NULL 3 33.33 Using where
Warnings:
Note 1003 delete /*+ JOIN_PREFIX(@`select#1` `t2`,`t3`,`ta1`,`ta2`) */ from `test`.`t1` `ta1` using `test`.`t2` semi join (`test`.`t3`) left join (`test`.`t1` `ta1` join `test`.`t1` `ta2`) on(`test`.`ta1`.`f1` = `test`.`t2`.`f1` and 1 and `test`.`t2`.`f1` is not null) where `test`.`t3`.`f1` = 9
Note 1003 delete /*+ JOIN_PREFIX(@`select#1` `t2`,`t3`,`ta1`,`ta2`) */ from `ta1` using `test`.`t2` semi join (`test`.`t3`) left join (`test`.`t1` `ta1` join `test`.`t1` `ta2`) on(`test`.`ta1`.`f1` = `test`.`t2`.`f1` and 1 and `test`.`t2`.`f1` is not null) where `test`.`t3`.`f1` = 9
# Applicable hint
EXPLAIN EXTENDED DELETE /*+ JOIN_PREFIX(t2, t3, ta2, ta1) */
FROM ta1.* USING t1 AS ta1 JOIN t1 AS ta2 ON 1
Expand All @@ -932,7 +932,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY ta1 eq_ref PRIMARY PRIMARY 4 test.t2.f1 1 100.00
2 MATERIALIZED t3 ALL NULL NULL NULL NULL 3 33.33 Using where
Warnings:
Note 1003 delete /*+ JOIN_PREFIX(@`select#1` `t2`,`t3`,`ta2`,`ta1`) */ from `test`.`t1` `ta1` using `test`.`t2` semi join (`test`.`t3`) left join (`test`.`t1` `ta1` join `test`.`t1` `ta2`) on(`test`.`ta1`.`f1` = `test`.`t2`.`f1` and 1 and `test`.`t2`.`f1` is not null) where `test`.`t3`.`f1` = 9
Note 1003 delete /*+ JOIN_PREFIX(@`select#1` `t2`,`t3`,`ta2`,`ta1`) */ from `ta1` using `test`.`t2` semi join (`test`.`t3`) left join (`test`.`t1` `ta1` join `test`.`t1` `ta2`) on(`test`.`ta1`.`f1` = `test`.`t2`.`f1` and 1 and `test`.`t2`.`f1` is not null) where `test`.`t3`.`f1` = 9
# Hint should be ignored
EXPLAIN EXTENDED DELETE /*+ JOIN_SUFFIX(ta2, t3, ta1) */
FROM ta1.* USING t1 AS ta1 JOIN t1 AS ta2 ON 1
Expand All @@ -945,7 +945,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY ta2 ALL NULL NULL NULL NULL 3 100.00
Warnings:
Warning 4219 Hint JOIN_SUFFIX(`ta2`,`t3`,`ta1`) is ignored as conflicting/duplicated
Note 1003 delete from `test`.`t1` `ta1` using `test`.`t2` semi join (`test`.`t3`) left join (`test`.`t1` `ta1` join `test`.`t1` `ta2`) on(`test`.`ta1`.`f1` = `test`.`t2`.`f1` and 1 and `test`.`t2`.`f1` is not null) where `test`.`t3`.`f1` = 9
Note 1003 delete from `ta1` using `test`.`t2` semi join (`test`.`t3`) left join (`test`.`t1` `ta1` join `test`.`t1` `ta2`) on(`test`.`ta1`.`f1` = `test`.`t2`.`f1` and 1 and `test`.`t2`.`f1` is not null) where `test`.`t3`.`f1` = 9
# Both hints are ignored
EXPLAIN EXTENDED DELETE /*+ JOIN_PREFIX(ta1, t2, t3) JOIN_SUFFIX(t3, ta2) */
FROM ta1.* USING t1 AS ta1 JOIN t1 AS ta2 ON 1
Expand All @@ -959,7 +959,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
Warnings:
Warning 4219 Hint JOIN_PREFIX(`ta1`,`t2`,`t3`) is ignored as conflicting/duplicated
Warning 4219 Hint JOIN_SUFFIX(`t3`,`ta2`) is ignored as conflicting/duplicated
Note 1003 delete from `test`.`t1` `ta1` using `test`.`t2` semi join (`test`.`t3`) left join (`test`.`t1` `ta1` join `test`.`t1` `ta2`) on(`test`.`ta1`.`f1` = `test`.`t2`.`f1` and 1 and `test`.`t2`.`f1` is not null) where `test`.`t3`.`f1` = 9
Note 1003 delete from `ta1` using `test`.`t2` semi join (`test`.`t3`) left join (`test`.`t1` `ta1` join `test`.`t1` `ta2`) on(`test`.`ta1`.`f1` = `test`.`t2`.`f1` and 1 and `test`.`t2`.`f1` is not null) where `test`.`t3`.`f1` = 9
DROP TABLE t1, t2, t3;
# Const table behavior, table order is not changed, hint is applicable.
# Note: Const tables are excluded from the process of dependency setting
Expand Down
75 changes: 70 additions & 5 deletions sql/select_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,27 @@


select_handler::select_handler(THD *thd_arg, handlerton *ht_arg,
SELECT_LEX *sel_lex)
SELECT_LEX *sel_lex,
select_handler_type type_arg)
: select_lex(sel_lex), lex_unit(nullptr), table(nullptr),
thd(thd_arg), ht(ht_arg), result(sel_lex->join->result),
is_analyze(thd_arg->lex->analyze_stmt)
is_analyze(thd_arg->lex->analyze_stmt), handler_type(type_arg)
{}

select_handler::select_handler(THD *thd_arg, handlerton *ht_arg,
SELECT_LEX_UNIT *sel_unit)
: select_lex(nullptr), lex_unit(sel_unit), table(nullptr),
thd(thd_arg), ht(ht_arg), result(sel_unit->result),
is_analyze(thd_arg->lex->analyze_stmt)
is_analyze(thd_arg->lex->analyze_stmt),
handler_type(select_handler_type::SELECT)
{}

select_handler::select_handler(THD *thd_arg, handlerton *ht_arg,
SELECT_LEX *sel_lex, SELECT_LEX_UNIT *sel_unit)
: select_lex(sel_lex), lex_unit(sel_unit), table(nullptr), thd(thd_arg),
ht(ht_arg), result(sel_lex->join->result),
is_analyze(thd_arg->lex->analyze_stmt)
is_analyze(thd_arg->lex->analyze_stmt),
handler_type(select_handler_type::SELECT)
{}

select_handler::~select_handler()
Expand Down Expand Up @@ -106,6 +109,12 @@ TABLE *select_handler::create_tmp_table(THD *thd)
bool select_handler::prepare()
{
DBUG_ENTER("select_handler::prepare");
/*
A multi-table UPDATE/DELETE handler produces no rows, so it needs
neither a record buffer nor a list of result columns.
*/
if (is_update_delete_handler())
DBUG_RETURN(false);
/*
Some engines (e.g. XPand) initialize "table" on their own.
So we need to create a temporary table only if "table" is NULL.
Expand Down Expand Up @@ -152,10 +161,66 @@ bool select_handler::send_eof()


int select_handler::execute()
{
DBUG_ENTER("select_handler::execute");
DBUG_RETURN(is_update_delete_handler() ? execute_update_delete() :
execute_select());
}


/*
Default implementation of the batch_update_delete() interface function.
An engine that creates a select_handler_type::UPDATE_DELETE handler must
provide its own.
*/

int select_handler::batch_update_delete(ha_rows *found_rows,
ha_rows *affected_rows)
{
DBUG_ASSERT(0);
return HA_ERR_WRONG_COMMAND;
}


/*
Execute a multi-table UPDATE/DELETE that was pushed down into the engine.

The engine does the whole job and only reports the row counts. The SQL
layer still has to do the final part of the statement: invalidate the
query cache, write the statement to the binary log and send the OK packet
to the client. All of that is done by multi_update::send_eof() /
multi_delete::send_eof(), so the counters are passed to the result object
and send_eof() is called as if the rows had been updated locally.
*/

int select_handler::execute_update_delete()
{
int err;
ha_rows found_rows= 0, affected_rows= 0;

DBUG_ENTER("select_handler::execute");
DBUG_ENTER("select_handler::execute_update_delete");

if ((err= batch_update_delete(&found_rows, &affected_rows)))
{
if (!thd->is_error())
print_error(err, MYF(0));
DBUG_RETURN(-1);
}

result->direct_update_delete_done(found_rows, affected_rows);

if (send_eof())
DBUG_RETURN(-1);

DBUG_RETURN(0);
}


int select_handler::execute_select()
{
int err;

DBUG_ENTER("select_handler::execute_select");

if ((err= init_scan()))
goto error;
Expand Down
52 changes: 51 additions & 1 deletion sql/select_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,29 @@ enum class select_pushdown_type {
WHOLE_UNIT
};


/*
What kind of statement a select_handler object takes over.

SELECT
The handler produces a result set. The SQL layer retrieves the rows
one by one with init_scan()/next_row()/end_scan() and sends them to
the client.

UPDATE_DELETE
The handler performs a multi-table UPDATE or DELETE. There is no
result set and no "primary" handler object to call
handler::direct_update_rows_init()/direct_delete_rows_init() on, so
the whole statement is handed over to the engine at once with
batch_update_delete(). init_scan()/next_row()/end_scan() are never
called for such a handler.
*/
enum class select_handler_type
{
SELECT,
UPDATE_DELETE
};

/**
@class select_handler

Expand All @@ -37,7 +60,8 @@ class select_handler
{
public:
// Constructor for a single SELECT_LEX (not a part of a unit)
select_handler(THD *thd_arg, handlerton *ht_arg, SELECT_LEX *sel_lex);
select_handler(THD *thd_arg, handlerton *ht_arg, SELECT_LEX *sel_lex,
select_handler_type type_arg= select_handler_type::SELECT);

// Constructor for a unit (UNION/EXCEPT/INTERSECT)
select_handler(THD *thd_arg, handlerton *ht_arg, SELECT_LEX_UNIT *sel_unit);
Expand All @@ -55,6 +79,9 @@ class select_handler

virtual bool prepare();

bool is_update_delete_handler() const
{ return handler_type == select_handler_type::UPDATE_DELETE; }

/*
Select_handler processes these cases:
- single SELECT
Expand All @@ -81,6 +108,7 @@ class select_handler
/*
Functions to scan the select result set.
All these returns 0 if ok, error code in case of error.
They are called only for select_handler_type::SELECT handlers.
*/

/* Initialize the process of producing rows of result set */
Expand All @@ -96,6 +124,20 @@ class select_handler
/* Finish scanning */
virtual int end_scan() = 0;

/*
Perform the whole multi-table UPDATE/DELETE statement and report how many
rows matched the WHERE clause (*found_rows) and how many rows were really
changed or deleted (*affected_rows).

This is called instead of the init_scan()/next_row()/end_scan() loop for
select_handler_type::UPDATE_DELETE handlers, so an engine that creates
such a handler must implement it.

@retval 0 ok
@retval != 0 error code
*/
virtual int batch_update_delete(ha_rows *found_rows, ha_rows *affected_rows);

/* Report errors */
virtual void print_error(int error, myf errflag);

Expand All @@ -114,6 +156,14 @@ class select_handler
List<Item> result_columns;

bool is_analyze;

/* Which kind of statement this handler takes over */
select_handler_type handler_type;

private:

int execute_select();
int execute_update_delete();
};

#endif /* SELECT_HANDLER_INCLUDED */
11 changes: 11 additions & 0 deletions sql/select_result.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,17 @@ class select_result :public select_result_sink
@retval TRUE error, an error message is set
*/
virtual bool check_simple_select() const;
/*
A multi-table UPDATE/DELETE was performed by the storage engine itself,
see select_handler_type::UPDATE_DELETE. Tell the result object how many
rows matched the WHERE clause and how many rows were really changed, so
that the following send_eof() reports them instead of running the
SQL-layer update/delete loop.
Only multi_update and multi_delete can receive this call.
*/
virtual void direct_update_delete_done(ha_rows found_rows,
ha_rows affected_rows)
{ DBUG_ASSERT(0); }
virtual void abort_result_set() {}
virtual void reset_for_next_ps_execution();
void set_thd(THD *thd_arg) { thd= thd_arg; }
Expand Down
10 changes: 9 additions & 1 deletion sql/sql_class.h
Original file line number Diff line number Diff line change
Expand Up @@ -7832,6 +7832,8 @@ class multi_delete :public select_result_interceptor
so that afterward abort_result_set() needs to find out that.
*/
bool error_handled;
/* True if the engine has performed the whole DELETE, see select_handler */
bool direct_dml_done;

public:
// Methods used by ColumnStore
Expand All @@ -7849,6 +7851,8 @@ class multi_delete :public select_result_interceptor
int do_table_deletes(TABLE *table, SORT_INFO *sort_info, bool ignore);
bool send_eof() override;
inline ha_rows num_deleted() const { return deleted; }
void direct_update_delete_done(ha_rows found_rows,
ha_rows affected_rows) override;
void abort_result_set() override;
void prepare_to_read_rows() override;
};
Expand Down Expand Up @@ -7883,7 +7887,9 @@ class multi_update :public select_result_interceptor
so that afterward abort_result_set() needs to find out that.
*/
bool error_handled;

/* True if the engine has performed the whole UPDATE, see select_handler */
bool direct_dml_done;

/* Need this to protect against multiple prepare() calls */
bool prepared;

Expand All @@ -7910,6 +7916,8 @@ class multi_update :public select_result_interceptor
inline ha_rows num_updated() const { return updated; }
inline void set_found (ha_rows n) { found= n; }
inline void set_updated (ha_rows n) { updated= n; }
void direct_update_delete_done(ha_rows found_rows,
ha_rows affected_rows) override;
virtual void abort_result_set() override;
void update_used_tables() override;
void prepare_to_read_rows() override;
Expand Down
Loading