1 #ifndef SQL_EXECUTOR_INCLUDED
2 #define SQL_EXECUTOR_INCLUDED
3 
4 /* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights
5  * reserved.
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License, version 2.0,
9    as published by the Free Software Foundation.
10 
11    This program is also distributed with certain software (including
12    but not limited to OpenSSL) that is licensed under separate terms,
13    as designated in a particular file or component or in included license
14    documentation.  The authors of MySQL hereby grant you an additional
15    permission to link the program and your derivative works with the
16    separately licensed software that they have included with MySQL.
17 
18    This program is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21    GNU General Public License, version 2.0, for more details.
22 
23    You should have received a copy of the GNU General Public License
24    along with this program; if not, write to the Free Software
25    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
26 
27 /** @file Classes for query execution */
28 
29 #include "records.h"               // READ_RECORD
30 #include "sql_opt_exec_shared.h"   // QEP_shared_owner
31 
32 class JOIN;
33 class JOIN_TAB;
34 class QEP_TAB;
35 typedef struct st_table_ref TABLE_REF;
36 typedef struct st_position POSITION;
37 
38 /**
39    Possible status of a "nested loop" operation (Next_select_func family of
40    functions).
41    All values except NESTED_LOOP_OK abort the nested loop.
42 */
43 enum enum_nested_loop_state
44 {
45   /**
46      Thread shutdown was requested while processing the record
47      @todo could it be merged with NESTED_LOOP_ERROR? Why two distinct states?
48   */
49   NESTED_LOOP_KILLED= -2,
50   /// A fatal error (like table corruption) was detected
51   NESTED_LOOP_ERROR= -1,
52   /// Record has been successfully handled
53   NESTED_LOOP_OK= 0,
54   /**
55      Record has been successfully handled; additionally, the nested loop
56      produced the number of rows specified in the LIMIT clause for the query.
57   */
58   NESTED_LOOP_QUERY_LIMIT= 3,
59   /**
60      Record has been successfully handled; additionally, there is a cursor and
61      the nested loop algorithm produced the number of rows that is specified
62      for current cursor fetch operation.
63   */
64   NESTED_LOOP_CURSOR_LIMIT= 4
65 };
66 
67 
68 typedef enum_nested_loop_state
69 (*Next_select_func)(JOIN *, class QEP_TAB *, bool);
70 
71 /*
72   Temporary table used by semi-join DuplicateElimination strategy
73 
74   This consists of the temptable itself and data needed to put records
75   into it. The table's DDL is as follows:
76 
77     CREATE TABLE tmptable (col VARCHAR(n) BINARY, PRIMARY KEY(col));
78 
79   where the primary key can be replaced with unique constraint if n exceeds
80   the limit (as it is always done for query execution-time temptables).
81 
82   The record value is a concatenation of rowids of tables from the join we're
83   executing. If a join table is on the inner side of the outer join, we
84   assume that its rowid can be NULL and provide means to store this rowid in
85   the tuple.
86 */
87 
88 class SJ_TMP_TABLE : public Sql_alloc
89 {
90 public:
SJ_TMP_TABLE()91   SJ_TMP_TABLE():hash_field(NULL)
92   {}
93   /*
94     Array of pointers to tables whose rowids compose the temporary table
95     record.
96   */
97   class TAB
98   {
99   public:
100     QEP_TAB *qep_tab;
101     uint rowid_offset;
102     ushort null_byte;
103     uchar null_bit;
104   };
105   TAB *tabs;
106   TAB *tabs_end;
107 
108   /*
109     is_confluent==TRUE means this is a special case where the temptable record
110     has zero length (and presence of a unique key means that the temptable can
111     have either 0 or 1 records).
112     In this case we don't create the physical temptable but instead record
113     its state in SJ_TMP_TABLE::have_confluent_record.
114   */
115   bool is_confluent;
116 
117   /*
118     When is_confluent==TRUE: the contents of the table (whether it has the
119     record or not).
120   */
121   bool have_confluent_row;
122 
123   /* table record parameters */
124   uint null_bits;
125   uint null_bytes;
126   uint rowid_len;
127 
128   /* The temporary table itself (NULL means not created yet) */
129   TABLE *tmp_table;
130 
131   /*
132     These are the members we got from temptable creation code. We'll need
133     them if we'll need to convert table from HEAP to MyISAM/Maria.
134   */
135   MI_COLUMNDEF *start_recinfo;
136   MI_COLUMNDEF *recinfo;
137 
138   /* Pointer to next table (next->start_idx > this->end_idx) */
139   SJ_TMP_TABLE *next;
140   /* Calc hash instead of too long key */
141   Field_longlong *hash_field;
142 };
143 
144 
145  /**
146   Executor structure for the materialized semi-join info, which contains
147    - Description of expressions selected from subquery
148    - The sj-materialization temporary table
149 */
150 class Semijoin_mat_exec : public Sql_alloc
151 {
152 public:
Semijoin_mat_exec(TABLE_LIST * sj_nest,bool is_scan,uint table_count,uint mat_table_index,uint inner_table_index)153   Semijoin_mat_exec(TABLE_LIST *sj_nest, bool is_scan, uint table_count,
154                     uint mat_table_index, uint inner_table_index)
155     :sj_nest(sj_nest), is_scan(is_scan), table_count(table_count),
156      mat_table_index(mat_table_index), inner_table_index(inner_table_index),
157     table_param(), table(NULL)
158   {}
~Semijoin_mat_exec()159   ~Semijoin_mat_exec()
160   {}
161   TABLE_LIST *const sj_nest;    ///< Semi-join nest for this materialization
162   const bool is_scan;           ///< TRUE if executing a scan, FALSE if lookup
163   const uint table_count;       ///< Number of tables in the sj-nest
164   const uint mat_table_index;   ///< Index in join_tab for materialized table
165   const uint inner_table_index; ///< Index in join_tab for first inner table
166   Temp_table_param table_param; ///< The temptable and its related info
167   TABLE *table;                 ///< Reference to temporary table
168 };
169 
170 
171 
172 /**
173   QEP_operation is an interface class for operations in query execution plan.
174 
175   Currently following operations are implemented:
176     JOIN_CACHE      - caches partial join result and joins with attached table
177     QEP_tmp_table   - materializes join result in attached table
178 
179   An operation's life cycle is as follows:
180   .) it is initialized on the init() call
181   .) accumulates records one by one when put_record() is called.
182   .) finalize record sending when end_send() is called.
183   .) free all internal buffers on the free() call.
184 
185   Each operation is attached to a join_tab, to which exactly depends on the
186   operation type: JOIN_CACHE is attached to the table following the table
187   being cached, QEP_tmp_buffer is attached to a tmp table.
188 */
189 
190 class QEP_operation :public Sql_alloc
191 {
192 public:
193   // Type of the operation
194   enum enum_op_type { OT_CACHE, OT_TMP_TABLE };
195   /**
196     For JOIN_CACHE : Table to be joined with the partial join records from
197                      the cache
198     For JOIN_TMP_BUFFER : join_tab of tmp table
199   */
200   QEP_TAB *qep_tab;
201 
QEP_operation()202   QEP_operation(): qep_tab(NULL) {};
QEP_operation(QEP_TAB * qep_tab_arg)203   QEP_operation(QEP_TAB *qep_tab_arg): qep_tab(qep_tab_arg) {};
~QEP_operation()204   virtual ~QEP_operation() {};
205   virtual enum_op_type type()= 0;
206   /**
207     Initialize operation's internal state.  Called once per query execution.
208   */
init()209   virtual int init() { return 0; };
210   /**
211     Put a new record into the operation's buffer
212     @return
213       return one of enum_nested_loop_state values.
214   */
215   virtual enum_nested_loop_state put_record()= 0;
216   /**
217     Finalize records sending.
218   */
219   virtual enum_nested_loop_state end_send()= 0;
220   /**
221     Internal state cleanup.
222   */
mem_free()223   virtual void mem_free() {};
224 };
225 
226 
227 /**
228   @brief
229     Class for accumulating join result in a tmp table, grouping them if
230     necessary, and sending further.
231 
232   @details
233     Join result records are accumulated on the put_record() call.
234     The accumulation process is determined by the write_func, it could be:
235       end_write          Simply store all records in tmp table.
236       end_write_group    Perform grouping using join->group_fields,
237                          records are expected to be sorted.
238       end_update         Perform grouping using the key generated on tmp
239                          table. Input records aren't expected to be sorted.
240                          Tmp table uses the heap engine
241       end_update_unique  Same as above, but the engine is myisam.
242 
243     Lazy table initialization is used - the table will be instantiated and
244     rnd/index scan started on the first put_record() call.
245 
246 */
247 
248 class QEP_tmp_table :public QEP_operation
249 {
250 public:
QEP_tmp_table(QEP_TAB * qep_tab_arg)251   QEP_tmp_table(QEP_TAB *qep_tab_arg) :
252   QEP_operation(qep_tab_arg), write_func(NULL)
253   {};
type()254   enum_op_type type() { return OT_TMP_TABLE; }
put_record()255   enum_nested_loop_state put_record() { return put_record(false); };
256   /*
257     Send the result of operation further (to a next operation/client)
258     This function is called after all records were put into the buffer
259     (determined by the caller).
260 
261     @return return one of enum_nested_loop_state values.
262   */
263   enum_nested_loop_state end_send();
264   /** write_func setter */
set_write_func(Next_select_func new_write_func)265   void set_write_func(Next_select_func new_write_func)
266   {
267     write_func= new_write_func;
268   }
269 
270 private:
271   /** Write function that would be used for saving records in tmp table. */
272   Next_select_func write_func;
273   enum_nested_loop_state put_record(bool end_of_records);
274   MY_ATTRIBUTE((warn_unused_result))
275   bool prepare_tmp_table();
276 };
277 
278 
279 void setup_tmptable_write_func(QEP_TAB *tab);
280 enum_nested_loop_state sub_select_op(JOIN *join, QEP_TAB *qep_tab, bool
281                                         end_of_records);
282 enum_nested_loop_state end_send_group(JOIN *join, QEP_TAB *qep_tab,
283                                       bool end_of_records);
284 enum_nested_loop_state end_write_group(JOIN *join, QEP_TAB *qep_tab,
285                                        bool end_of_records);
286 enum_nested_loop_state sub_select(JOIN *join,QEP_TAB *qep_tab, bool
287                                   end_of_records);
288 enum_nested_loop_state
289 evaluate_join_record(JOIN *join, QEP_TAB *qep_tab, int error);
290 
291 
292 
293 MY_ATTRIBUTE((warn_unused_result))
294 bool copy_fields(Temp_table_param *param, const THD *thd);
295 
296 bool copy_funcs(Func_ptr_array*, const THD *thd);
297 bool cp_buffer_from_ref(THD *thd, TABLE *table, TABLE_REF *ref);
298 
299 /** Help function when we get some an error from the table handler. */
300 int report_handler_error(TABLE *table, int error);
301 
302 int safe_index_read(QEP_TAB *tab);
303 st_sort_field * make_unireg_sortorder(ORDER *order, uint *length,
304                                       st_sort_field *sortorder);
305 
306 int join_read_const_table(JOIN_TAB *tab, POSITION *pos);
307 void join_read_key_unlock_row(st_join_table *tab);
308 void join_const_unlock_row(st_join_table *tab);
309 int join_init_quick_read_record(QEP_TAB *tab);
310 int join_init_read_record(QEP_TAB *tab);
311 int join_read_first(QEP_TAB *tab);
312 int join_read_last(QEP_TAB *tab);
313 int join_read_last_key(QEP_TAB *tab);
314 int join_materialize_derived(QEP_TAB *tab);
315 int join_materialize_semijoin(QEP_TAB *tab);
316 int join_read_prev_same(READ_RECORD *info);
317 
318 int do_sj_dups_weedout(THD *thd, SJ_TMP_TABLE *sjtbl);
319 int test_if_item_cache_changed(List<Cached_item> &list);
320 
321 // Create list for using with tempory table
322 bool change_to_use_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array,
323 				     List<Item> &new_list1,
324 				     List<Item> &new_list2,
325 				     uint elements, List<Item> &items);
326 // Create list for using with tempory table
327 bool change_refs_to_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array,
328 				      List<Item> &new_list1,
329 				      List<Item> &new_list2,
330 				      uint elements, List<Item> &items);
331 bool alloc_group_fields(JOIN *join, ORDER *group);
332 bool prepare_sum_aggregators(Item_sum **func_ptr, bool need_distinct);
333 bool setup_sum_funcs(THD *thd, Item_sum **func_ptr);
334 bool make_group_fields(JOIN *main_join, JOIN *curr_join);
335 bool setup_copy_fields(THD *thd, Temp_table_param *param,
336 		  Ref_ptr_array ref_pointer_array,
337 		  List<Item> &res_selected_fields, List<Item> &res_all_fields,
338 		  uint elements, List<Item> &all_fields);
339 bool check_unique_constraint(TABLE *table);
340 ulonglong unique_hash(Field *field, ulonglong *hash);
341 
342 class Opt_trace_object;
343 
344 class QEP_TAB : public Sql_alloc, public QEP_shared_owner
345 {
346 public:
QEP_TAB()347   QEP_TAB() :
348     QEP_shared_owner(),
349     table_ref(NULL),
350     flush_weedout_table(NULL),
351     check_weed_out_table(NULL),
352     firstmatch_return(NO_PLAN_IDX),
353     loosescan_key_len(0),
354     loosescan_buf(NULL),
355     match_tab(NO_PLAN_IDX),
356     found_match(false),
357     found(false),
358     not_null_compl(false),
359     first_unmatched(NO_PLAN_IDX),
360     materialized(false),
361     materialize_table(NULL),
362     read_first_record(NULL),
363     next_select(NULL),
364     read_record(),
365     save_read_first_record(NULL),
366     save_read_record(NULL),
367     used_null_fields(false),
368     used_uneven_bit_fields(false),
369     keep_current_rowid(false),
370     copy_current_rowid(NULL),
371     distinct(false),
372     not_used_in_distinct(false),
373     cache_idx_cond(NULL),
374     having(NULL),
375     op(NULL),
376     tmp_table_param(NULL),
377     filesort(NULL),
378     fields(NULL),
379     all_fields(NULL),
380     ref_array(NULL),
381     send_records(0),
382     quick_traced_before(false),
383     m_condition_optim(NULL),
384     m_quick_optim(NULL),
385     m_keyread_optim(false)
386   {
387     /**
388        @todo Add constructor to READ_RECORD.
389        All users do init_read_record(), which does memset(),
390        rather than invoking a constructor.
391     */
392   }
393 
394   /// Initializes the object from a JOIN_TAB
395   void init(JOIN_TAB *jt);
396   // Cleans up.
397   void cleanup();
398 
399   // Getters and setters
400 
condition_optim()401   Item *condition_optim() const { return m_condition_optim; }
quick_optim()402   QUICK_SELECT_I *quick_optim() const { return m_quick_optim; }
set_quick_optim()403   void set_quick_optim() { m_quick_optim= quick(); }
set_condition_optim()404   void set_condition_optim() { m_condition_optim= condition(); }
keyread_optim()405   bool keyread_optim() const { return m_keyread_optim; }
set_keyread_optim()406   void set_keyread_optim()
407   {
408     if (table())
409       m_keyread_optim= table()->key_read;
410   }
411 
set_table(TABLE * t)412   void set_table(TABLE *t)
413   {
414     m_qs->set_table(t);
415     if (t)
416       t->reginfo.qep_tab= this;
417   }
418 
419   /// @returns semijoin strategy for this table.
420   uint get_sj_strategy() const;
421 
422   /// Return true if join_tab should perform a FirstMatch action
do_firstmatch()423   bool do_firstmatch() const { return firstmatch_return != NO_PLAN_IDX; }
424 
425   /// Return true if join_tab should perform a LooseScan action
do_loosescan()426   bool do_loosescan() const { return loosescan_key_len; }
427 
428   /// Return true if join_tab starts a Duplicate Weedout action
starts_weedout()429   bool starts_weedout() const { return flush_weedout_table; }
430 
431   /// Return true if join_tab finishes a Duplicate Weedout action
finishes_weedout()432   bool finishes_weedout() const { return check_weed_out_table; }
433 
434   bool prepare_scan();
435 
436   /**
437     A helper function that allocates appropriate join cache object and
438     sets next_select function of previous tab.
439   */
440   void init_join_cache(JOIN_TAB *join_tab);
441 
442   /**
443      @returns query block id for an inner table of materialized semi-join, and
444               0 for all other tables.
445      @note implementation is not efficient (loops over all tables) - use this
446      function only in EXPLAIN.
447   */
448   uint sjm_query_block_id() const;
449 
450   /// @returns whether this is doing QS_DYNAMIC_RANGE
dynamic_range()451   bool dynamic_range() const
452   {
453     if (!position())
454       return false; // tmp table
455     return read_first_record == join_init_quick_read_record;
456   }
457 
458   bool use_order() const; ///< Use ordering provided by chosen index?
459   bool sort_table();
460   bool remove_duplicates();
461 
skip_record(THD * thd,bool * skip_record_arg)462   inline bool skip_record(THD *thd, bool *skip_record_arg)
463   {
464     *skip_record_arg= condition() ? condition()->val_int() == FALSE : FALSE;
465     return thd->is_error();
466   }
467 
468   /**
469      Used to begin a new execution of a subquery. Necessary if this subquery
470      has done a filesort which which has cleared condition/quick.
471   */
restore_quick_optim_and_condition()472   void restore_quick_optim_and_condition()
473   {
474     if (m_condition_optim)
475       set_condition(m_condition_optim);
476     if (m_quick_optim)
477       set_quick(m_quick_optim);
478   }
479 
480   void pick_table_access_method(const JOIN_TAB *join_tab);
481   void set_pushed_table_access_method(void);
482   void push_index_cond(const JOIN_TAB *join_tab,
483                        uint keyno, Opt_trace_object *trace_obj);
484 
485   /// @return the index used for a table in a QEP
486   uint effective_index() const;
487 
488   bool pfs_batch_update(JOIN *join);
489 
490 public:
491   /// Pointer to table reference
492   TABLE_LIST *table_ref;
493 
494   /* Variables for semi-join duplicate elimination */
495   SJ_TMP_TABLE *flush_weedout_table;
496   SJ_TMP_TABLE *check_weed_out_table;
497 
498   /*
499     If set, means we should stop join enumeration after we've got the first
500     match and return to the specified join tab. May be PRE_FIRST_PLAN_IDX
501     which means stopping join execution after the first match.
502   */
503   plan_idx firstmatch_return;
504 
505   /*
506     Length of key tuple (depends on #keyparts used) to store in loosescan_buf.
507     If zero, means that loosescan is not used.
508   */
509   uint loosescan_key_len;
510 
511   /* Buffer to save index tuple to be able to skip duplicates */
512   uchar *loosescan_buf;
513 
514   /*
515     If doing a LooseScan, this QEP is the first (i.e.  "driving")
516     QEP_TAB, and match_tab points to the last QEP_TAB handled by the strategy.
517     match_tab->found_match should be checked to see if the current value group
518     had a match.
519     If doing a FirstMatch, check this QEP_TAB to see if there is a match.
520     Unless the FirstMatch performs a "split jump", this is equal to the
521     current QEP_TAB.
522   */
523   plan_idx match_tab;
524 
525   /*
526     Used by FirstMatch and LooseScan. TRUE <=> there is a matching
527     record combination
528   */
529   bool found_match;
530 
531   /**
532     Used to decide whether an inner table of an outer join should produce NULL
533     values. If it is true after a call to evaluate_join_record(), the join
534     condition has been satisfied for at least one row from the inner
535     table. This member is not really manipulated by this class, see sub_select
536     for details on its use.
537   */
538   bool found;
539 
540   /**
541     This member is true as long as we are evaluating rows from the inner
542     tables of an outer join. If none of these rows satisfy the join condition,
543     we generated NULL-complemented rows and set this member to false. In the
544     meantime, the value may be read by triggered conditions, see
545     Item_func_trig_cond::val_int().
546   */
547   bool not_null_compl;
548 
549   plan_idx first_unmatched; /**< used for optimization purposes only   */
550 
551   /// For a materializable derived or SJ table: true if has been materialized
552   bool materialized;
553 
554   READ_RECORD::Setup_func materialize_table;
555   /**
556      Initialize table for reading and fetch the first row from the table. If
557      table is a materialized derived one, function must materialize it with
558      prepare_scan().
559   */
560   READ_RECORD::Setup_func read_first_record;
561   Next_select_func next_select;
562   READ_RECORD read_record;
563   /*
564     The following two fields are used for a [NOT] IN subquery if it is
565     executed by an alternative full table scan when the left operand of
566     the subquery predicate is evaluated to NULL.
567   */
568   READ_RECORD::Setup_func save_read_first_record;/* to save read_first_record */
569   READ_RECORD::Read_func save_read_record;/* to save read_record.read_record */
570 
571   // join-cache-related members
572   bool          used_null_fields;
573   bool          used_uneven_bit_fields;
574 
575   /*
576     Used by DuplicateElimination. tab->table->ref must have the rowid
577     whenever we have a current record. copy_current_rowid needed because
578     we cannot bind to the rowid buffer before the table has been opened.
579   */
580   bool keep_current_rowid;
581   st_cache_field *copy_current_rowid;
582 
583   /** TRUE <=> remove duplicates on this table. */
584   bool distinct;
585 
586   bool not_used_in_distinct;
587 
588   /// Index condition for BKA access join
589   Item *cache_idx_cond;
590 
591   /** HAVING condition for checking prior saving a record into tmp table*/
592   Item *having;
593 
594   QEP_operation *op;
595 
596   /* Tmp table info */
597   Temp_table_param *tmp_table_param;
598 
599   /* Sorting related info */
600   Filesort *filesort;
601 
602   /**
603     List of topmost expressions in the select list. The *next* JOIN TAB
604     in the plan should use it to obtain correct values. Same applicable to
605     all_fields. These lists are needed because after tmp tables functions
606     will be turned to fields. These variables are pointing to
607     tmp_fields_list[123]. Valid only for tmp tables and the last non-tmp
608     table in the query plan.
609     @see JOIN::make_tmp_tables_info()
610   */
611   List<Item> *fields;
612   /** List of all expressions in the select list */
613   List<Item> *all_fields;
614   /*
615     Pointer to the ref array slice which to switch to before sending
616     records. Valid only for tmp tables.
617   */
618   Ref_ptr_array *ref_array;
619 
620   /** Number of records saved in tmp table */
621   ha_rows send_records;
622 
623   /**
624     Used for QS_DYNAMIC_RANGE, i.e., "Range checked for each record".
625     Used by optimizer tracing to decide whether or not dynamic range
626     analysis of this select has been traced already. If optimizer
627     trace option DYNAMIC_RANGE is enabled, range analysis will be
628     traced with different ranges for every record to the left of this
629     table in the join. If disabled, range analysis will only be traced
630     for the first range.
631   */
632   bool quick_traced_before;
633 
634   /// @See m_quick_optim
635   Item          *m_condition_optim;
636 
637   /**
638      m_quick is the quick "to be used at this stage of execution".
639      It can happen that filesort uses the quick (produced by the optimizer) to
640      produce a sorted result, then the read of this result has to be done
641      without "quick", so we must reset m_quick to NULL, but we want to delay
642      freeing of m_quick or it would close the filesort's result and the table
643      prematurely.
644      In that case, we move m_quick to m_quick_optim (=> delay deletion), reset
645      m_quick to NULL (read of filesort's result will be without quick); if
646      this is a subquery which is later executed a second time,
647      QEP_TAB::reset() will restore the quick from m_quick_optim into m_quick.
648      quick_optim stands for "the quick decided by the optimizer".
649      EXPLAIN reads this member and m_condition_optim; so if you change them
650      after exposing the plan (setting plan_state), do it with the
651      LOCK_query_plan mutex.
652   */
653   QUICK_SELECT_I *m_quick_optim;
654 
655   /**
656      True if only index is going to be read for this table. This is the
657      optimizer's decision.
658   */
659   bool m_keyread_optim;
660 
661   QEP_TAB(const QEP_TAB&);                      // not defined
662   QEP_TAB& operator=(const QEP_TAB&);           // not defined
663 };
664 
665 
666 /**
667    @returns a pointer to the QEP_TAB whose index is qtab->member. For
668    example, QEP_AT(x,first_inner) is the first_inner table of x.
669 */
670 #define QEP_AT(qtab,member) (qtab->join()->qep_tab[qtab->member])
671 
672 
673 /**
674    Use this class when you need a QEP_TAB not connected to any JOIN_TAB.
675 */
676 class QEP_TAB_standalone : public Sql_alloc
677 {
678 public:
QEP_TAB_standalone()679   QEP_TAB_standalone() { m_qt.set_qs(&m_qs); }
~QEP_TAB_standalone()680   ~QEP_TAB_standalone() { m_qt.cleanup(); }
681   /// @returns access to the QEP_TAB
as_QEP_TAB()682   QEP_TAB &as_QEP_TAB() { return m_qt; }
683 private:
684   QEP_shared m_qs;
685   QEP_TAB m_qt;
686 };
687 
688 #endif /* SQL_EXECUTOR_INCLUDED */
689