~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/optimizer/explain_plan.cc

  • Committer: Monty Taylor
  • Date: 2008-12-08 01:15:27 UTC
  • mto: This revision was merged to the branch mainline in revision 670.
  • Revision ID: monty@inaugust.com-20081208011527-lq9m47jsmiiqn999
Replaced my hacked up m4/ac_system_extensions.m4 with the one from gnulib.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2
 
 *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3
 
 *
4
 
 *  Copyright (C) 2008-2009 Sun Microsystems
5
 
 *
6
 
 *  This program is free software; you can redistribute it and/or modify
7
 
 *  it under the terms of the GNU General Public License as published by
8
 
 *  the Free Software Foundation; version 2 of the License.
9
 
 *
10
 
 *  This program is distributed in the hope that it will be useful,
11
 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 
 *  GNU General Public License for more details.
14
 
 *
15
 
 *  You should have received a copy of the GNU General Public License
16
 
 *  along with this program; if not, write to the Free Software
17
 
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
 
 */
19
 
 
20
 
#include "config.h"
21
 
#include "drizzled/session.h"
22
 
#include "drizzled/item/uint.h"
23
 
#include "drizzled/item/float.h"
24
 
#include "drizzled/optimizer/explain_plan.h"
25
 
#include "drizzled/optimizer/position.h"
26
 
#include "drizzled/optimizer/quick_ror_intersect_select.h"
27
 
#include "drizzled/optimizer/range.h"
28
 
#include "drizzled/sql_select.h"
29
 
#include "drizzled/join.h"
30
 
#include "drizzled/internal/m_string.h"
31
 
 
32
 
#include <string>
33
 
#include <sstream>
34
 
 
35
 
using namespace std;
36
 
 
37
 
namespace drizzled
38
 
