Drizzled Public API Documentation

subselect.h
00001 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2008 Sun Microsystems, Inc.
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; version 2 of the License.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00018  */
00019 
00020 #pragma once
00021 
00022 /* subselect Item */
00023 
00024 
00025 #include <drizzled/comp_creator.h>
00026 #include <drizzled/item/ref.h>
00027 #include <drizzled/item/field.h>
00028 #include <drizzled/item/bin_string.h>
00029 #include <drizzled/util/test.h>
00030 
00031 namespace drizzled
00032 {
00033 
00034 class Select_Lex;
00035 class Select_Lex_Unit;
00036 class Join;
00037 class select_result_interceptor;
00038 class subselect_engine;
00039 class subselect_hash_sj_engine;
00040 class Item_bool_func2;
00041 class Cached_item;
00042 class Item_in_optimizer;
00043 class Item_func_not_all;
00044 class Tmp_Table_Param;
00045 
00046 
00047 /* base class for subselects */
00048 
00049 class Item_subselect :public Item_result_field
00050 {
00051   bool value_assigned; /* value already assigned to subselect */
00052 public:
00053   /* thread handler, will be assigned in fix_fields only */
00054   Session *session;
00055   /* substitution instead of subselect in case of optimization */
00056   Item *substitution;
00057   /* unit of subquery */
00058   Select_Lex_Unit *unit;
00059 protected:
00060   /* engine that perform execution of subselect (single select or union) */
00061   subselect_engine *engine;
00062   /* old engine if engine was changed */
00063   subselect_engine *old_engine;
00064   /* cache of used external tables */
00065   table_map used_tables_cache;
00066   /* allowed number of columns (1 for single value subqueries) */
00067   uint32_t max_columns;
00068   /* where subquery is placed */
00069   enum_parsing_place parsing_place;
00070   /* work with 'substitution' */
00071   bool have_to_be_excluded;
00072   /* cache of constant state */
00073   bool const_item_cache;
00074 
00075 public:
00076   /* changed engine indicator */
00077   bool engine_changed;
00078   /* subquery is transformed */
00079   bool changed;
00080 
00081   /* TRUE <=> The underlying SELECT is correlated w.r.t some ancestor select */
00082   bool is_correlated;
00083 
00084   enum trans_res {RES_OK, RES_REDUCE, RES_ERROR};
00085   enum subs_type {UNKNOWN_SUBS, SINGLEROW_SUBS,
00086       EXISTS_SUBS, IN_SUBS, ALL_SUBS, ANY_SUBS};
00087 
00088   Item_subselect();
00089 
00090   virtual subs_type substype() { return UNKNOWN_SUBS; }
00091 
00092   /*
00093     We need this method, because some compilers do not allow 'this'
00094     pointer in constructor initialization list, but we need to pass a pointer
00095     to subselect Item class to select_result_interceptor's constructor.
00096   */
00097   virtual void init (Select_Lex *select_lex,
00098          select_result_interceptor *result);
00099 
00100   ~Item_subselect();
00101   void cleanup();
00102   virtual void reset()
00103   {
00104     null_value= 1;
00105   }
00106   virtual trans_res select_transformer(Join *join);
00107   bool assigned() { return value_assigned; }
00108   void assigned(bool a) { value_assigned= a; }
00109   enum Type type() const;
00110   bool is_null()
00111   {
00112     update_null_value();
00113     return null_value;
00114   }
00115   bool fix_fields(Session *session, Item **ref);
00116   virtual bool exec();
00117   virtual void fix_length_and_dec();
00118   table_map used_tables() const;
00119   table_map not_null_tables() const { return 0; }
00120   bool const_item() const;
00121   inline table_map get_used_tables_cache() { return used_tables_cache; }
00122   inline bool get_const_item_cache() { return const_item_cache; }
00123   Item *get_tmp_table_item(Session *session);
00124   void update_used_tables();
00125   virtual void print(String *str);
00126   virtual bool have_guarded_conds() { return false; }
00127   bool change_engine(subselect_engine *eng)
00128   {
00129     old_engine= engine;
00130     engine= eng;
00131     engine_changed= 1;
00132     return eng == 0;
00133   }
00134   /*
00135     True if this subquery has been already evaluated. Implemented only for
00136     single select and union subqueries only.
00137   */
00138   bool is_evaluated() const;
00139   bool is_uncacheable() const;
00140 
00141   /*
00142     Used by max/min subquery to initialize value presence registration
00143     mechanism. Engine call this method before rexecution query.
00144   */
00145   virtual void reset_value_registration() {}
00146   enum_parsing_place place() { return parsing_place; }
00147   bool walk(Item_processor processor, bool walk_subquery, unsigned char *arg);
00148 
00153   Select_Lex* get_select_lex();
00154 
00155   friend class select_result_interceptor;
00156   friend class Item_in_optimizer;
00157   friend bool Item_field::fix_fields(Session *, Item **);
00158   friend int  Item_field::fix_outer_field(Session *, Field **, Item **);
00159   friend bool Item_ref::fix_fields(Session *, Item **);
00160   friend void mark_select_range_as_dependent(Session*,
00161                                              Select_Lex*, Select_Lex*,
00162                                              Field*, Item*, Item_ident*);
00163 };
00164 
00165 /* single value subselect */
00166 
00167 class Item_cache;
00168 class Item_singlerow_subselect :public Item_subselect
00169 {
00170 protected:
00171   Item_cache *value, **row;
00172 public:
00173   Item_singlerow_subselect(Select_Lex *select_lex);
00174   Item_singlerow_subselect() :Item_subselect(), value(0), row (0) {}
00175 
00176   void cleanup();
00177   subs_type substype() { return SINGLEROW_SUBS; }
00178 
00179   void reset();
00180   trans_res select_transformer(Join *join);
00181   void store(uint32_t i, Item* item);
00182   double val_real();
00183   int64_t val_int ();
00184   String *val_str (String *);
00185   type::Decimal *val_decimal(type::Decimal *);
00186   bool val_bool();
00187   enum Item_result result_type() const;
00188   enum_field_types field_type() const;
00189   void fix_length_and_dec();
00190 
00191   uint32_t cols();
00192   Item* element_index(uint32_t i) { return reinterpret_cast<Item*>(row[i]); }
00193   Item** addr(uint32_t i) { return (Item**)row + i; }
00194   bool check_cols(uint32_t c);
00195   bool null_inside();
00196   void bring_value();
00197 
00210   Select_Lex* invalidate_and_restore_select_lex();
00211 
00212   friend class select_singlerow_subselect;
00213 };
00214 
00215 /* used in static ALL/ANY optimization */
00216 class Item_maxmin_subselect :public Item_singlerow_subselect
00217 {
00218 protected:
00219   bool max;
00220   bool was_values;  // Set if we have found at least one row
00221 public:
00222   Item_maxmin_subselect(Session *session, Item_subselect *parent,
00223       Select_Lex *select_lex, bool max);
00224   virtual void print(String *str);
00225   void cleanup();
00226   bool any_value() { return was_values; }
00227   void register_value() { was_values= true; }
00228   void reset_value_registration() { was_values= false; }
00229 };
00230 
00231 /* exists subselect */
00232 
00233 class Item_exists_subselect :public Item_subselect
00234 {
00235 protected:
00236   bool value; /* value of this item (boolean: exists/not-exists) */
00237 
00238 public:
00239   Item_exists_subselect(Select_Lex *select_lex);
00240   Item_exists_subselect(): Item_subselect() {}
00241 
00242   subs_type substype() { return EXISTS_SUBS; }
00243   void reset()
00244   {
00245     value= 0;
00246   }
00247 
00248   enum Item_result result_type() const { return INT_RESULT;}
00249   int64_t val_int();
00250   double val_real();
00251   String *val_str(String*);
00252   type::Decimal *val_decimal(type::Decimal *);
00253   bool val_bool();
00254   void fix_length_and_dec();
00255   virtual void print(String *str);
00256 
00257   friend class select_exists_subselect;
00258   friend class subselect_uniquesubquery_engine;
00259   friend class subselect_indexsubquery_engine;
00260 };
00261 
00262 
00278 class Item_in_subselect :public Item_exists_subselect
00279 {
00280 public:
00281   Item *left_expr;
00282 protected:
00283   /*
00284     Cache of the left operand of the subquery predicate. Allocated in the
00285     runtime memory root, for each execution, thus need not be freed.
00286   */
00287   List<Cached_item> *left_expr_cache;
00288   bool first_execution;
00289 
00290   /*
00291     expr & optimizer used in subselect rewriting to store Item for
00292     all JOIN in UNION
00293   */
00294   Item *expr;
00295   Item_in_optimizer *optimizer;
00296   bool was_null;
00297   bool abort_on_null;
00298 
00299 public:
00300   /* Used to trigger on/off conditions that were pushed down to subselect */
00301   bool *pushed_cond_guards;
00302 
00303   /* Priority of this predicate in the convert-to-semi-join-nest process. */
00304   int sj_convert_priority;
00305 
00306   /*
00307     Location of the subquery predicate. It is either
00308      - pointer to join nest if the subquery predicate is in the ON expression
00309      - (TableList*)1 if the predicate is in the WHERE.
00310   */
00311   TableList *expr_join_nest;
00312 
00313   /* The method chosen to execute the IN predicate.  */
00314   enum enum_exec_method {
00315     NOT_TRANSFORMED, /* No execution method was chosen for this IN. */
00316     SEMI_JOIN,   /* IN was converted to semi-join nest and should be removed. */
00317     IN_TO_EXISTS, /* IN was converted to correlated EXISTS. */
00318     MATERIALIZATION /* IN will be executed via subquery materialization. */
00319   };
00320   enum_exec_method exec_method;
00321 
00322   bool *get_cond_guard(int i)
00323   {
00324     return pushed_cond_guards ? pushed_cond_guards + i : NULL;
00325   }
00326   void set_cond_guard_var(int i, bool v)
00327   {
00328     if ( pushed_cond_guards)
00329       pushed_cond_guards[i]= v;
00330   }
00331   bool have_guarded_conds() { return test(pushed_cond_guards); }
00332 
00333   Item_func_not_all *upper_item; // point on NOT/NOP before ALL/SOME subquery
00334 
00335   Item_in_subselect(Item * left_expr, Select_Lex *select_lex);
00336   Item_in_subselect()
00337     :
00338       Item_exists_subselect(),
00339       left_expr(NULL),
00340       left_expr_cache(NULL),
00341       first_execution(true),
00342       optimizer(NULL),
00343       abort_on_null(false),
00344       pushed_cond_guards(NULL),
00345       sj_convert_priority(0),
00346       expr_join_nest(NULL),
00347       exec_method(NOT_TRANSFORMED),
00348       upper_item(NULL)
00349   {}
00350   void cleanup();
00351   subs_type substype() { return IN_SUBS; }
00352   void reset()
00353   {
00354     value= 0;
00355     null_value= 0;
00356     was_null= 0;
00357   }
00358   trans_res select_transformer(Join *join);
00359   trans_res select_in_like_transformer(Join *join, const Comp_creator *func);
00360   trans_res single_value_transformer(Join *join, const Comp_creator *func);
00361   trans_res row_value_transformer(Join * join);
00362   trans_res single_value_in_to_exists_transformer(Join * join,
00363                                                   const Comp_creator *func);
00364   trans_res row_value_in_to_exists_transformer(Join * join);
00365   virtual bool exec();
00366   int64_t val_int();
00367   double val_real();
00368   String *val_str(String*);
00369   type::Decimal *val_decimal(type::Decimal *);
00370   void update_null_value () { (void) val_bool(); }
00371   bool val_bool();
00372   void top_level_item() { abort_on_null=1; }
00373   inline bool is_top_level_item() { return abort_on_null; }
00374   bool test_limit(Select_Lex_Unit *unit);
00375   virtual void print(String *str);
00376   bool fix_fields(Session *session, Item **ref);
00377   bool setup_engine();
00378   bool init_left_expr_cache();
00379   bool is_expensive_processor(unsigned char *arg);
00380 
00381   friend class Item_ref_null_helper;
00382   friend class Item_is_not_null_test;
00383   friend class Item_in_optimizer;
00384   friend class subselect_indexsubquery_engine;
00385   friend class subselect_hash_sj_engine;
00386 };
00387 
00388 
00389 /* ALL/ANY/SOME subselect */
00390 class Item_allany_subselect :public Item_in_subselect
00391 {
00392 public:
00393   chooser_compare_func_creator func_creator;
00394   Comp_creator *func;
00395   bool all;
00396 
00397   Item_allany_subselect(Item * left_expr, chooser_compare_func_creator fc,
00398                         Select_Lex *select_lex, bool all);
00399 
00400   // only ALL subquery has upper not
00401   subs_type substype() { return all?ALL_SUBS:ANY_SUBS; }
00402   trans_res select_transformer(Join *join);
00403   virtual void print(String *str);
00404 };
00405 
00406 
00407 class subselect_engine: public memory::SqlAlloc
00408 {
00409 protected:
00410   select_result_interceptor *result; /* results storage class */
00411   Session *session; /* pointer to current Session */
00412   Item_subselect *item; /* item, that use this engine */
00413   enum Item_result res_type; /* type of results */
00414   enum_field_types res_field_type; /* column type of the results */
00415   bool maybe_null; /* may be null (first item in select) */
00416 public:
00417 
00418   enum enum_engine_type {ABSTRACT_ENGINE, SINGLE_SELECT_ENGINE,
00419                          UNION_ENGINE, UNIQUESUBQUERY_ENGINE,
00420                          INDEXSUBQUERY_ENGINE, HASH_SJ_ENGINE};
00421 
00422   subselect_engine(Item_subselect *si, select_result_interceptor *res)
00423     :session(NULL)
00424   {
00425     result= res;
00426     item= si;
00427     res_type= STRING_RESULT;
00428     res_field_type= DRIZZLE_TYPE_VARCHAR;
00429     maybe_null= 0;
00430   }
00431   virtual ~subselect_engine() {} // to satisfy compiler
00432   virtual void cleanup()= 0;
00433 
00434   /*
00435     Also sets "session" for subselect_engine::result.
00436     Should be called before prepare().
00437   */
00438   void set_session(Session *session_arg);
00439   Session * get_session() { return session; }
00440   virtual int prepare()= 0;
00441   virtual void fix_length_and_dec(Item_cache** row)= 0;
00442   /*
00443     Execute the engine
00444 
00445     SYNOPSIS
00446       exec()
00447 
00448     DESCRIPTION
00449       Execute the engine. The result of execution is subquery value that is
00450       either captured by previously set up select_result-based 'sink' or
00451       stored somewhere by the exec() method itself.
00452 
00453       A required side effect: If at least one pushed-down predicate is
00454       disabled, subselect_engine->no_rows() must return correct result after
00455       the exec() call.
00456 
00457     RETURN
00458       0 - OK
00459       1 - Either an execution error, or the engine was "changed", and the
00460           caller should call exec() again for the new engine.
00461   */
00462   virtual int exec()= 0;
00463   virtual uint32_t cols()= 0; /* return number of columns in select */
00464   virtual bool uncacheable()= 0; /* query is uncacheable */
00465   virtual bool uncacheable(uint32_t bit_pos)= 0; /* query is uncacheable */
00466   enum Item_result type() { return res_type; }
00467   enum_field_types field_type() { return res_field_type; }
00468   virtual void exclude()= 0;
00469   virtual bool may_be_null() { return maybe_null; }
00470   virtual table_map upper_select_const_tables()= 0;
00471   static table_map calc_const_tables(TableList *);
00472   virtual void print(String *str)= 0;
00473   virtual bool change_result(Item_subselect *si,
00474                              select_result_interceptor *result)= 0;
00475   virtual bool no_tables()= 0;
00476   virtual bool is_executed() const { return false; }
00477   /* Check if subquery produced any rows during last query execution */
00478   virtual bool no_rows() = 0;
00479   virtual enum_engine_type engine_type() { return ABSTRACT_ENGINE; }
00480 
00481 protected:
00482   void set_row(List<Item> &item_list, Item_cache **row);
00483 };
00484 
00485 
00486 class subselect_single_select_engine: public subselect_engine
00487 {
00488   bool prepared; /* simple subselect is prepared */
00489   bool optimized; /* simple subselect is optimized */
00490   bool executed; /* simple subselect is executed */
00491   Select_Lex *select_lex; /* corresponding select_lex */
00492   Join * join; /* corresponding JOIN structure */
00493 public:
00494   subselect_single_select_engine(Select_Lex *select,
00495          select_result_interceptor *result,
00496          Item_subselect *item);
00497   void cleanup();
00498   int prepare();
00499   void fix_length_and_dec(Item_cache** row);
00500   int exec();
00501   uint32_t cols();
00502   bool uncacheable();
00503   bool uncacheable(uint32_t bit_pos);
00504   void exclude();
00505   table_map upper_select_const_tables();
00506   virtual void print (String *str);
00507   bool change_result(Item_subselect *si, select_result_interceptor *result);
00508   bool no_tables();
00509   bool may_be_null();
00510   bool is_executed() const { return executed; }
00511   bool no_rows();
00512   virtual enum_engine_type engine_type() { return SINGLE_SELECT_ENGINE; }
00513   bool save_join_if_explain();
00514 
00515   friend class subselect_hash_sj_engine;
00516   friend class Item_in_subselect;
00517 };
00518 
00519 
00520 class subselect_union_engine: public subselect_engine
00521 {
00522   Select_Lex_Unit *unit;  /* corresponding unit structure */
00523 public:
00524   subselect_union_engine(Select_Lex_Unit *u,
00525        select_result_interceptor *result,
00526        Item_subselect *item);
00527   void cleanup();
00528   int prepare();
00529   void fix_length_and_dec(Item_cache** row);
00530   int exec();
00531   uint32_t cols();
00532   bool uncacheable();
00533   bool uncacheable(uint32_t bit_pos);
00534   void exclude();
00535   table_map upper_select_const_tables();
00536   virtual void print (String *str);
00537   bool change_result(Item_subselect *si, select_result_interceptor *result);
00538   bool no_tables();
00539   bool is_executed() const;
00540   bool no_rows();
00541   virtual enum_engine_type engine_type() { return UNION_ENGINE; }
00542 };
00543 
00544 
00545 class JoinTable;
00546 
00547 
00548 /*
00549   A subquery execution engine that evaluates the subquery by doing one index
00550   lookup in a unique index.
00551 
00552   This engine is used to resolve subqueries in forms
00553 
00554     outer_expr IN (SELECT tbl.unique_key FROM tbl WHERE subq_where)
00555 
00556   or, tuple-based:
00557 
00558     (oe1, .. oeN) IN (SELECT uniq_key_part1, ... uniq_key_partK
00559                       FROM tbl WHERE subqwhere)
00560 
00561   i.e. the subquery is a single table SELECT without GROUP BY, aggregate
00562   functions, etc.
00563 */
00564 
00565 class subselect_uniquesubquery_engine: public subselect_engine
00566 {
00567 protected:
00568   JoinTable *tab;
00569   Item *cond; /* The WHERE condition of subselect */
00570   /*
00571     TRUE<=> last execution produced empty set. Valid only when left
00572     expression is NULL.
00573   */
00574   bool empty_result_set;
00575   bool null_keypart; /* TRUE <=> constructed search tuple has a NULL */
00576 public:
00577 
00578   // constructor can assign Session because it will be called after Join::prepare
00579   subselect_uniquesubquery_engine(Session *session_arg, JoinTable *tab_arg,
00580           Item_subselect *subs, Item *where)
00581     :subselect_engine(subs, 0), tab(tab_arg), cond(where)
00582   {
00583     set_session(session_arg);
00584   }
00585   void cleanup();
00586   int prepare();
00587   void fix_length_and_dec(Item_cache** row);
00588   int exec();
00589   uint32_t cols() { return 1; }
00590   bool uncacheable() { return true; }
00591   bool uncacheable(uint32_t) { return true; }
00592   void exclude();
00593   table_map upper_select_const_tables() { return 0; }
00594   virtual void print (String *str);
00595   bool change_result(Item_subselect *si, select_result_interceptor *result);
00596   bool no_tables();
00597   int scan_table();
00598   bool copy_ref_key();
00599   bool no_rows() { return empty_result_set; }
00600   virtual enum_engine_type engine_type() { return UNIQUESUBQUERY_ENGINE; }
00601 };
00602 
00603 
00604 class subselect_indexsubquery_engine: public subselect_uniquesubquery_engine
00605 {
00606   /* FALSE for 'ref', TRUE for 'ref-or-null'. */
00607   bool check_null;
00608   /*
00609     The "having" clause. This clause (further reffered to as "artificial
00610     having") was inserted by subquery transformation code. It contains
00611     Item(s) that have a side-effect: they record whether the subquery has
00612     produced a row with NULL certain components. We need to use it for cases
00613     like
00614       (oe1, oe2) IN (SELECT t.key, t.no_key FROM t1)
00615     where we do index lookup on t.key=oe1 but need also to check if there
00616     was a row such that t.no_key IS NULL.
00617 
00618     NOTE: This is currently here and not in the uniquesubquery_engine. Ideally
00619     it should have been in uniquesubquery_engine in order to allow execution of
00620     subqueries like
00621 
00622       (oe1, oe2) IN (SELECT primary_key, non_key_maybe_null_field FROM tbl)
00623 
00624     We could use uniquesubquery_engine for the first component and let
00625     Item_is_not_null_test( non_key_maybe_null_field) to handle the second.
00626 
00627     However, subqueries like the above are currently not handled by index
00628     lookup-based subquery engines, the engine applicability check misses
00629     them: it doesn't switch the engine for case of artificial having and
00630     [eq_]ref access (only for artifical having + ref_or_null or no having).
00631     The above example subquery is handled as a full-blown SELECT with eq_ref
00632     access to one table.
00633 
00634     Due to this limitation, the "artificial having" currently needs to be
00635     checked by only in indexsubquery_engine.
00636   */
00637   Item *having;
00638 public:
00639 
00640   // constructor can assign Session because it will be called after Join::prepare
00641   subselect_indexsubquery_engine(Session *session_arg, JoinTable *tab_arg,
00642          Item_subselect *subs, Item *where,
00643                                  Item *having_arg, bool chk_null)
00644     :subselect_uniquesubquery_engine(session_arg, tab_arg, subs, where),
00645      check_null(chk_null),
00646      having(having_arg)
00647   {}
00648   int exec();
00649   virtual void print (String *str);
00650   virtual enum_engine_type engine_type() { return INDEXSUBQUERY_ENGINE; }
00651 };
00652 
00653 
00654 inline bool Item_subselect::is_evaluated() const
00655 {
00656   return engine->is_executed();
00657 }
00658 
00659 
00660 inline bool Item_subselect::is_uncacheable() const
00661 {
00662   return engine->uncacheable();
00663 }
00664 
00665 
00672 class subselect_hash_sj_engine: public subselect_uniquesubquery_engine
00673 {
00674 protected:
00675   /* TRUE if the subquery was materialized into a temp table. */
00676   bool is_materialized;
00677   /*
00678     The old engine already chosen at parse time and stored in permanent memory.
00679     Through this member we can re-create and re-prepare materialize_join for
00680     each execution of a prepared statement. We akso resuse the functionality
00681     of subselect_single_select_engine::[prepare | cols].
00682   */
00683   subselect_single_select_engine *materialize_engine;
00684   /*
00685     QEP to execute the subquery and materialize its result into a
00686     temporary table. Created during the first call to exec().
00687   */
00688   Join *materialize_join;
00689   /* Temp table context of the outer select's JOIN. */
00690   Tmp_Table_Param *tmp_param;
00691 
00692 public:
00693   subselect_hash_sj_engine(Session *session_in, Item_subselect *in_predicate,
00694                                subselect_single_select_engine *old_engine)
00695     :subselect_uniquesubquery_engine(session_in, NULL, in_predicate, NULL),
00696     is_materialized(false), materialize_engine(old_engine),
00697     materialize_join(NULL), tmp_param(NULL)
00698   {}
00699   ~subselect_hash_sj_engine();
00700 
00701   bool init_permanent(List<Item> *tmp_columns);
00702   bool init_runtime();
00703   void cleanup();
00704   int prepare() { return 0; }
00705   int exec();
00706   virtual void print (String *str);
00707   uint32_t cols()
00708   {
00709     return materialize_engine->cols();
00710   }
00711   virtual enum_engine_type engine_type() { return HASH_SJ_ENGINE; }
00712 };
00713 
00714 } /* namespace drizzled */
00715