[Commits] a30776d: MDEV-17631 select_handler for a full query pushdown.

IgorBabaev igor at mariadb.com
Fri Jan 25 13:08:46 EET 2019


revision-id: a30776dd98d8e7b44e8b3e2bc07c23c0beed2434 (mariadb-10.3.6-131-ga30776d)
parent(s): 5dc4ca6554c9bb4685b64c35f345c06d28c8d3f9
author: Igor Babaev
committer: Igor Babaev
timestamp: 2019-01-25 03:08:46 -0800
message:

MDEV-17631 select_handler for a full query pushdown.

Interface + Proof of Concept for federatedx with a test case.

---
 libmysqld/CMakeLists.txt                           |   1 +
 .../federated/federatedx_create_handlers.result    |  92 +++++++++++++++
 .../federated/federatedx_create_handlers.test      |  68 +++++++++++
 sql/CMakeLists.txt                                 |   2 +-
 sql/handler.h                                      |   7 ++
 sql/select_handler.cc                              | 131 +++++++++++++++++++++
 sql/select_handler.h                               |  48 ++++++++
 sql/sql_lex.cc                                     |   2 +
 sql/sql_lex.h                                      |  10 +-
 sql/sql_select.cc                                  |  52 +++++++-
 sql/sql_select.h                                   |  27 +++++
 storage/federatedx/ha_federatedx.cc                | 122 +++++++++++++++++++
 storage/federatedx/ha_federatedx.h                 |  20 ++++
 13 files changed, 578 insertions(+), 4 deletions(-)

diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt
index dc3d190..5936c4e 100644
--- a/libmysqld/CMakeLists.txt
+++ b/libmysqld/CMakeLists.txt
@@ -78,6 +78,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
            ../sql/sql_prepare.cc ../sql/sql_rename.cc ../sql/sql_repl.cc 
            ../sql/sql_select.cc ../sql/sql_servers.cc
            ../sql/group_by_handler.cc ../sql/derived_handler.cc
+           ../sql/select_handler.cc
            ../sql/sql_show.cc ../sql/sql_state.c 
            ../sql/sql_statistics.cc ../sql/sql_string.cc
            ../sql/sql_tablespace.cc ../sql/sql_table.cc ../sql/sql_test.cc
