Skip to content

Commit cf50e13

Browse files
montywivuvova
authored andcommitted
MDEV-6080: Allowing storage engine to shortcut group by queries
This task is to allow storage engines that can execute GROUP BY or summary queries efficiently to intercept a full query or sub query from MariaDB and deliver the result either to the client or to a temporary table for further processing. - Added code in sql_select.cc to intercept GROUP BY queries. Creation of group_by_handler is done after all optimizations to allow storage engine to benefit of an optimized WHERE clause and suggested indexes to use. - Added group by handler to sequence engine and a group_by test suite as a way to test the new interface. - Intercept EXPLAIN with a message "Storage engine handles GROUP BY" libmysqld/CMakeLists.txt: Added new group_by_handler files sql/CMakeLists.txt: Added new group_by_handler files sql/group_by_handler.cc: Implementation of group_by_handler functions sql/group_by_handler.h: Definition of group_by_handler class sql/handler.h: Added handlerton function to create a group_by_handler, if the storage engine can intercept the query. sql/item_cmpfunc.cc: Allow one to evaluate item_equal any time. sql/sql_select.cc: Added code to intercept GROUP BY queries - If all tables are from the same storage engine and the query is using sum functions, call create_group_by() to check if the storage engine can intercept the query. - If yes: - create a temporary table to hold a GROUP_BY row or result - In do_select() intercept normal query execution by instead calling the group_by_handler to get the result - Intercept EXPLAIN sql/sql_select.h: Added handling of group_by_handler Added caching of the original join tab (needed for cleanup after group_by handler) storage/sequence/mysql-test/sequence/group_by.result: Test group_by_handler interface storage/sequence/mysql-test/sequence/group_by.test: Test group_by_handler interface storage/sequence/sequence.cc: Added simple group_by_engine for handling COUNT(*) and SUM(primary_key). This was done as a test of the group_by_handler interface
1 parent d8df2b9 commit cf50e13

File tree

10 files changed

+761
-5
lines changed

10 files changed

+761
-5
lines changed

libmysqld/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
7575
../sql/debug_sync.cc ../sql/opt_table_elimination.cc
7676
../sql/sql_prepare.cc ../sql/sql_rename.cc ../sql/sql_repl.cc
7777
../sql/sql_select.cc ../sql/sql_servers.cc
78+
../sql/group_by_handler.cc
7879
../sql/sql_show.cc ../sql/sql_state.c
7980
../sql/sql_statistics.cc ../sql/sql_string.cc
8081
../sql/sql_tablespace.cc ../sql/sql_table.cc ../sql/sql_test.cc

