summaryrefslogtreecommitdiff
path: root/sql/group_by_handler.cc
diff options
context:
space:
mode:
authorMonty <monty@mariadb.org>2015-10-02 10:18:27 +0200
committerSergei Golubchik <serg@mariadb.org>2015-10-05 17:14:14 +0200
commitcf50e13fbd1b4d27a3542fe2751216d274eb7493 (patch)
tree2ff477367c13e63a50c2e13b00e0be79d65bc646 /sql/group_by_handler.cc
parentd8df2b946442e6f4bd7dd73570a603ae3e2d21f0 (diff)
downloadmariadb-git-cf50e13fbd1b4d27a3542fe2751216d274eb7493.tar.gz
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
Diffstat (limited to 'sql/group_by_handler.cc')
-rw-r--r--sql/group_by_handler.cc144
1 files changed, 144 insertions, 0 deletions
diff --git a/sql/group_by_handler.cc b/sql/group_by_handler.cc
new file mode 100644
index 00000000000..db03ce9196b
--- /dev/null
+++ b/sql/group_by_handler.cc
@@ -0,0 +1,144 @@
+/*
+ Copyright (c) 2014, SkySQL Ab & MariaDB Foundation
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ This file implements the group_by_handler code. This interface
+ can be used by storage handlers that can intercept summary or GROUP
+ BY queries from MariaDB and itself return the result to the user or
+ upper level.
+*/
+
+#ifdef USE_PRAGMA_IMPLEMENTATION
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "sql_priv.h"
+#include "sql_select.h"
+
+/*
+ Same return values as do_select();
+
+ @retval
+ 0 if ok
+ @retval
+ 1 if error is sent
+ @retval
+ -1 if error should be sent
+*/
+
+int group_by_handler::execute(JOIN *join)
+{
+ int err;
+ ha_rows max_limit;
+ ha_rows *reset_limit= 0;
+ Item **reset_item= 0;
+ DBUG_ENTER("group_by_handler");
+
+ if ((err= init_scan()))
+ goto error;
+
+ if (store_data_in_temp_table)
+ {
+ max_limit= join->tmp_table_param.end_write_records;
+ reset_limit= &join->unit->select_limit_cnt;
+ }
+ else
+ {
+ max_limit= join->unit->select_limit_cnt;
+ if (join->unit->fake_select_lex)
+ reset_item= &join->unit->fake_select_lex->select_limit;
+ }
+
+ while (!(err= next_row()))
+ {
+ if (thd->check_killed())
+ {
+ thd->send_kill_message();
+ (void) end_scan();
+ DBUG_RETURN(-1);
+ }
+
+ /* Check if we can accept the row */
+ if (!having || having->val_bool())
+ {
+ if (store_data_in_temp_table)
+ {
+ if ((err= table->file->ha_write_tmp_row(table->record[0])))
+ {
+ bool is_duplicate;
+ if (!table->file->is_fatal_error(err, HA_CHECK_DUP))
+ continue; // Distinct elimination
+
+ if (create_internal_tmp_table_from_heap(thd, table,
+ join->tmp_table_param.
+ start_recinfo,
+ &join->tmp_table_param.
+ recinfo,
+ err, 1, &is_duplicate))
+ DBUG_RETURN(1);
+ if (is_duplicate)
+ continue;
+ }
+ }
+ else
+ {
+ if (join->do_send_rows)
+ {
+ int error;
+ /* result < 0 if row was not accepted and should not be counted */
+ if ((error= join->result->send_data(*join->fields)))
+ {
+ (void) end_scan();
+ DBUG_RETURN(error < 0 ? 0 : -1);
+ }
+ }
+ }
+
+ /* limit handling */
+ if (++join->send_records >= max_limit && join->do_send_rows)
+ {
+ if (!(join->select_options & OPTION_FOUND_ROWS))
+ break; // LIMIT reached
+ join->do_send_rows= 0; // Calculate FOUND_ROWS()
+ if (reset_limit)
+ *reset_limit= HA_POS_ERROR;
+ if (reset_item)
+ *reset_item= 0;
+ }
+ }
+ }
+ if (err != 0 && err != HA_ERR_END_OF_FILE)
+ goto error;
+
+ if ((err= end_scan()))
+ goto error_2;
+ if (!store_data_in_temp_table && join->result->send_eof())
+ DBUG_RETURN(1); // Don't send error to client
+
+ DBUG_RETURN(0);
+
+error:
+ (void) end_scan();
+error_2:
+ print_error(err, MYF(0));
+ DBUG_RETURN(-1); // Error not sent to client
+}
+
+
+void group_by_handler::print_error(int error, myf errflag)
+{
+ my_error(ER_GET_ERRNO, MYF(0), error, hton_name(ht)->str);
+}