diff --git a/mysql-test/suite/federated/federatedx_create_handlers.result b/mysql-test/suite/federated/federatedx_create_handlers.result
new file mode 100644
index 0000000..d32853e
--- /dev/null
+++ b/mysql-test/suite/federated/federatedx_create_handlers.result
@@ -0,0 +1,92 @@
+connect  master,127.0.0.1,root,,test,$MASTER_MYPORT,;
+connect  slave,127.0.0.1,root,,test,$SLAVE_MYPORT,;
+connection master;
+CREATE DATABASE federated;
+connection slave;
+CREATE DATABASE federated;
+connection slave;
+DROP TABLE IF EXISTS federated.t1;
+Warnings:
+Note	1051	Unknown table 'federated.t1'
+CREATE TABLE federated.t1 (
+id int(20) NOT NULL,
+name varchar(16) NOT NULL default ''  
+)
+DEFAULT CHARSET=latin1;
+INSERT INTO federated.t1 VALUES
+(3,'xxx'), (7,'yyy'), (4,'xxx'), (1,'zzz'), (5,'yyy');
+CREATE TABLE federated.t2 (
+name varchar(16) NOT NULL default ''  
+)
+DEFAULT CHARSET=latin1;
+INSERT INTO federated.t2 VALUES
+('yyy'), ('www'), ('yyy'), ('xxx'), ('www'), ('yyy'), ('www');
+connection master;
+DROP TABLE IF EXISTS federated.t1;
+Warnings:
+Note	1051	Unknown table 'federated.t1'
+CREATE TABLE federated.t1 (
+id int(20) NOT NULL,
+name varchar(16) NOT NULL default ''  
+)
+ENGINE="FEDERATED" DEFAULT CHARSET=latin1
+CONNECTION='mysql://root@127.0.0.1:SLAVE_PORT/federated/t1';
+CREATE TABLE federated.t2 (
+name varchar(16) NOT NULL default ''  
+)
+ENGINE="FEDERATED" DEFAULT CHARSET=latin1
+CONNECTION='mysql://root@127.0.0.1:SLAVE_PORT/federated/t2';
+SELECT * FROM federated.t1;
+id	name
+3	xxx
+7	yyy
+4	xxx
+1	zzz
+5	yyy
+SELECT id FROM federated.t1 WHERE id < 5;
+id
+3
+4
+1
+SELECT count(*), name FROM federated.t1 WHERE id < 5 GROUP BY name;
+count(*)	name
+2	xxx
+1	zzz
+SELECT * FROM federated.t1, federated.t2
+WHERE federated.t1.name = federated.t2.name;
+id	name	name
+7	yyy	yyy
+5	yyy	yyy
+7	yyy	yyy
+5	yyy	yyy
+3	xxx	xxx
+4	xxx	xxx
+7	yyy	yyy
+5	yyy	yyy
+SELECT * FROM federated.t1 LEFT JOIN federated.t2
+ON federated.t1.name = federated.t2.name
+WHERE federated.t1.id > 1;
+id	name	name
+7	yyy	yyy
+5	yyy	yyy
+7	yyy	yyy
+5	yyy	yyy
+3	xxx	xxx
+4	xxx	xxx
+7	yyy	yyy
+5	yyy	yyy
+SELECT * FROM federated.t1
+WHERE id IN (SELECT count(*) FROM federated.t2 GROUP BY name);
+id	name
+3	xxx
+1	zzz
+DROP TABLE federated.t1, federated.t2;
+connection slave;
+DROP TABLE federated.t1, federated.t2;
+connection default;
+connection master;
+DROP TABLE IF EXISTS federated.t1;
+DROP DATABASE IF EXISTS federated;
+connection slave;
+DROP TABLE IF EXISTS federated.t1;
+DROP DATABASE IF EXISTS federated;
diff --git a/mysql-test/suite/federated/federatedx_create_handlers.test b/mysql-test/suite/federated/federatedx_create_handlers.test
new file mode 100644
index 0000000..92a6f3c
--- /dev/null
+++ b/mysql-test/suite/federated/federatedx_create_handlers.test
@@ -0,0 +1,68 @@
+--source have_federatedx.inc
+--source include/federated.inc
+
+connection slave;
+DROP TABLE IF EXISTS federated.t1;
+
+CREATE TABLE federated.t1 (
+  id int(20) NOT NULL,
+  name varchar(16) NOT NULL default ''  
+)
+DEFAULT CHARSET=latin1;
+
+INSERT INTO federated.t1 VALUES
+  (3,'xxx'), (7,'yyy'), (4,'xxx'), (1,'zzz'), (5,'yyy');
+
+CREATE TABLE federated.t2 (
+  name varchar(16) NOT NULL default ''  
+)
+DEFAULT CHARSET=latin1;
+
+INSERT INTO federated.t2 VALUES
+  ('yyy'), ('www'), ('yyy'), ('xxx'), ('www'), ('yyy'), ('www');
+
+
+connection master;
+DROP TABLE IF EXISTS federated.t1;
+
+--replace_result $SLAVE_MYPORT SLAVE_PORT
+eval
+CREATE TABLE federated.t1 (
+  id int(20) NOT NULL,
+  name varchar(16) NOT NULL default ''  
+)
+ENGINE="FEDERATED" DEFAULT CHARSET=latin1
+CONNECTION='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1';
+
+--replace_result $SLAVE_MYPORT SLAVE_PORT
+eval
+CREATE TABLE federated.t2 (
+  name varchar(16) NOT NULL default ''  
+)
+ENGINE="FEDERATED" DEFAULT CHARSET=latin1
+CONNECTION='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t2';
+
+SELECT * FROM federated.t1;
+
+SELECT id FROM federated.t1 WHERE id < 5;
+
+SELECT count(*), name FROM federated.t1 WHERE id < 5 GROUP BY name;
+
+SELECT * FROM federated.t1, federated.t2
+  WHERE federated.t1.name = federated.t2.name;
+
+SELECT * FROM federated.t1 LEFT JOIN federated.t2
+              ON federated.t1.name = federated.t2.name
+  WHERE federated.t1.id > 1;
+
+SELECT * FROM federated.t1
+  WHERE id IN (SELECT count(*) FROM federated.t2 GROUP BY name); 
+ 
+DROP TABLE federated.t1, federated.t2;
+
+connection slave; 
+DROP TABLE federated.t1, federated.t2;
+
+connection default;
+
+source include/federated_cleanup.inc;
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index f76753a..0c0851e 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -96,7 +96,7 @@ SET (SQL_SOURCE
                sql_partition.cc sql_plugin.cc sql_prepare.cc sql_rename.cc 
                debug_sync.cc
                sql_repl.cc sql_select.cc sql_show.cc sql_state.c
-               group_by_handler.cc derived_handler.cc
+               group_by_handler.cc derived_handler.cc select_handler.cc
                sql_statistics.cc sql_string.cc lex_string.h
                sql_table.cc sql_test.cc sql_trigger.cc sql_udf.cc sql_union.cc
                sql_update.cc sql_view.cc strfunc.cc table.cc thr_malloc.cc 
diff --git a/sql/handler.h b/sql/handler.h
index ce6dd35..346dbd6 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -1184,6 +1184,7 @@ struct handler_iterator {
 class handler;
 class group_by_handler;
 class derived_handler;
+class select_handler;
 struct Query;
 typedef class st_select_lex SELECT_LEX;
 typedef struct st_order ORDER;
@@ -1511,6 +1512,12 @@ struct handlerton
     the function create_group_by has to return NULL.
   */
   derived_handler *(*create_derived)(THD *thd, TABLE_LIST *derived);
+
+  /*
+    Create and return a select_handler if the storage engine can execute
+    the select statement 'select, otherwise return NULL
+  */
+  select_handler *(*create_select) (THD *thd, SELECT_LEX *select);
    
    /*********************************************************************
      Table discovery API.
diff --git a/sql/select_handler.cc b/sql/select_handler.cc
new file mode 100644
index 0000000..f196678
--- /dev/null
+++ b/sql/select_handler.cc
@@ -0,0 +1,131 @@
+#include "mariadb.h"
+#include "sql_priv.h"
+#include "sql_select.h"
+#include "select_handler.h"
+
+Pushdown_select::~Pushdown_select()
+{
+  delete handler;
+}
+
+bool Pushdown_select::init()
+{
+  List<Item> types;
+  TMP_TABLE_PARAM tmp_table_param;
+  THD *thd= handler->thd;
+  DBUG_ENTER("Pushdown_select::init");
+  if (select->master_unit()->join_union_item_types(thd, types, 1))
+    DBUG_RETURN(true);
+  tmp_table_param.init();
+  tmp_table_param.field_count= types.elements;
+
+  handler->table= create_tmp_table(thd, &tmp_table_param, types,
+                                   (ORDER *) 0, false, 0,
+                                   TMP_TABLE_ALL_COLUMNS, 1,
+                                   &empty_clex_str, true, false);
+  if (!handler->table)
+    DBUG_RETURN(true);
+  if (handler->table->fill_item_list(&result_columns))
+    DBUG_RETURN(true);
+  DBUG_RETURN(false);  
+}
+
+bool Pushdown_select::send_result_set_metadata()
+{
+  THD *thd= handler->thd;
+  Protocol *protocol= thd->protocol;
+  DBUG_ENTER("Pushdown_select::send_result_set_metadata");
+
+#ifdef WITH_WSREP
+  if (WSREP(thd) && thd->wsrep_retry_query)
+  {
+    WSREP_DEBUG("skipping select metadata");
+    DBUG_RETURN(false);
+  }
+  #endif /* WITH_WSREP */
+  if (protocol->send_result_set_metadata(&result_columns,
+                                         Protocol::SEND_NUM_ROWS |
+                                         Protocol::SEND_EOF))
+    DBUG_RETURN(true);
+
+  DBUG_RETURN(false);
+}
+
+bool Pushdown_select::send_data()
+{
+  THD *thd= handler->thd;
+  Protocol *protocol= thd->protocol;
+  DBUG_ENTER("Pushdown_select::send_data");
+
+  if (thd->killed == ABORT_QUERY)
+    DBUG_RETURN(false);
+
+  protocol->prepare_for_resend();
+  if (protocol->send_result_set_row(&result_columns))
+  {
+    protocol->remove_last_row();
+    DBUG_RETURN(true);
+  }
+ 
+  thd->inc_sent_row_count(1);
+
+  if (thd->vio_ok())
+    DBUG_RETURN(protocol->write());
+
+  DBUG_RETURN(false);
+}
+
+bool Pushdown_select::send_eof()
+{
+  THD *thd= handler->thd;
+  DBUG_ENTER("Pushdown_select::send_eof");
+
+  /* 
+    Don't send EOF if we're in error condition (which implies we've already
+    sent or are sending an error)
+  */
+  if (thd->is_error())
+    DBUG_RETURN(true);
+  ::my_eof(thd);  
+  DBUG_RETURN(false);
+}
+
+int Pushdown_select::execute()
+{
+  int err;
+  THD *thd= handler->thd;
+
+  DBUG_ENTER("Pushdown_select::execute");
+
+  if ((err= handler->init_scan()))
+    goto error;
+
+  if (send_result_set_metadata())
+    DBUG_RETURN(-1);
+  
+  while (!(err= handler->next_row()))
+  {
+    if (thd->check_killed() || send_data())
+    {
+      handler->end_scan();
+      DBUG_RETURN(-1);
+    }
+  }
+
+  if (err != 0 && err != HA_ERR_END_OF_FILE)
+    goto error;
+
+  if ((err= handler->end_scan()))
+   goto error_2;
+
+  if (send_eof())
+    DBUG_RETURN(-1);
+
+  DBUG_RETURN(0);
+
+error:
+  handler->end_scan();
+error_2:
+  handler->print_error(err, MYF(0));
+  DBUG_RETURN(-1);                              // Error not sent to client
+}
diff --git a/sql/select_handler.h b/sql/select_handler.h
new file mode 100644
index 0000000..68fca03
--- /dev/null
+++ b/sql/select_handler.h
@@ -0,0 +1,48 @@
+#ifndef SELECT_HANDLER_INCLUDED
+#define SELECT_HANDLER_INCLUDED
+
+#include "mariadb.h"
+#include "sql_priv.h"
+
+class select_handler
+{
+ public:
+  THD *thd;
+  handlerton *ht;
+
+  SELECT_LEX *select;
+
+   /*
+    Temporary table where all results should be stored in record[0]
+    The table has a field for every item from the select_lex::item_list.
+  */
+  TABLE *table;
+
+  select_handler(THD *thd_arg, handlerton *ht_arg)
+    : thd(thd_arg), ht(ht_arg), table(0) {}
+  
+  virtual ~select_handler() {} 
+ 
+  /*
+    Functions to scan the select result set.
+    All these returns 0 if ok, error code in case of error.
+  */
+
+  /* Initialize the process of producing rows of result set */
+  virtual int init_scan()= 0;
+
+  /*
+    Put the next produced row of the result set in table->record[0] 
+    and return 0. Return HA_ERR_END_OF_FILE if there are no more rows,
+    return other error number in case of fatal error.
+  */
+  virtual int next_row()= 0;
+
+  /* Finish scanning */
+  virtual int end_scan()=0;
+  
+  /* Report errors */
+  virtual void print_error(int error, myf errflag)=0;
+};
+
+#endif /* SELECT_HANDLER_INCLUDED */
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index b309a0a..4c33091 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -2354,6 +2354,7 @@ void st_select_lex::init_query()
   tvc= 0;
   in_tvc= false;
   versioned_tables= 0;
+  pushdown_select= 0;
 }
 
 void st_select_lex::init_select()