sql/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ SET (SQL_SOURCE
108108
sql_partition.cc sql_plugin.cc sql_prepare.cc sql_rename.cc
109109
debug_sync.cc debug_sync.h
110110
sql_repl.cc sql_select.cc sql_show.cc sql_state.c
111+
group_by_handler.cc
111112
sql_statistics.cc sql_string.cc
112113
sql_table.cc sql_test.cc sql_trigger.cc sql_udf.cc sql_union.cc
113114
sql_update.cc sql_view.cc strfunc.cc table.cc thr_malloc.cc

sql/group_by_handler.cc

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
Copyright (c) 2014, SkySQL Ab & MariaDB Foundation
3+
4+
This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation; version 2 of the License.
7+
8+
This program is distributed in the hope that it will be useful,
9+
but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
GNU General Public License for more details.
12+
13+
You should have received a copy of the GNU General Public License
14+
along with this program; if not, write to the Free Software
15+
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
16+
17+
/*
18+
This file implements the group_by_handler code. This interface
19+
can be used by storage handlers that can intercept summary or GROUP
20+
BY queries from MariaDB and itself return the result to the user or
21+
upper level.
22+
*/
23+
24+
#ifdef USE_PRAGMA_IMPLEMENTATION
25+
#pragma implementation // gcc: Class implementation
26+
#endif
27+
28+
#include "sql_priv.h"
29+
#include "sql_select.h"
30+
31+
/*
32+
Same return values as do_select();
33+
34+
@retval
35+
0 if ok
36+
@retval
37+
1 if error is sent
38+
@retval
39+
-1 if error should be sent
40+
*/
41+
42+
int group_by_handler::execute(JOIN *join)
43+
{
44+
int err;
45+
ha_rows max_limit;
46+
ha_rows *reset_limit= 0;
47+
Item **reset_item= 0;
48+
DBUG_ENTER("group_by_handler");
49+
50+
if ((err= init_scan()))
51+
goto error;
52+
53+
if (store_data_in_temp_table)
54+
{
55+
max_limit= join->tmp_table_param.end_write_records;
56+
reset_limit= &join->unit->select_limit_cnt;
57+
}
58+
else
59+
{
60+
max_limit= join->unit->select_limit_cnt;
61+
if (join->unit->fake_select_lex)
62+
reset_item= &join->unit->fake_select_lex->select_limit;
63+
}
64+
65+
while (!(err= next_row()))
66+
{
67+
if (thd->check_killed())
68+
{
69+
thd->send_kill_message();
70+
(void) end_scan();
71+
DBUG_RETURN(-1);
72+
}
73+
74+
/* Check if we can accept the row */
75+
if (!having || having->val_bool())
76+
{
77+
if (store_data_in_temp_table)
78+
{
79+
if ((err= table->file->ha_write_tmp_row(table->record[0])))
80+
{
81+
bool is_duplicate;
82+
if (!table->file->is_fatal_error(err, HA_CHECK_DUP))
83+
continue; // Distinct elimination
84+
85+
if (create_internal_tmp_table_from_heap(thd, table,
86+
join->tmp_table_param.
87+
start_recinfo,
88+
&join->tmp_table_param.
89+
recinfo,
90+
err, 1, &is_duplicate))
91+
DBUG_RETURN(1);
92+
if (is_duplicate)
93+
continue;
94+
}
95+
}
96+
else
97+
{
98+
if (join->do_send_rows)
99+
{
100+
int error;
101+
/* result < 0 if row was not accepted and should not be counted */
102+
if ((error= join->result->send_data(*join->fields)))
103+
{
104+
(void) end_scan();
105+
DBUG_RETURN(error < 0 ? 0 : -1);
106+
}
107+
}
108+
}
109+
110+
/* limit handling */
111+
if (++join->send_records >= max_limit && join->do_send_rows)
112+
{
113+
if (!(join->select_options & OPTION_FOUND_ROWS))
114+
break; // LIMIT reached
115+
join->do_send_rows= 0; // Calculate FOUND_ROWS()
116+
if (reset_limit)
117+
*reset_limit= HA_POS_ERROR;
118+
if (reset_item)
119+
*reset_item= 0;
120+
}
121+
}
122+
}
123+
if (err != 0 && err != HA_ERR_END_OF_FILE)
124+
goto error;
125+
126+
if ((err= end_scan()))
127+
goto error_2;
128+
if (!store_data_in_temp_table && join->result->send_eof())
129+
DBUG_RETURN(1); // Don't send error to client
130+
131+
DBUG_RETURN(0);
132+
133+
error:
134+
(void) end_scan();
135+
error_2:
136+
print_error(err, MYF(0));
137+
DBUG_RETURN(-1); // Error not sent to client
138+
}
139+
140+
141+
void group_by_handler::print_error(int error, myf errflag)
142+
{
143+
my_error(ER_GET_ERRNO, MYF(0), error, hton_name(ht)->str);
144+
}

sql/group_by_handler.h

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
Copyright (c) 2014, SkySQL Ab & MariaDB Foundation
3+
4+
This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation; version 2 of the License.
7+
8+
This program is distributed in the hope that it will be useful,
9+
but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
GNU General Public License for more details.
12+
13+
You should have received a copy of the GNU General Public License
14+
along with this program; if not, write to the Free Software
15+
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
16+
17+
/*
18+
This file implements the group_by_handler interface. This interface
19+
can be used by storage handlers that can intercept summary or GROUP
20+
BY queries from MariaDB and itself return the result to the user or
21+
upper level.
22+
23+
Both main and sub queries are supported. Here are some examples of what the
24+
storage engine could intersept:
25+
26+
SELECT count(*) FROM t1;
27+
SELECT a,count(*) FROM t1 group by a;
28+
SELECT a,count(*) as sum FROM t1 where b > 10 group by a, order by sum;
29+
SELECT a,count(*) FROM t1,t2;
30+
SELECT a, (select sum(*) from t2 where t1.a=t2.a) from t2;
31+
32+
See https://mariadb.atlassian.net/browse/MDEV-6080 for more information.
33+
*/
34+
35+
class JOIN;
36+
37+
class group_by_handler
38+
{
39+
public:
40+
/* Arguments for group_by_handler, for usage later */
41+
THD *thd;
42+
SELECT_LEX *select_lex;
43+
List<Item> *fields;
44+
TABLE_LIST *table_list;
45+
ORDER *group_by, *order_by;
46+
Item *where, *having;
47+
handlerton *ht; /* storage engine of this handler */
48+
49+
/*
50+
Bit's of things the storage engine can do for this query.
51+
Should be initialized on object creation.
52+
*/
53+
/* Temporary table where all results should be stored in record[0] */
54+
TABLE *table;
55+
56+
bool store_data_in_temp_table; /* Set by mariadb */
57+
58+
group_by_handler(THD *thd_arg, SELECT_LEX *select_lex_arg,
59+
List<Item> *fields_arg,
60+
TABLE_LIST *table_list_arg, ORDER *group_by_arg,
61+
ORDER *order_by_arg, Item *where_arg,
62+
Item *having_arg, handlerton *ht_arg)
63+
: thd(thd_arg), select_lex(select_lex_arg), fields(fields_arg),
64+
table_list(table_list_arg), group_by(group_by_arg),
65+
order_by(order_by_arg), where(where_arg), having(having_arg),
66+
ht(ht_arg), table(0), store_data_in_temp_table(0)
67+
{}
68+
virtual ~group_by_handler() {}
69+
70+
/*
71+
Store pointer to temporary table and objects modified to point to
72+
the temporary table. This will happen during the optimize phase.
73+
74+
We provide new 'having' and 'order_by' elements here. The differ from the
75+
original ones in that these are modified to point to fields in the
76+
temporary table 'table'.
77+
78+
Return 1 if the storage handler cannot handle the GROUP BY after all,
79+
in which case we have to give an error to the end user for the query.
80+
This is becasue we can't revert back the old having and order_by elements.
81+
*/
82+
83+
virtual bool init(TABLE *temporary_table, Item *having_arg,
84+
ORDER *order_by_arg)
85+
{
86+
table= temporary_table;
87+
having= having_arg;
88+
order_by= order_by_arg;
89+
return 0;
90+
}
91+
92+
/*
93+
Result data is sorted by the storage engine according to order_by (if it
94+
exists) else according to the group_by. If this is not specified,
95+
MariaDB will store the result set into the temporary table and sort the
96+
result.
97+
*/
98+
#define GROUP_BY_ORDER_BY 1
99+
/* The storage engine can handle DISTINCT */
100+
#define GROUP_BY_DISTINCT 2
101+
virtual uint flags() { return 0; }
102+
103+
/*
104+
Functions to scan data. All these returns 0 if ok, error code in case
105+
of error
106+
*/
107+
108+
/*
109+
Initialize group_by scan, prepare for next_row().
110+
If this is a sub query with group by, this can be called many times for
111+
a query.
112+
*/
113+
virtual int init_scan()= 0;
114+
115+
/*
116+
Return next group by result in table->record[0].
117+
Return 0 if row found, HA_ERR_END_OF_FILE if last row and other error
118+
number in case of fatal error.
119+
*/
120+
virtual int next_row()= 0;
121+
122+
/* End scanning */
123+
virtual int end_scan()=0;
124+
125+
/* Information for optimizer (used by EXPLAIN) */
126+
virtual int info(uint flag, ha_statistics *stats)= 0;
127+
128+
/* Function that calls the above scan functions */
129+
int execute(JOIN *join);
130+
131+
/* Report errors */
132+
virtual void print_error(int error, myf errflag);
133+
};

sql/handler.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,10 @@ struct handler_iterator {
949949
};
950950

951951
class handler;
952+
class group_by_handler;
953+
typedef class st_select_lex SELECT_LEX;
954+
typedef struct st_order ORDER;
955+
952956
/*
953957
handlerton is a singleton structure - one instance per storage engine -
954958
to provide access to storage engine functionality that works on the
@@ -1251,6 +1255,24 @@ struct handlerton
12511255
*/
12521256
const char **tablefile_extensions; // by default - empty list
12531257

1258+
/**********************************************************************
1259+
Functions to intercept queries
1260+
**********************************************************************/
1261+
1262+
/*
1263+
Create and return a group_by_handler, if the storage engine can execute
1264+
the summary / group by query.
1265+
If the storage engine can't do that, return NULL.
1266+
1267+
This is only called for SELECT's where all tables are from the same
1268+
storage engine.
1269+
*/
1270+
group_by_handler *(*create_group_by)(THD *thd, SELECT_LEX *select_lex,
1271+
List<Item> *fields,
1272+
TABLE_LIST *table_list, ORDER *group_by,
1273+
ORDER *order_by, Item *where,
1274+
Item *having);
1275+
12541276
/*********************************************************************
12551277
Table discovery API.
12561278
It allows the server to "discover" tables that exist in the storage
@@ -4080,6 +4102,7 @@ class handler :public Sql_alloc
40804102
};
40814103

40824104
#include "multi_range_read.h"
4105+
#include "group_by_handler.h"
40834106

40844107
bool key_uses_partial_cols(TABLE_SHARE *table, uint keyno);
40854108

0 commit comments

Comments
 (0)