{
39
 
 
40
 
static const string access_method_str[]=
41
 
{
42
 
  "UNKNOWN",
43
 
  "system",
44
 
  "const",
45
 
  "eq_ref",
46
 
  "ref",
47
 
  "MAYBE_REF",
48
 
  "ALL",
49
 
  "range",
50
 
  "index",
51
 
  "ref_or_null",
52
 
  "unique_subquery",
53
 
  "index_subquery",
54
 
  "index_merge"
55
 
};
56
 
 
57
 
static const string select_type_str[]=
58
 
{
59
 
  "PRIMARY",
60
 
  "SIMPLE",
61
 
  "DERIVED",
62
 
  "DEPENDENT SUBQUERY",
63
 
  "UNCACHEABLE SUBQUERY",
64
 
  "SUBQUERY",
65
 
  "DEPENDENT UNION",
66
 
  "UNCACHEABLE_UNION",
67
 
  "UNION",
68
 
  "UNION RESULT"
69
 
};
70
 
 
71
 
void optimizer::ExplainPlan::printPlan()
72
 
{
73
 
  List<Item> field_list;
74
 
  List<Item> item_list;
75
 
  Session *session= join->session;
76
 
  select_result *result= join->result;
77
 
  Item *item_null= new Item_null();
78
 
  const CHARSET_INFO * const cs= system_charset_info;
79
 
  int quick_type;
80
 
  /* Don't log this into the slow query log */
81
 
  session->server_status&= ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED);
82
 
  join->unit->offset_limit_cnt= 0;
83
 
 
84
 
  /*
85
 
   NOTE: the number/types of items pushed into item_list must be in sync with
86
 
   EXPLAIN column types as they're "defined" in Session::send_explain_fields()
87
 
   */
88
 
  if (message)
89
 
  {
90
 
    item_list.push_back(new Item_int((int32_t)
91
 
                        join->select_lex->select_number));
92
 
    item_list.push_back(new Item_string(select_type_str[join->select_lex->type].c_str(),
93
 
                                        select_type_str[join->select_lex->type].length(),
94
 
                                        cs));
95
 
    for (uint32_t i= 0; i < 7; i++)
96
 
      item_list.push_back(item_null);
97
 
 
98
 
    if (join->session->lex->describe & DESCRIBE_EXTENDED)
99
 
      item_list.push_back(item_null);
100
 
 
101
 
    item_list.push_back(new Item_string(message,strlen(message),cs));
102
 
    if (result->send_data(item_list))
103
 
      join->error= 1;
104
 
  }
105
 
  else if (join->select_lex == join->unit->fake_select_lex)
106
 
  {
107
 
    /*
108
 
       here we assume that the query will return at least two rows, so we
109
 
       show "filesort" in EXPLAIN. Of course, sometimes we'll be wrong
110
 
       and no filesort will be actually done, but executing all selects in
111
 
       the UNION to provide precise EXPLAIN information will hardly be
112
 
       appreciated :)
113
 
     */
114
 
    char table_name_buffer[NAME_LEN];
115
 
    item_list.empty();
116
 
    /* id */
117
 
    item_list.push_back(new Item_null);
118
 
    /* select_type */
119
 
    item_list.push_back(new Item_string(select_type_str[join->select_lex->type].c_str(),
120
 
                                        select_type_str[join->select_lex->type].length(),
121
 
                                        cs));
122
 
    /* table */
123
 
    {
124
 
      Select_Lex *sl= join->unit->first_select();
125
 
      uint32_t len= 6, lastop= 0;
126
 
      memcpy(table_name_buffer, STRING_WITH_LEN("<union"));
127
 
      for (; sl && len + lastop + 5 < NAME_LEN; sl= sl->next_select())
128
 
      {
129
 
        len+= lastop;
130
 
        lastop= snprintf(table_name_buffer + len, NAME_LEN - len,
131
 
            "%u,", sl->select_number);
132
 
      }
133
 
      if (sl || len + lastop >= NAME_LEN)
134
 
      {
135
 
        memcpy(table_name_buffer + len, STRING_WITH_LEN("...>") + 1);
136
 
        len+= 4;
137
 
      }
138
 
      else
139
 
      {
140
 
        len+= lastop;
141
 
        table_name_buffer[len - 1]= '>';  // change ',' to '>'
142
 
      }
143
 
      item_list.push_back(new Item_string(table_name_buffer, len, cs));
144
 
    }
145
 
    /* type */
146
 
    item_list.push_back(new Item_string(access_method_str[AM_ALL].c_str(),
147
 
                                        access_method_str[AM_ALL].length(),
148
 
                                        cs));
149
 
    /* possible_keys */
150
 
    item_list.push_back(item_null);
151
 
    /* key*/
152
 
    item_list.push_back(item_null);
153
 
    /* key_len */
154
 
    item_list.push_back(item_null);
155
 
    /* ref */
156
 
    item_list.push_back(item_null);
157
 
    /* in_rows */
158
 
    if (join->session->lex->describe & DESCRIBE_EXTENDED)
159
 
      item_list.push_back(item_null);
160
 
    /* rows */
161
 
    item_list.push_back(item_null);
162
 
    /* extra */
163
 
    if (join->unit->global_parameters->order_list.first)
164
 
      item_list.push_back(new Item_string("Using filesort",
165
 
                                          14, 
166
 
                                          cs));
167
 
    else
168
 
      item_list.push_back(new Item_string("", 0, cs));
169
 
 
170
 
    if (result->send_data(item_list))
171
 
      join->error= 1;
172
 
  }
173
 
  else
174
 
  {
175
 
    table_map used_tables= 0;
176
 
    for (uint32_t i= 0; i < join->tables; i++)
177
 
    {
178
 
      JoinTable *tab= join->join_tab + i;
179
 
      Table *table= tab->table;
180
 
      char buff[512];
181
 
      char buff1[512], buff2[512], buff3[512];
182
 
      char keylen_str_buf[64];
183
 
      String extra(buff, sizeof(buff),cs);
184
 
      char table_name_buffer[NAME_LEN];
185
 
      String tmp1(buff1,sizeof(buff1),cs);
186
 
      String tmp2(buff2,sizeof(buff2),cs);
187
 
      String tmp3(buff3,sizeof(buff3),cs);
188
 
      extra.length(0);
189
 
      tmp1.length(0);
190
 
      tmp2.length(0);
191
 
      tmp3.length(0);
192
 
 
193
 
      quick_type= -1;
194
 
      item_list.empty();
195
 
      /* id */
196
 
      item_list.push_back(new Item_uint((uint32_t)
197
 
            join->select_lex->select_number));
198
 
      /* select_type */
199
 
      item_list.push_back(new Item_string(select_type_str[join->select_lex->type].c_str(),
200
 
                                          select_type_str[join->select_lex->type].length(),
201
 
                                          cs));
202
 
      if (tab->type == AM_ALL && tab->select && tab->select->quick)
203
 
      {
204
 
        quick_type= tab->select->quick->get_type();
205
 
        if ((quick_type == optimizer::QuickSelectInterface::QS_TYPE_INDEX_MERGE) ||
206
 
            (quick_type == optimizer::QuickSelectInterface::QS_TYPE_ROR_INTERSECT) ||
207
 
            (quick_type == optimizer::QuickSelectInterface::QS_TYPE_ROR_UNION))
208
 
          tab->type = AM_INDEX_MERGE;
209
 
        else
210
 
          tab->type = AM_RANGE;
211
 
      }
212
 
      /* table */
213
 
      if (table->derived_select_number)
214
 
      {
215
 
        /* Derived table name generation */
216
 
        int len= snprintf(table_name_buffer, 
217
 
                          sizeof(table_name_buffer)-1,
218
 
                          "<derived%u>",
219
 
                          table->derived_select_number);
220
 
        item_list.push_back(new Item_string(table_name_buffer, len, cs));
221
 
      }
222
 
      else
223
 
      {
224
 
        TableList *real_table= table->pos_in_table_list;
225
 
        item_list.push_back(new Item_string(real_table->alias,
226
 
                                            strlen(real_table->alias),
227
 
                                            cs));
228
 
      }
229
 
      /* "type" column */
230
 
      item_list.push_back(new Item_string(access_method_str[tab->type].c_str(),
231
 
                                          access_method_str[tab->type].length(),
232
 
                                          cs));
233
 
      /* Build "possible_keys" value and add it to item_list */
234
 
      if (tab->keys.any())
235
 
      {
236
 
        for (uint32_t j= 0; j < table->s->keys; j++)
237
 
        {
238
 
          if (tab->keys.test(j))
239
 
          {
240
 
            if (tmp1.length())
241
 
              tmp1.append(',');
242
 
            tmp1.append(table->key_info[j].name,
243
 
                        strlen(table->key_info[j].name),
244
 
                        system_charset_info);
245
 
          }
246
 
        }
247
 
      }
248
 
      if (tmp1.length())
249
 
        item_list.push_back(new Item_string(tmp1.ptr(),tmp1.length(),cs));
250
 
      else
251
 
        item_list.push_back(item_null);
252
 
 
253
 
      /* Build "key", "key_len", and "ref" values and add them to item_list */
254
 
      if (tab->ref.key_parts)
255
 
      {
256
 
        KEY *key_info= table->key_info+ tab->ref.key;
257
 
        item_list.push_back(new Item_string(key_info->name,
258
 
                                            strlen(key_info->name),
259
 
                                            system_charset_info));
260
 
        uint32_t length= internal::int64_t2str(tab->ref.key_length, keylen_str_buf, 10) -
261
 
                                     keylen_str_buf;
262
 
        item_list.push_back(new Item_string(keylen_str_buf, 
263
 
                                            length,
264
 
                                            system_charset_info));
265
 
        for (StoredKey **ref= tab->ref.key_copy; *ref; ref++)
266
 
        {
267
 
          if (tmp2.length())
268
 
            tmp2.append(',');
269
 
          tmp2.append((*ref)->name(), 
270
 
                       strlen((*ref)->name()),
271
 
                       system_charset_info);
272
 
        }
273
 
        item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length(),cs));
274
 
      }
275
 
      else if (tab->type == AM_NEXT)
276
 
      {
277
 
        KEY *key_info=table->key_info+ tab->index;
278
 
        item_list.push_back(new Item_string(key_info->name,
279
 
              strlen(key_info->name),cs));
280
 
        uint32_t length= internal::int64_t2str(key_info->key_length, keylen_str_buf, 10) -
281
 
                                     keylen_str_buf;
282
 
        item_list.push_back(new Item_string(keylen_str_buf,
283
 
                                            length,
284
 
                                            system_charset_info));
285
 
        item_list.push_back(item_null);
286
 
      }
287
 
      else if (tab->select && tab->select->quick)
288
 
      {
289
 
        tab->select->quick->add_keys_and_lengths(&tmp2, &tmp3);
290
 
        item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length(),cs));