@@ -9462,3 +9463,4 @@ bool SELECT_LEX::make_unique_derived_name(THD *thd, LEX_CSTRING *alias)
   alias->str= thd->strmake(buff, alias->length);
   return !alias->str;
 }
+    
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 2478815..8fa28ff 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -225,6 +225,8 @@ class Item_window_func;
 struct sql_digest_state;
 class With_clause;
 class my_var;
+class select_handler;
+class Pushdown_select;
 
 #define ALLOC_ROOT_SET 1024
 
@@ -812,12 +814,13 @@ class st_select_lex_unit: public st_select_lex_node {
   bool prepare_join(THD *thd, SELECT_LEX *sl, select_result *result,
                     ulong additional_options,
                     bool is_union_select);
-  bool join_union_item_types(THD *thd, List<Item> &types, uint count);
   bool join_union_type_handlers(THD *thd,
                                 class Type_holder *holders, uint count);
   bool join_union_type_attributes(THD *thd,
                                   class Type_holder *holders, uint count);
 public:
+  bool join_union_item_types(THD *thd, List<Item> &types, uint count);
+public:
   // Ensures that at least all members used during cleanup() are initialized.
   st_select_lex_unit()
     : union_result(NULL), table(NULL), result(NULL),
@@ -1240,6 +1243,9 @@ class st_select_lex: public st_select_lex_node
   table_value_constr *tvc;
   bool in_tvc;
 
+  select_handler *select_h;
+  Pushdown_select *pushdown_select;
+
   /** System Versioning */
 public:
   uint versioned_tables;
@@ -1471,6 +1477,8 @@ class st_select_lex: public st_select_lex_node
                                        Item_transformer transformer,
                                        uchar *arg);
 
