105
class QuickSelectInterface
118
ha_rows records; /* estimate of # of records to be retrieved */
119
double read_time; /* time to perform this retrieval */
109
ha_rows records; /**< estimate of # of records to be retrieved */
110
double read_time; /**< time to perform this retrieval */
122
113
Index this quick select uses, or MAX_KEY for quick selects
123
114
that use several indexes
128
118
Total length of first used_key_parts parts of the key.
129
119
Applicable if index!= MAX_KEY.
131
uint max_used_key_length;
134
Max. number of (first) key parts this quick select uses for retrieval.
121
uint32_t max_used_key_length;
123
Maximum number of (first) key parts this quick select uses for retrieval.
135
124
eg. for "(key1p1=c1 AND key1p2=c2) OR key1p1=c2" used_key_parts == 2.
136
125
Applicable if index!= MAX_KEY.
138
127
For QUICK_GROUP_MIN_MAX_SELECT it includes MIN/MAX argument keyparts.
143
virtual ~QUICK_SELECT_I(){};
146
Do post-constructor initialization.
150
init() performs initializations that should have been in constructor if
151
it was possible to return errors from constructors. The join optimizer may
152
create and then delete quick selects without retrieving any rows so init()
153
must not contain any IO or CPU intensive code.
155
If init() call fails the only valid action is to delete this quick select,
156
reset() and get_next() must not be called.
162
virtual int init() = 0;
165
Initialize quick select for row retrieval.
169
reset() should be called when it is certain that row retrieval will be
170
necessary. This call may do heavyweight initialization like buffering first
171
N records etc. If reset() call fails get_next() must not be called.
172
Note that reset() may be called several times if
173
* the quick select is executed in a subselect
174
* a JOIN buffer is used
180
virtual int reset(void) = 0;
182
virtual int get_next() = 0; /* get next record to retrieve */
184
/* Range end should be called when we have looped over the whole index */
129
uint32_t used_key_parts;
131
* The rowid of last row retrieved by this quick select. This is used only when
132
* doing ROR-index_merge selects
134
unsigned char *last_rowid;
137
* Table record buffer used by this quick select.
139
unsigned char *record;
141
QuickSelectInterface();
142
virtual ~QuickSelectInterface(){};
145
* Do post-constructor initialization.
149
* Performs initializations that should have been in constructor if
150
* it was possible to return errors from constructors. The join optimizer may
151
* create and then delete quick selects without retrieving any rows so init()
152
* must not contain any IO or CPU intensive code.
154
* If init() call fails the only valid action is to delete this quick select,
155
* reset() and get_next() must not be called.
162
virtual int init() = 0;
165
* Initializes quick select for row retrieval.
169
* Should be called when it is certain that row retrieval will be
170
* necessary. This call may do heavyweight initialization like buffering first
171
* N records etc. If reset() call fails get_next() must not be called.
172
* Note that reset() may be called several times if
173
* - the quick select is executed in a subselect
174
* - a JOIN buffer is used
181
virtual int reset(void) = 0;
182
/** Gets next record to retrieve */
183
virtual int get_next() = 0;
185
/** Range end should be called when we have looped over the whole index */
185
186
virtual void range_end() {}
187
virtual bool reverse_sorted() = 0;
188
virtual bool unique_key_range() { return false; }
192
QS_TYPE_INDEX_MERGE = 1,
193
QS_TYPE_RANGE_DESC = 2,
194
QS_TYPE_ROR_INTERSECT = 4,
195
QS_TYPE_ROR_UNION = 5,
196
QS_TYPE_GROUP_MIN_MAX = 6
188
virtual bool reverse_sorted() const = 0;
190
virtual bool unique_key_range() const
198
QS_TYPE_INDEX_MERGE= 1,
199
QS_TYPE_RANGE_DESC= 2,
200
QS_TYPE_ROR_INTERSECT= 4,
201
QS_TYPE_ROR_UNION= 5,
202
QS_TYPE_GROUP_MIN_MAX= 6
199
/* Get type of this quick select - one of the QS_TYPE_* values */
200
virtual int get_type() = 0;
203
Initialize this quick select as a merged scan inside a ROR-union or a ROR-
204
intersection scan. The caller must not additionally call init() if this
207
init_ror_merged_scan()
208
reuse_handler If true, the quick select may use table->handler,
209
otherwise it must create and use a separate handler
215
virtual int init_ror_merged_scan(bool reuse_handler __attribute__((unused)))
216
{ assert(0); return 1; }
219
Save ROWID of last retrieved row in file->ref. This used in ROR-merging.
205
/** Returns the type of this quick select - one of the QS_TYPE_* values */
206
virtual int get_type() const = 0;
209
* Initialize this quick select as a merged scan inside a ROR-union or a ROR-
210
* intersection scan. The caller must not additionally call init() if this
211
* function is called.
213
* @param If true, the quick select may use table->Cursor,
214
* otherwise it must create and use a separate Cursor
222
virtual int init_ror_merged_scan(bool)
229
* Save ROWID of last retrieved row in file->ref. This used in ROR-merging.
221
231
virtual void save_last_pos(){};
224
Append comma-separated list of keys this quick select uses to key_names;
225
append comma-separated list of corresponding used lengths to used_lengths.
226
This is used by select_describe.
228
virtual void add_keys_and_lengths(String *key_names,
229
String *used_lengths)=0;
232
Append text representation of quick select structure (what and how is
233
merged) to str. The result is added to "Extra" field in EXPLAIN output.
234
This function is implemented only by quick selects that merge other quick
235
selects output and/or can produce output suitable for merging.
237
virtual void add_info_string(String *str __attribute__((unused))) {};
239
Return 1 if any index used by this quick select
240
uses field which is marked in passed bitmap.
242
virtual bool is_keys_used(const MY_BITMAP *fields);
245
rowid of last row retrieved by this quick select. This is used only when
246
doing ROR-index_merge selects
251
Table record buffer used by this quick select.
234
* Append comma-separated list of keys this quick select uses to key_names;
235
* append comma-separated list of corresponding used lengths to used_lengths.
237
* @note This is used by during explain plan.
239
virtual void add_keys_and_lengths(String *key_names, String *used_lengths)=0;
242
* Append text representation of quick select structure (what and how is
243
* merged) to str. The result is added to "Extra" field in EXPLAIN output.
247
* This function is implemented only by quick selects that merge other quick
248
* selects output and/or can produce output suitable for merging.
250
virtual void add_info_string(String *)
254
* Returns true if any index used by this quick select
255
* uses field which is marked in passed bitmap.
257
virtual bool is_keys_used(const MyBitmap *fields);
257
260
struct st_qsel_param;
263
MRR range sequence, array<QUICK_RANGE> implementation: sequence traversal
262
class QuickRangeSelect;
265
* MRR range sequence, array<QuickRange> implementation: sequence traversal
266
268
typedef struct st_quick_range_seq_ctx
271
} QUICK_RANGE_SEQ_CTX;
273
range_seq_t quick_range_seq_init(void *init_param, uint n_ranges, uint flags);
274
uint quick_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range);
278
Quick select that does a range scan on a single key. The records are
279
returned in key order.
281
class QUICK_RANGE_SELECT : public QUICK_SELECT_I
285
DYNAMIC_ARRAY ranges; /* ordered array of range ptrs */
287
/* Members to deal with case when this quick select is a ROR-merged scan */
288
bool in_ror_merged_scan;
289
MY_BITMAP column_bitmap, *save_read_set, *save_write_set;
290
bool free_file; /* TRUE <=> this->file is "owned" by this quick select */
292
/* Range pointers to be used when not using MRR interface */
293
QUICK_RANGE **cur_range; /* current element in ranges */
294
QUICK_RANGE *last_range;
296
/* Members needed to use the MRR interface */
297
QUICK_RANGE_SEQ_CTX qr_traversal_ctx;
299
uint mrr_flags; /* Flags to be used with MRR interface */
301
uint mrr_buf_size; /* copy from thd->variables.read_rnd_buff_size */
302
HANDLER_BUFFER *mrr_buf_desc; /* the handler buffer */
304
/* Info about index we're scanning */
306
KEY_PART_INFO *key_part_info;
308
bool dont_free; /* Used by QUICK_SELECT_DESC */
310
int cmp_next(QUICK_RANGE *range);
311
int cmp_prev(QUICK_RANGE *range);
312
bool row_in_ranges();
316
QUICK_RANGE_SELECT(THD *thd, TABLE *table,uint index_arg,bool no_alloc,
317
MEM_ROOT *parent_alloc, bool *create_err);
318
~QUICK_RANGE_SELECT();
324
int get_next_prefix(uint prefix_length, key_part_map keypart_map,
326
bool reverse_sorted() { return 0; }
327
bool unique_key_range();
328
int init_ror_merged_scan(bool reuse_handler);
330
{ file->position(record); }
331
int get_type() { return QS_TYPE_RANGE; }
332
void add_keys_and_lengths(String *key_names, String *used_lengths);
333
void add_info_string(String *str);
335
/* Used only by QUICK_SELECT_DESC */
336
QUICK_RANGE_SELECT(const QUICK_RANGE_SELECT& org) : QUICK_SELECT_I()
338
memmove(this, &org, sizeof(*this));
340
Use default MRR implementation for reverse scans. No table engine
341
currently can do an MRR scan with output in reverse index order.
344
mrr_flags |= HA_MRR_USE_DEFAULT_IMPL;
347
friend class TRP_ROR_INTERSECT;
349
QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
350
struct st_table_ref *ref,
352
friend bool get_quick_keys(PARAM *param, QUICK_RANGE_SELECT *quick,
353
KEY_PART *key, SEL_ARG *key_tree,
354
uchar *min_key, uint min_key_flag,
355
uchar *max_key, uint max_key_flag);
356
friend QUICK_RANGE_SELECT *get_quick_select(PARAM*,uint idx,
361
friend class QUICK_SELECT_DESC;
362
friend class QUICK_INDEX_MERGE_SELECT;
363
friend class QUICK_ROR_INTERSECT_SELECT;
364
friend class QUICK_GROUP_MIN_MAX_SELECT;
365
friend uint quick_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range);
366
friend range_seq_t quick_range_seq_init(void *init_param,
367
uint n_ranges, uint flags);
368
friend void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
369
bool distinct,const char *message);
374
QUICK_INDEX_MERGE_SELECT - index_merge access method quick select.
376
QUICK_INDEX_MERGE_SELECT uses
377
* QUICK_RANGE_SELECTs to get rows
378
* Unique class to remove duplicate rows
380
INDEX MERGE OPTIMIZER
381
Current implementation doesn't detect all cases where index_merge could
382
be used, in particular:
383
* index_merge will never be used if range scan is possible (even if
384
range scan is more expensive)
386
* index_merge+'using index' is not supported (this the consequence of
387
the above restriction)
389
* If WHERE part contains complex nested AND and OR conditions, some ways
390
to retrieve rows using index_merge will not be considered. The choice
391
of read plan may depend on the order of conjuncts/disjuncts in WHERE
392
part of the query, see comments near imerge_list_or_list and
393
SEL_IMERGE::or_sel_tree_with_checks functions for details.
395
* There is no "index_merge_ref" method (but index_merge on non-first
396
table in join is possible with 'range checked for each record').
398
See comments around SEL_IMERGE class and test_quick_select for more
401
ROW RETRIEVAL ALGORITHM
403
index_merge uses Unique class for duplicates removal. index_merge takes
404
advantage of Clustered Primary Key (CPK) if the table has one.
405
The index_merge algorithm consists of two phases:
407
Phase 1 (implemented in QUICK_INDEX_MERGE_SELECT::prepare_unique):
410
activate 'index only';
411
while(retrieve next row for non-CPK scan)
413
if (there is a CPK scan and row will be retrieved by it)
416
put its rowid into Unique;
418
deactivate 'index only';
421
Phase 2 (implemented as sequence of QUICK_INDEX_MERGE_SELECT::get_next
426
retrieve all rows from row pointers stored in Unique;
428
retrieve all rows for CPK scan;
432
class QUICK_INDEX_MERGE_SELECT : public QUICK_SELECT_I
435
QUICK_INDEX_MERGE_SELECT(THD *thd, TABLE *table);
436
~QUICK_INDEX_MERGE_SELECT();
441
bool reverse_sorted() { return false; }
442
bool unique_key_range() { return false; }
443
int get_type() { return QS_TYPE_INDEX_MERGE; }
444
void add_keys_and_lengths(String *key_names, String *used_lengths);
445
void add_info_string(String *str);
446
bool is_keys_used(const MY_BITMAP *fields);
448
bool push_quick_back(QUICK_RANGE_SELECT *quick_sel_range);
450
/* range quick selects this index_merge read consists of */
451
List<QUICK_RANGE_SELECT> quick_selects;
453
/* quick select that uses clustered primary key (NULL if none) */
454
QUICK_RANGE_SELECT* pk_quick_select;
456
/* true if this select is currently doing a clustered PK scan */
461
int read_keys_and_merge();
463
/* used to get rows collected in Unique */
464
READ_RECORD read_record;
469
Rowid-Ordered Retrieval (ROR) index intersection quick select.
470
This quick select produces intersection of row sequences returned
471
by several QUICK_RANGE_SELECTs it "merges".
473
All merged QUICK_RANGE_SELECTs must return rowids in rowid order.
474
QUICK_ROR_INTERSECT_SELECT will return rows in rowid order, too.
476
All merged quick selects retrieve {rowid, covered_fields} tuples (not full
478
QUICK_ROR_INTERSECT_SELECT retrieves full records if it is not being used
479
by QUICK_ROR_INTERSECT_SELECT and all merged quick selects together don't
480
cover needed all fields.
482
If one of the merged quick selects is a Clustered PK range scan, it is
483
used only to filter rowid sequence produced by other merged quick selects.
486
class QUICK_ROR_INTERSECT_SELECT : public QUICK_SELECT_I
489
QUICK_ROR_INTERSECT_SELECT(THD *thd, TABLE *table,
490
bool retrieve_full_rows,
491
MEM_ROOT *parent_alloc);
492
~QUICK_ROR_INTERSECT_SELECT();
497
bool reverse_sorted() { return false; }
498
bool unique_key_range() { return false; }
499
int get_type() { return QS_TYPE_ROR_INTERSECT; }
500
void add_keys_and_lengths(String *key_names, String *used_lengths);
501
void add_info_string(String *str);
502
bool is_keys_used(const MY_BITMAP *fields);
503
int init_ror_merged_scan(bool reuse_handler);
504
bool push_quick_back(QUICK_RANGE_SELECT *quick_sel_range);
507
Range quick selects this intersection consists of, not including
510
List<QUICK_RANGE_SELECT> quick_selects;
513
Merged quick select that uses Clustered PK, if there is one. This quick
514
select is not used for row retrieval, it is used for row retrieval.
516
QUICK_RANGE_SELECT *cpk_quick;
518
MEM_ROOT alloc; /* Memory pool for this and merged quick selects data. */
519
THD *thd; /* current thread */
520
bool need_to_fetch_row; /* if true, do retrieve full table records. */
521
/* in top-level quick select, true if merged scans where initialized */
527
Rowid-Ordered Retrieval index union select.
528
This quick select produces union of row sequences returned by several
529
quick select it "merges".
531
All merged quick selects must return rowids in rowid order.
532
QUICK_ROR_UNION_SELECT will return rows in rowid order, too.
534
All merged quick selects are set not to retrieve full table records.
535
ROR-union quick select always retrieves full records.
539
class QUICK_ROR_UNION_SELECT : public QUICK_SELECT_I
542
QUICK_ROR_UNION_SELECT(THD *thd, TABLE *table);
543
~QUICK_ROR_UNION_SELECT();
548
bool reverse_sorted() { return false; }
549
bool unique_key_range() { return false; }
550
int get_type() { return QS_TYPE_ROR_UNION; }
551
void add_keys_and_lengths(String *key_names, String *used_lengths);
552
void add_info_string(String *str);
553
bool is_keys_used(const MY_BITMAP *fields);
555
bool push_quick_back(QUICK_SELECT_I *quick_sel_range);
557
List<QUICK_SELECT_I> quick_selects; /* Merged quick selects */
559
QUEUE queue; /* Priority queue for merge operation */
560
MEM_ROOT alloc; /* Memory pool for this and merged quick selects data. */
562
THD *thd; /* current thread */
563
uchar *cur_rowid; /* buffer used in get_next() */
564
uchar *prev_rowid; /* rowid of last row returned by get_next() */
565
bool have_prev_rowid; /* true if prev_rowid has valid data */
566
uint rowid_length; /* table rowid length */
568
static int queue_cmp(void *arg, uchar *val1, uchar *val2);
574
Index scan for GROUP-BY queries with MIN/MAX aggregate functions.
576
This class provides a specialized index access method for GROUP-BY queries
579
SELECT A_1,...,A_k, [B_1,...,B_m], [MIN(C)], [MAX(C)]
581
WHERE [RNG(A_1,...,A_p ; where p <= k)]
582
[AND EQ(B_1,...,B_m)]
584
[AND PA(A_i1,...,A_iq)]
585
GROUP BY A_1,...,A_k;
589
SELECT DISTINCT A_i1,...,A_ik
591
WHERE [RNG(A_1,...,A_p ; where p <= k)]
592
[AND PA(A_i1,...,A_iq)];
594
where all selected fields are parts of the same index.
595
The class of queries that can be processed by this quick select is fully
596
specified in the description of get_best_trp_group_min_max() in opt_range.cc.
598
The get_next() method directly produces result tuples, thus obviating the
599
need to call end_send_group() because all grouping is already done inside
602
Since one of the requirements is that all select fields are part of the same
603
index, this class produces only index keys, and not complete records.
606
class QUICK_GROUP_MIN_MAX_SELECT : public QUICK_SELECT_I
609
handler *file; /* The handler used to get data. */
610
JOIN *join; /* Descriptor of the current query */
611
KEY *index_info; /* The index chosen for data access */
612
uchar *record; /* Buffer where the next record is returned. */
613
uchar *tmp_record; /* Temporary storage for next_min(), next_max(). */
614
uchar *group_prefix; /* Key prefix consisting of the GROUP fields. */
615
uint group_prefix_len; /* Length of the group prefix. */
616
uint group_key_parts; /* A number of keyparts in the group prefix */
617
uchar *last_prefix; /* Prefix of the last group for detecting EOF. */
618
bool have_min; /* Specify whether we are computing */
619
bool have_max; /* a MIN, a MAX, or both. */
620
bool seen_first_key; /* Denotes whether the first key was retrieved.*/
621
KEY_PART_INFO *min_max_arg_part; /* The keypart of the only argument field */
622
/* of all MIN/MAX functions. */
623
uint min_max_arg_len; /* The length of the MIN/MAX argument field */
624
uchar *key_infix; /* Infix of constants from equality predicates. */
626
DYNAMIC_ARRAY min_max_ranges; /* Array of range ptrs for the MIN/MAX field. */
627
uint real_prefix_len; /* Length of key prefix extended with key_infix. */
628
uint real_key_parts; /* A number of keyparts in the above value. */
629
List<Item_sum> *min_functions;
630
List<Item_sum> *max_functions;
631
List_iterator<Item_sum> *min_functions_it;
632
List_iterator<Item_sum> *max_functions_it;
635
The following two members are public to allow easy access from
636
TRP_GROUP_MIN_MAX::make_quick()
638
MEM_ROOT alloc; /* Memory pool for this and quick_prefix_select data. */
639
QUICK_RANGE_SELECT *quick_prefix_select;/* For retrieval of group prefixes. */
642
int next_min_in_range();
643
int next_max_in_range();
646
void update_min_result();
647
void update_max_result();
649
QUICK_GROUP_MIN_MAX_SELECT(TABLE *table, JOIN *join, bool have_min,
650
bool have_max, KEY_PART_INFO *min_max_arg_part,
651
uint group_prefix_len, uint group_key_parts,
652
uint used_key_parts, KEY *index_info, uint
653
use_index, double read_cost, ha_rows records, uint
654
key_infix_len, uchar *key_infix, MEM_ROOT
656
~QUICK_GROUP_MIN_MAX_SELECT();
657
bool add_range(SEL_ARG *sel_range);
658
void update_key_stat();
659
void adjust_prefix_ranges();
660
bool alloc_buffers();
664
bool reverse_sorted() { return false; }
665
bool unique_key_range() { return false; }
666
int get_type() { return QS_TYPE_GROUP_MIN_MAX; }
667
void add_keys_and_lengths(String *key_names, String *used_lengths);
671
class QUICK_SELECT_DESC: public QUICK_RANGE_SELECT
674
QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q, uint used_key_parts,
677
bool reverse_sorted() { return 1; }
678
int get_type() { return QS_TYPE_RANGE_DESC; }
680
bool range_reads_after_key(QUICK_RANGE *range);
682
bool test_if_null_range(QUICK_RANGE *range, uint used_key_parts);
684
int reset(void) { rev_it.rewind(); return QUICK_RANGE_SELECT::reset(); }
685
List<QUICK_RANGE> rev_ranges;
686
List_iterator<QUICK_RANGE> rev_it;
690
class SQL_SELECT :public Sql_alloc {
273
} QuickRangeSequenceContext;
275
range_seq_t quick_range_seq_init(void *init_param, uint32_t n_ranges, uint32_t flags);
277
uint32_t quick_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range);
280
* Executor class for SELECT statements.
284
* The QuickSelectInterface member variable is the implementor
285
* of the SELECT execution.
287
class SqlSelect : public drizzled::memory::SqlAlloc
692
QUICK_SELECT_I *quick; // If quick-select used
693
COND *cond; // where condition
695
IO_CACHE file; // Positions to used records
696
ha_rows records; // Records in use if read from file
697
double read_time; // Time to read rows
698
key_map quick_keys; // Possible quick keys
699
key_map needed_reg; // Possible quick keys after prev tables.
700
table_map const_tables,read_tables;
290
QuickSelectInterface *quick; /**< If quick-select used */
291
COND *cond; /**< where condition */
293
IO_CACHE *file; /**< Positions to used records */
294
ha_rows records; /**< Records in use if read from file */
295
double read_time; /**< Time to read rows */
296
key_map quick_keys; /**< Possible quick keys */
297
key_map needed_reg; /**< Possible quick keys after prev tables. */
298
table_map const_tables;
299
table_map read_tables;
706
bool check_quick(THD *thd, bool force_quick_range, ha_rows limit)
710
return test_quick_select(thd, tmp, 0, limit, force_quick_range, false) < 0;
712
inline bool skip_record() { return cond ? cond->val_int() == 0 : 0; }
713
int test_quick_select(THD *thd, key_map keys, table_map prev_tables,
714
ha_rows limit, bool force_quick_range,
305
bool check_quick(Session *session, bool force_quick_range, ha_rows limit);
307
int test_quick_select(Session *session, key_map keys, table_map prev_tables,
308
ha_rows limit, bool force_quick_range,
715
309
bool ordered_output);
718
QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
719
struct st_table_ref *ref,
312
QuickRangeSelect *get_quick_select_for_ref(Session *session,
314
struct table_reference_st *ref,
720
315
ha_rows records);
721
uint get_index_for_order(TABLE *table, ORDER *order, ha_rows limit);
318
Create a QuickRangeSelect from given key and SEL_ARG tree for that key.
323
idx Index of used key in param->key.
324
key_tree SEL_ARG tree for the used key
325
mrr_flags MRR parameter for quick select
326
mrr_buf_size MRR parameter for quick select
327
parent_alloc If not NULL, use it to allocate memory for
328
quick select data. Otherwise use quick->alloc.
330
The caller must call QUICK_SELECT::init for returned quick select.
332
CAUTION! This function may change session->mem_root to a drizzled::memory::Root which will be
333
deallocated when the returned quick select is deleted.
337
otherwise created quick select
339
QuickRangeSelect *get_quick_select(Parameter *param,
343
uint32_t mrr_buf_size,
344
drizzled::memory::Root *alloc);
346
uint32_t get_index_for_order(Table *table, order_st *order, ha_rows limit);
348
SqlSelect *make_select(Table *head,
349
table_map const_tables,
350
table_map read_tables,
352
bool allow_null_cond,
355
bool get_quick_keys(Parameter *param,
356
QuickRangeSelect *quick,
359
unsigned char *min_key,
360
uint32_t min_key_flag,
361
unsigned char *max_key,
362
uint32_t max_key_flag);
364
} /* namespace optimizer */
366
} /* namespace drizzled */
368
#endif /* DRIZZLED_OPTIMIZER_RANGE_H */