291
 
        item_list.push_back(new Item_string(tmp3.ptr(),tmp3.length(),cs));
292
 
        item_list.push_back(item_null);
293
 
      }
294
 
      else
295
 
      {
296
 
        item_list.push_back(item_null);
297
 
        item_list.push_back(item_null);
298
 
        item_list.push_back(item_null);
299
 
      }
300
 
 
301
 
      /* Add "rows" field to item_list. */
302
 
      double examined_rows;
303
 
      if (tab->select && tab->select->quick)
304
 
      {
305
 
        examined_rows= rows2double(tab->select->quick->records);
306
 
      }
307
 
      else if (tab->type == AM_NEXT || tab->type == AM_ALL)
308
 
      {
309
 
        examined_rows= rows2double(tab->limit ? tab->limit :
310
 
                                                tab->table->cursor->records());
311
 
      }
312
 
      else
313
 
      {
314
 
        optimizer::Position cur_pos= join->getPosFromOptimalPlan(i);
315
 
        examined_rows= cur_pos.getFanout();
316
 
      }
317
 
 
318
 
      item_list.push_back(new Item_int((int64_t) (uint64_t) examined_rows,
319
 
                                       MY_INT64_NUM_DECIMAL_DIGITS));
320
 
 
321
 
      /* Add "filtered" field to item_list. */