+  select_handler *find_select_handler(THD *thd);
+
 private:
   bool m_non_agg_field_used;
   bool m_agg_func_used;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index d0acbef..4ac5ea1 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -64,6 +64,7 @@
 #include "sys_vars_shared.h"
 #include "sp_head.h"
 #include "sp_rcontext.h"
+#include "select_handler.h"
 
 /*
   A key part number that means we're using a fulltext scan.
@@ -1437,7 +1438,12 @@ int JOIN::optimize()
 {
   int res= 0;
   join_optimization_state init_state= optimization_state;
-  if (optimization_state == JOIN::OPTIMIZATION_PHASE_1_DONE)
+  if (select_lex->pushdown_select)
+  {
+    res= select_lex->pushdown_select->init();
+    with_two_phase_optimization= false;
+  }
+  else if (optimization_state == JOIN::OPTIMIZATION_PHASE_1_DONE)
     res= optimize_stage2();
   else
   {
@@ -3935,6 +3941,12 @@ void JOIN::exec_inner()
       select_describe(this, FALSE, FALSE, FALSE,
 		      (zero_result_cause?zero_result_cause:"No tables used"));
 
+    else if (select_lex->pushdown_select)
+    {
+      error= select_lex->pushdown_select->execute();
+      delete select_lex->pushdown_select; 
+      DBUG_VOID_RETURN;
+    }
     else
     {
       if (result->send_result_set_metadata(*columns_list,
@@ -4033,7 +4045,8 @@ void JOIN::exec_inner()
     not the case.
   */
   if (exec_const_order_group_cond.elements &&
-      !(select_options & SELECT_DESCRIBE))
+      !(select_options & SELECT_DESCRIBE) && 
+      !select_lex->pushdown_select)
   {
     List_iterator_fast<Item> const_item_it(exec_const_order_group_cond);
     Item *cur_const_item;
@@ -4060,6 +4073,12 @@ void JOIN::exec_inner()
                     !table_count ? "No tables used" : NullS);
     DBUG_VOID_RETURN;
   }
+  else if (select_lex->pushdown_select)
+  {
+    error= select_lex->pushdown_select->execute();
+    delete select_lex->pushdown_select; 
+    DBUG_VOID_RETURN;
+  }
   else
   {
     /* it's a const select, materialize it. */
@@ -4271,6 +4290,15 @@ mysql_select(THD *thd,
     }
   }
 
+  select_lex->select_h= select_lex->find_select_handler(thd);
+  if (select_lex->select_h)
+  {
+    if (!(select_lex->pushdown_select=
+      new (thd->mem_root) Pushdown_select(select_lex,
+                                          select_lex->select_h)))
+      DBUG_RETURN(TRUE);
+  }
+      
   if ((err= join->optimize()))
   {
     goto err;					// 1
@@ -27371,6 +27399,26 @@ Item *remove_pushed_top_conjuncts(THD *thd, Item *cond)
   return cond;
 }
 
+select_handler *SELECT_LEX::find_select_handler(THD *thd)
+{
+  if (next_select())
+      return 0;
+  if (master_unit()->outer_select())
+    return 0;
+  for (TABLE_LIST *tbl= join->tables_list; tbl; tbl= tbl->next_local)
+  {
+    if (!tbl->table)
+      continue;
+    handlerton *ht= tbl->table->file->partition_ht();
+    if (!ht->create_select)
+      continue;
+    select_handler *sh= ht->create_select(thd, this);
+    return sh;
+  }
+  return 0;
+}
+
+
 /**
   @} (end of group Query_Optimizer)
 */