322
 
      if (join->session->lex->describe & DESCRIBE_EXTENDED)
323
 
      {
324
 
        float f= 0.0;
325
 
        if (examined_rows)
326
 
        {
327
 
          optimizer::Position cur_pos= join->getPosFromOptimalPlan(i);
328
 
          f= static_cast<float>(100.0 * cur_pos.getFanout() / examined_rows);
329
 
        }
330
 
        item_list.push_back(new Item_float(f, 2));
331
 
      }
332
 
 
333
 
      /* Build "Extra" field and add it to item_list. */
334
 
      bool key_read= table->key_read;
335
 
      if ((tab->type == AM_NEXT || tab->type == AM_CONST) &&
336
 
          table->covering_keys.test(tab->index))
337
 
        key_read= 1;
338
 
      if (quick_type == optimizer::QuickSelectInterface::QS_TYPE_ROR_INTERSECT &&
339
 
          ! ((optimizer::QuickRorIntersectSelect *) tab->select->quick)->need_to_fetch_row)
340
 
        key_read= 1;
341
 
 
342
 
      if (tab->info)
343
 
        item_list.push_back(new Item_string(tab->info,strlen(tab->info),cs));
344
 
      else if (tab->packed_info & TAB_INFO_HAVE_VALUE)
345
 
      {
346
 
        if (tab->packed_info & TAB_INFO_USING_INDEX)
347
 
          extra.append(STRING_WITH_LEN("; Using index"));
348
 
        if (tab->packed_info & TAB_INFO_USING_WHERE)
349
 
          extra.append(STRING_WITH_LEN("; Using where"));
350
 
        if (tab->packed_info & TAB_INFO_FULL_SCAN_ON_NULL)
351
 
          extra.append(STRING_WITH_LEN("; Full scan on NULL key"));
352
 
        /* Skip initial "; "*/
353
 
        const char *str= extra.ptr();
354
 
        uint32_t len= extra.length();
355
 
        if (len)
356
 
        {
357
 
          str += 2;
358
 
          len -= 2;
359
 
        }
360
 
        item_list.push_back(new Item_string(str, len, cs));
361
 
      }
362
 
      else