diff --git a/sql/sql_select.h b/sql/sql_select.h
index e51367c..e9cf51b 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -2461,6 +2461,33 @@ class Pushdown_derived: public Sql_alloc
   int execute(); 
 };
 
+
+class select_handler;
+
+
+class Pushdown_select: public Sql_alloc
+{
+private:
+  List<Item> result_columns;
+  bool send_result_set_metadata();
+  bool send_data();
+  bool send_eof();
+
+public:
+  SELECT_LEX *select;
+  select_handler *handler;
+
+  Pushdown_select(SELECT_LEX *sel, select_handler *h)
+    : select(sel), handler(h) {}
+
+  ~Pushdown_select();
+
+  bool init();
+
+  int execute(); 
+};
+
+
 bool test_if_order_compatible(SQL_I_List<ORDER> &a, SQL_I_List<ORDER> &b);
 int test_if_group_changed(List<Cached_item> &list);
 int create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort);
diff --git a/storage/federatedx/ha_federatedx.cc b/storage/federatedx/ha_federatedx.cc
index 45cd14e..ec96a24 100644
--- a/storage/federatedx/ha_federatedx.cc
+++ b/storage/federatedx/ha_federatedx.cc
@@ -406,6 +406,8 @@ handlerton* federatedx_hton;
 
 static derived_handler*
 create_federatedx_derived_handler(THD* thd, TABLE_LIST *derived);