363
 
      {
364
 
        uint32_t keyno= MAX_KEY;
365
 
        if (tab->ref.key_parts)
366
 
          keyno= tab->ref.key;
367
 
        else if (tab->select && tab->select->quick)
368
 
          keyno = tab->select->quick->index;
369
 
 
370
 
        if (quick_type == optimizer::QuickSelectInterface::QS_TYPE_ROR_UNION ||
371
 
            quick_type == optimizer::QuickSelectInterface::QS_TYPE_ROR_INTERSECT ||
372
 
            quick_type == optimizer::QuickSelectInterface::QS_TYPE_INDEX_MERGE)
373
 
        {
374
 
          extra.append(STRING_WITH_LEN("; Using "));
375
 
          tab->select->quick->add_info_string(&extra);
376
 
        }
377
 
        if (tab->select)
378
 
        {
379
 
          if (tab->use_quick == 2)
380
 
          {
381
 
            /*
382
 
             * To print out the bitset in tab->keys, we go through
383
 
             * it 32 bits at a time. We need to do this to ensure
384
 
             * that the to_ulong() method will not throw an
385
 
             * out_of_range exception at runtime which would happen
386
 
             * if the bitset we were working with was larger than 64
387
 
             * bits on a 64-bit platform (for example).
388
 
             */
389
 
            stringstream s, w;
390
 
            string str;
391
 
            w << tab->keys;
392
 
            w >> str;
393
 
            for (uint32_t pos= 0; pos < tab->keys.size(); pos+= 32)
394
 
            {
395
 
              bitset<32> tmp(str, pos, 32);
396
 
              if (tmp.any())
397
 
                s << uppercase << hex << tmp.to_ulong();
398
 
            }
399
 
            extra.append(STRING_WITH_LEN("; Range checked for each "
400
 
                  "record (index map: 0x"));
401
 
            extra.append(s.str().c_str());
402
 
            extra.append(')');
403
 
          }
404
 
          else if (tab->select->cond)
405
 
          {
406
 
            extra.append(STRING_WITH_LEN("; Using where"));
407
 
          }
408
 
        }
409
 
        if (key_read)
410
 
        {
411
 
          if (quick_type == optimizer::QuickSelectInterface::QS_TYPE_GROUP_MIN_MAX)
412
 
            extra.append(STRING_WITH_LEN("; Using index for group-by"));
413
 
          else
414
 
            extra.append(STRING_WITH_LEN("; Using index"));
415
 
        }
416
 
        if (table->reginfo.not_exists_optimize)
417
 
          extra.append(STRING_WITH_LEN("; Not exists"));
418
 
 
419
 
        if (need_tmp_table)
420
 
        {
421
 
          need_tmp_table=0;
422
 
          extra.append(STRING_WITH_LEN("; Using temporary"));
423
 
        }
424
 
        if (need_order)
425
 
        {
426
 
          need_order=0;
427
 
          extra.append(STRING_WITH_LEN("; Using filesort"));
428
 
        }
429
 
        if (distinct & test_all_bits(used_tables,session->used_tables))
430
 
          extra.append(STRING_WITH_LEN("; Distinct"));
431
 
 
432
 
        if (tab->insideout_match_tab)
433
 
        {
434
 
          extra.append(STRING_WITH_LEN("; LooseScan"));
435
 
        }
436
 
 
437
 
        for (uint32_t part= 0; part < tab->ref.key_parts; part++)
438
 
        {
439
 
          if (tab->ref.cond_guards[part])
440
 
          {
441
 
            extra.append(STRING_WITH_LEN("; Full scan on NULL key"));
442
 
            break;
443
 
          }
444
 
        }
445
 
 
446
 
        if (i > 0 && tab[-1].next_select == sub_select_cache)
447
 
          extra.append(STRING_WITH_LEN("; Using join buffer"));
448
 
 
449
 
        /* Skip initial "; "*/
450
 
        const char *str= extra.ptr();
451
 
        uint32_t len= extra.length();
452
 
        if (len)
453
 
        {
454
 
          str += 2;
455
 
          len -= 2;
456
 
        }
457
 
        item_list.push_back(new Item_string(str, len, cs));
458
 
      }
459
 
      // For next iteration
460
 
      used_tables|=table->map;
461
 
      if (result->send_data(item_list))
462
 
        join->error= 1;
463
 
    }
464
 
  }
465
 
  for (Select_Lex_Unit *unit= join->select_lex->first_inner_unit();
466
 
      unit;
467
 
      unit= unit->next_unit())
468
 
  {
469
 
    if (explainUnion(session, unit, result))
470
 
      return;
471
 
  }
472
 
  return;
473
 
}
474
 
 
475
 
bool optimizer::ExplainPlan::explainUnion(Session *session,
476
 
                                          Select_Lex_Unit *unit,
477
 
                                          select_result *result)
478
 
{
479
 
  bool res= false;
480
 
  Select_Lex *first= unit->first_select();
481
 
 
482
 
  for (Select_Lex *sl= first;
483
 
       sl;
484
 
       sl= sl->next_select())
485
 
  {
486
 
    // drop UNCACHEABLE_EXPLAIN, because it is for internal usage only
487
 
    uint8_t uncacheable= (sl->uncacheable & ~UNCACHEABLE_EXPLAIN);
488
 
    if (&session->lex->select_lex == sl)
489
 
    {
490
 
      if (sl->first_inner_unit() || sl->next_select())
491
 
      {
492
 
        sl->type= optimizer::ST_PRIMARY;
493
 
      }
494
 
      else
495
 
      {
496
 
        sl->type= optimizer::ST_SIMPLE;
497
 
      }
498
 
    }
499
 
    else
500
 
    {
501
 
      if (sl == first)
502
 
      {
503
 
        if (sl->linkage == DERIVED_TABLE_TYPE)
504
 
        {
505
 
          sl->type= optimizer::ST_DERIVED;
506
 
        }
507
 
        else
508
 
        {
509
 
          if (uncacheable & UNCACHEABLE_DEPENDENT)
510
 
          {
511
 
            sl->type= optimizer::ST_DEPENDENT_SUBQUERY;
512
 
          }
513
 
          else
514
 
          {
515
 
            if (uncacheable)
516
 
            {
517
 
              sl->type= optimizer::ST_UNCACHEABLE_SUBQUERY;
518
 
            }
519
 
            else
520
 
            {
521
 
              sl->type= optimizer::ST_SUBQUERY;
522
 
            }
523
 
          }
524
 
        }
525
 
      }
526
 
      else
527
 
      {
528
 
        if (uncacheable & UNCACHEABLE_DEPENDENT)
529
 
        {
530
 
          sl->type= optimizer::ST_DEPENDENT_UNION;
531
 
        }
532
 
        else
533
 
        {
534
 
          if (uncacheable)
535
 
          {
536
 
            sl->type= optimizer::ST_UNCACHEABLE_UNION;
537
 
          }
538
 
          else
539
 
          {
540
 
            sl->type= optimizer::ST_UNION;
541
 
          }
542
 
        }
543
 
      }
544
 
    }
545
 
    sl->options|= SELECT_DESCRIBE;
546
 
  }
547
 
 
548
 
  if (unit->is_union())
549
 
  {
550
 
    unit->fake_select_lex->select_number= UINT_MAX; // just for initialization
551
 
    unit->fake_select_lex->type= optimizer::ST_UNION_RESULT;
552
 
    unit->fake_select_lex->options|= SELECT_DESCRIBE;
553
 
    if (! (res= unit->prepare(session, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE)))
554
 
    {
555
 
      res= unit->exec();
556
 
    }
557
 
    res|= unit->cleanup();
558
 
  }
559
 
  else
560
 
  {
561
 
    session->lex->current_select= first;
562
 
    unit->set_limit(unit->global_parameters);
563
 
    res= mysql_select(session, 
564
 
                      &first->ref_pointer_array,
565
 
                      (TableList*) first->table_list.first,
566
 
                      first->with_wild, 
567
 
                      first->item_list,
568
 
                      first->where,
569
 
                      first->order_list.elements + first->group_list.elements,
570
 
                      (order_st*) first->order_list.first,
571
 
                      (order_st*) first->group_list.first,
572
 
                      first->having,
573
 
                      first->options | session->options | SELECT_DESCRIBE,
574
 
                      result, 
575
 
                      unit, 
576
 
                      first);
577
 
  }
578
 
  return (res || session->is_error());
579
 
}
580
 
 
581
 
} /* namespace drizzled */