+static select_handler*
+create_federatedx_select_handler(THD* thd, SELECT_LEX *sel);
 
 /*
   Initialize the federatedx handler.
@@ -438,6 +440,7 @@ int federatedx_db_init(void *p)
   federatedx_hton->create= federatedx_create_handler;
   federatedx_hton->flags= HTON_ALTER_NOT_SUPPORTED;
   federatedx_hton->create_derived= create_federatedx_derived_handler;
+  federatedx_hton->create_select= create_federatedx_select_handler;
 
   if (mysql_mutex_init(fe_key_mutex_federatedx,
                        &federatedx_mutex, MY_MUTEX_INIT_FAST))
@@ -3795,6 +3798,125 @@ void ha_federatedx_derived_handler::print_error(int, unsigned long)
 {
 }
 
+
+static select_handler*
+create_federatedx_select_handler(THD* thd, SELECT_LEX *sel)
+{
+  ha_federatedx_select_handler* handler = NULL;
+  handlerton *ht= 0;
+
+  for (TABLE_LIST *tbl= thd->lex->query_tables; tbl; tbl= tbl->next_global)
+  {
+    if (!tbl->table)
+      return 0;
+    if (!ht)
+      ht= tbl->table->file->partition_ht();
+    else if (ht != tbl->table->file->partition_ht())
+      return 0;
+  }
+
+  handler= new ha_federatedx_select_handler(thd, sel);
+
+  return handler;
+}
+
+ha_federatedx_select_handler::ha_federatedx_select_handler(THD *thd,
+                                                           SELECT_LEX *sel)
+  : select_handler(thd, federatedx_hton)
+{
+  select= sel;
+}
+
+ha_federatedx_select_handler::~ha_federatedx_select_handler() {}
+
+int ha_federatedx_select_handler::init_scan()
+{
+  int rc= 0;
+
+  DBUG_ENTER("ha_federatedx_select_handler::init_scan");
+
+  TABLE *table= 0;
+  for (TABLE_LIST *tbl= thd->lex->query_tables; tbl; tbl= tbl->next_global)
+  {
+    if (!tbl->table)
+      continue;
+    table= tbl->table;
+    break;
+  }
+  ha_federatedx *h= (ha_federatedx *) table->file;
+  io= h->io;
+  share= get_share(table->s->table_name.str, table);
+  txn= h->get_txn(thd);
+  if ((rc= txn->acquire(share, thd, TRUE, &io)))
+    DBUG_RETURN(rc);
+
+  if (io->query(thd->query(), thd->query_length()))
+    goto err;
+
+  stored_result= io->store_result();
+  if (!stored_result)
+      goto err;
+
+  DBUG_RETURN(0);
+
+err:
+  DBUG_RETURN(HA_FEDERATEDX_ERROR_WITH_REMOTE_SYSTEM);
+}
+
+int ha_federatedx_select_handler::next_row()
+{
+  int rc;
+  FEDERATEDX_IO_ROW *row;
+  ulong *lengths;
+  Field **field;
+  int column= 0;
+  Time_zone *saved_time_zone= table->in_use->variables.time_zone;
+  DBUG_ENTER("ha_federatedx_select_handler::next_row");
+
+  if ((rc= txn->acquire(share, table->in_use, TRUE, &io)))
+    DBUG_RETURN(rc);
+
+  if (!(row= io->fetch_row(stored_result)))
+    DBUG_RETURN(HA_ERR_END_OF_FILE);
+
+  /* Convert row to internal format */
+  table->in_use->variables.time_zone= UTC;
+  lengths= io->fetch_lengths(stored_result);
+
+  for (field= table->field; *field; field++, column++)
+  {
+    if (io->is_column_null(row, column))
+       (*field)->set_null();
+    else
+    {
+      (*field)->set_notnull();
+      (*field)->store(io->get_column_data(row, column),
+                      lengths[column], &my_charset_bin);
+    }
+  }
+  table->in_use->variables.time_zone= saved_time_zone;
+
+  DBUG_RETURN(rc);
+}
+
+int ha_federatedx_select_handler::end_scan()
+{
+  DBUG_ENTER("ha_federatedx_derived_handler::end_scan");
+
+  free_tmp_table(thd, table);
+  table= 0;
+
+  txn->release(&io);
+  DBUG_ASSERT(io == NULL);
+
+  DBUG_RETURN(0);
+}
+
+void ha_federatedx_select_handler::print_error(int, unsigned long)
+{
+}
+
+
 struct st_mysql_storage_engine federatedx_storage_engine=
 { MYSQL_HANDLERTON_INTERFACE_VERSION };
 
diff --git a/storage/federatedx/ha_federatedx.h b/storage/federatedx/ha_federatedx.h
index 61c7029..4c721e5 100644
--- a/storage/federatedx/ha_federatedx.h
+++ b/storage/federatedx/ha_federatedx.h
@@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <thr_lock.h>
 #include "handler.h"
 #include "derived_handler.h"
+#include "select_handler.h"
 
 class federatedx_io;
 
@@ -450,6 +451,7 @@ class ha_federatedx: public handler
   int free_result(void);
 
   friend class ha_federatedx_derived_handler;
+  friend class ha_federatedx_select_handler;
 };
 
 extern const char ident_quote_char;              // Character for quoting
@@ -483,4 +485,22 @@ class ha_federatedx_derived_handler: public derived_handler
   void print_error(int, unsigned long);
 };
 
+
+class ha_federatedx_select_handler: public select_handler
+{
+private:
+  FEDERATEDX_SHARE *share;
+  federatedx_txn *txn;
+  federatedx_io *io;
+  FEDERATEDX_IO_RESULT *stored_result;
+
+public:
+  ha_federatedx_select_handler(THD* thd_arg, SELECT_LEX *sel);
+  ~ha_federatedx_select_handler();
+  int init_scan();
+  int next_row();
+  int end_scan();
+  void print_error(int, unsigned long);
+};
+
 #endif /* HA_FEDERATEDX_INCLUDED */


More information about the commits mailing list