~drizzle-trunk/drizzle/development

1 by brian
clean slate
1
/* Copyright (C) 2000-2006 MySQL AB
2
3
   This program is free software; you can redistribute it and/or modify
4
   it under the terms of the GNU General Public License as published by
5
   the Free Software Foundation; version 2 of the License.
6
7
   This program is distributed in the hope that it will be useful,
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
   GNU General Public License for more details.
11
12
   You should have received a copy of the GNU General Public License
13
   along with this program; if not, write to the Free Software
14
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
15
16
17
/* Write some debug info */
18
19
20
#include "mysql_priv.h"
21
#include "sql_select.h"
22
#include <hash.h>
23
#include <thr_alarm.h>
24
#if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_H)
25
#include <malloc.h>
26
#elif defined(HAVE_MALLINFO) && defined(HAVE_SYS_MALLOC_H)
27
#include <sys/malloc.h>
28
#endif
29
30
static const char *lock_descriptions[] =
31
{
32
  "No lock",
33
  "Low priority read lock",
34
  "Shared Read lock",
35
  "High priority read lock",
36
  "Read lock  without concurrent inserts",
37
  "Write lock that allows other writers",
38
  "Write lock, but allow reading",
39
  "Concurrent insert lock",
40
  "Lock Used by delayed insert",
41
  "Low priority write lock",
42
  "High priority write lock",
43
  "Highest priority write lock"
44
};
45
46
47
#ifndef DBUG_OFF
48
49
void
50
print_where(COND *cond,const char *info, enum_query_type query_type)
51
{
52
  if (cond)
53
  {
54
    char buff[256];
55
    String str(buff,(uint32) sizeof(buff), system_charset_info);
56
    str.length(0);
57
    cond->print(&str, query_type);
58
    str.append('\0');
59
    DBUG_LOCK_FILE;
60
    (void) fprintf(DBUG_FILE,"\nWHERE:(%s) ",info);
61
    (void) fputs(str.ptr(),DBUG_FILE);
62
    (void) fputc('\n',DBUG_FILE);
63
    DBUG_UNLOCK_FILE;
64
  }
65
}
66
	/* This is for debugging purposes */
67
68
69
void print_cached_tables(void)
70
{
71
  uint idx,count,unused;
72
  TABLE *start_link,*lnk;
73
74
  /* purecov: begin tested */
75
  VOID(pthread_mutex_lock(&LOCK_open));
76
  puts("DB             Table                            Version  Thread  Open  Lock");
77
78
  for (idx=unused=0 ; idx < open_cache.records ; idx++)
79
  {
80
    TABLE *entry=(TABLE*) hash_element(&open_cache,idx);
81
    printf("%-14.14s %-32s%6ld%8ld%6d  %s\n",
82
           entry->s->db.str, entry->s->table_name.str, entry->s->version,
83
	   entry->in_use ? entry->in_use->thread_id : 0L,
84
	   entry->db_stat ? 1 : 0,
85
           entry->in_use ? lock_descriptions[(int)entry->reginfo.lock_type] : "Not in use");
86
    if (!entry->in_use)
87
      unused++;
88
  }
89
  count=0;
90
  if ((start_link=lnk=unused_tables))
91
  {
92
    do
93
    {
94
      if (lnk != lnk->next->prev || lnk != lnk->prev->next)
95
      {
96
	printf("unused_links isn't linked properly\n");
97
	return;
98
      }
99
    } while (count++ < open_cache.records && (lnk=lnk->next) != start_link);
100
    if (lnk != start_link)
101
    {
102
      printf("Unused_links aren't connected\n");
103
    }
104
  }
105
  if (count != unused)
106
    printf("Unused_links (%d) doesn't match open_cache: %d\n", count,unused);
107
  printf("\nCurrent refresh version: %ld\n",refresh_version);
108
  if (hash_check(&open_cache))
109
    printf("Error: File hash table is corrupted\n");
110
  fflush(stdout);
111
  VOID(pthread_mutex_unlock(&LOCK_open));
112
  /* purecov: end */
113
  return;
114
}
115
116
117
void TEST_filesort(SORT_FIELD *sortorder,uint s_length)
118
{
119
  char buff[256],buff2[256];
120
  String str(buff,sizeof(buff),system_charset_info);
121
  String out(buff2,sizeof(buff2),system_charset_info);
122
  const char *sep;
123
  DBUG_ENTER("TEST_filesort");
124
125
  out.length(0);
126
  for (sep=""; s_length-- ; sortorder++, sep=" ")
127
  {
128
    out.append(sep);
129
    if (sortorder->reverse)
130
      out.append('-');
131
    if (sortorder->field)
132
    {
133
      if (sortorder->field->table_name)
134
      {
135
	out.append(*sortorder->field->table_name);
136
	out.append('.');
137
      }
138
      out.append(sortorder->field->field_name ? sortorder->field->field_name:
139
		 "tmp_table_column");
140
    }
141
    else
142
    {
143
      str.length(0);
144
      sortorder->item->print(&str, QT_ORDINARY);
145
      out.append(str);
146
    }
147
  }
148
  out.append('\0');				// Purify doesn't like c_ptr()
149
  DBUG_LOCK_FILE;
150
  VOID(fputs("\nInfo about FILESORT\n",DBUG_FILE));
151
  fprintf(DBUG_FILE,"Sortorder: %s\n",out.ptr());
152
  DBUG_UNLOCK_FILE;
153
  DBUG_VOID_RETURN;
154
}
155
156
157
void
158
TEST_join(JOIN *join)
159
{
160
  uint i,ref;
161
  DBUG_ENTER("TEST_join");
162
163
  DBUG_LOCK_FILE;
164
  VOID(fputs("\nInfo about JOIN\n",DBUG_FILE));
165
  for (i=0 ; i < join->tables ; i++)
166
  {
167
    JOIN_TAB *tab=join->join_tab+i;
168
    TABLE *form=tab->table;
169
    char key_map_buff[128];
170
    fprintf(DBUG_FILE,"%-16.16s  type: %-7s  q_keys: %s  refs: %d  key: %d  len: %d\n",
171
	    form->alias,
172
	    join_type_str[tab->type],
173
	    tab->keys.print(key_map_buff),
174
	    tab->ref.key_parts,
175
	    tab->ref.key,
176
	    tab->ref.key_length);
177
    if (tab->select)
178
    {
179
      char buf[MAX_KEY/8+1];
180
      if (tab->use_quick == 2)
181
	fprintf(DBUG_FILE,
182
		"                  quick select checked for each record (keys: %s)\n",
183
		tab->select->quick_keys.print(buf));
184
      else if (tab->select->quick)
185
      {
186
	fprintf(DBUG_FILE, "                  quick select used:\n");
187
        tab->select->quick->dbug_dump(18, FALSE);
188
      }
189
      else
190
	VOID(fputs("                  select used\n",DBUG_FILE));
191
    }
192
    if (tab->ref.key_parts)
193
    {
194
      VOID(fputs("                  refs: ",DBUG_FILE));
195
      for (ref=0 ; ref < tab->ref.key_parts ; ref++)
196
      {
197
	Item *item=tab->ref.items[ref];
198
	fprintf(DBUG_FILE,"%s  ", item->full_name());
199
      }
200
      VOID(fputc('\n',DBUG_FILE));
201
    }
202
  }
203
  DBUG_UNLOCK_FILE;
204
  DBUG_VOID_RETURN;
205
}
206
207
#define FT_KEYPART   (MAX_REF_PARTS+10)
208
209
void print_keyuse(KEYUSE *keyuse)
210
{
211
  char buff[256];
212
  char buf2[64]; 
213
  const char *fieldname;
214
  String str(buff,(uint32) sizeof(buff), system_charset_info);
215
  str.length(0);
216
  keyuse->val->print(&str, QT_ORDINARY);
217
  str.append('\0');
218
  if (keyuse->keypart == FT_KEYPART)
219
    fieldname= "FT_KEYPART";
220
  else
221
    fieldname= keyuse->table->key_info[keyuse->key].key_part[keyuse->keypart].field->field_name;
222
  longlong2str(keyuse->used_tables, buf2, 16); 
223
  DBUG_LOCK_FILE;
224
  fprintf(DBUG_FILE, "KEYUSE: %s.%s=%s  optimize= %d used_tables=%s "
225
          "ref_table_rows= %lu keypart_map= %0lx\n",
226
          keyuse->table->alias, fieldname, str.ptr(),
227
          keyuse->optimize, buf2, (ulong)keyuse->ref_table_rows, 
228
          keyuse->keypart_map);
229
  DBUG_UNLOCK_FILE;
230
  //key_part_map keypart_map; --?? there can be several? 
231
}
232
233
234
void print_keyuse_array(DYNAMIC_ARRAY *keyuse_array)
235
{
236
  DBUG_LOCK_FILE;
237
  fprintf(DBUG_FILE, "KEYUSE array (%d elements)\n", keyuse_array->elements);
238
  DBUG_UNLOCK_FILE;
239
  for(uint i=0; i < keyuse_array->elements; i++)
240
    print_keyuse((KEYUSE*)dynamic_array_ptr(keyuse_array, i));
241
}
242
243
244
/* 
245
  Print the current state during query optimization.
246
247
  SYNOPSIS
248
    print_plan()
249
    join         pointer to the structure providing all context info for
250
                 the query
251
    read_time    the cost of the best partial plan
252
    record_count estimate for the number of records returned by the best
253
                 partial plan
254
    idx          length of the partial QEP in 'join->positions';
255
                 also an index in the array 'join->best_ref';
256
    info         comment string to appear above the printout
257
258
  DESCRIPTION
259
    This function prints to the log file DBUG_FILE the members of 'join' that
260
    are used during query optimization (join->positions, join->best_positions,
261
    and join->best_ref) and few other related variables (read_time,
262
    record_count).
263
    Useful to trace query optimizer functions.
264
265
  RETURN
266
    None
267
*/
268
269
void
270
print_plan(JOIN* join, uint idx, double record_count, double read_time,
271
           double current_read_time, const char *info)
272
{
273
  uint i;
274
  POSITION pos;
275
  JOIN_TAB *join_table;
276
  JOIN_TAB **plan_nodes;
277
  TABLE*   table;
278
279
  if (info == 0)
280
    info= "";
281
282
  DBUG_LOCK_FILE;
283
  if (join->best_read == DBL_MAX)
284
  {
285
    fprintf(DBUG_FILE,
286
            "%s; idx: %u  best: DBL_MAX  atime: %g  itime: %g  count: %g\n",
287
            info, idx, current_read_time, read_time, record_count);
288
  }
289
  else
290
  {
291
    fprintf(DBUG_FILE,
292
            "%s; idx :%u  best: %g  accumulated: %g  increment: %g  count: %g\n",
293
            info, idx, join->best_read, current_read_time, read_time,
294
            record_count);
295
  }
296
297
  /* Print the tables in JOIN->positions */
298
  fputs("     POSITIONS: ", DBUG_FILE);
299
  for (i= 0; i < idx ; i++)
300
  {
301
    pos = join->positions[i];
302
    table= pos.table->table;
303
    if (table)
304
      fputs(table->s->table_name.str, DBUG_FILE);
305
    fputc(' ', DBUG_FILE);
306
  }
307
  fputc('\n', DBUG_FILE);
308
309
  /*
310
    Print the tables in JOIN->best_positions only if at least one complete plan
311
    has been found. An indicator for this is the value of 'join->best_read'.
312
  */
313
  if (join->best_read < DBL_MAX)
314
  {
315
    fputs("BEST_POSITIONS: ", DBUG_FILE);
316
    for (i= 0; i < idx ; i++)
317
    {
318
      pos= join->best_positions[i];
319
      table= pos.table->table;
320
      if (table)
321
        fputs(table->s->table_name.str, DBUG_FILE);
322
      fputc(' ', DBUG_FILE);
323
    }
324
  }
325
  fputc('\n', DBUG_FILE);
326
327
  /* Print the tables in JOIN->best_ref */
328
  fputs("      BEST_REF: ", DBUG_FILE);
329
  for (plan_nodes= join->best_ref ; *plan_nodes ; plan_nodes++)
330
  {
331
    join_table= (*plan_nodes);
332
    fputs(join_table->table->s->table_name.str, DBUG_FILE);
333
    fprintf(DBUG_FILE, "(%lu,%lu,%lu)",
334
            (ulong) join_table->found_records,
335
            (ulong) join_table->records,
336
            (ulong) join_table->read_time);
337
    fputc(' ', DBUG_FILE);
338
  }
339
  fputc('\n', DBUG_FILE);
340
341
  DBUG_UNLOCK_FILE;
342
}
343
344
#endif
345
346
typedef struct st_debug_lock
347
{
348
  ulong thread_id;
349
  char table_name[FN_REFLEN];
350
  bool waiting;
351
  const char *lock_text;
352
  enum thr_lock_type type;
353
} TABLE_LOCK_INFO;
354
355
static int dl_compare(TABLE_LOCK_INFO *a,TABLE_LOCK_INFO *b)
356
{
357
  if (a->thread_id > b->thread_id)
358
    return 1;
359
  if (a->thread_id < b->thread_id)
360
    return -1;
361
  if (a->waiting == b->waiting)
362
    return 0;
363
  else if (a->waiting)
364
    return -1;
365
  return 1;
366
}
367
368
369
static void push_locks_into_array(DYNAMIC_ARRAY *ar, THR_LOCK_DATA *data,
370
				  bool wait, const char *text)
371
{
372
  if (data)
373
  {
374
    TABLE *table=(TABLE *)data->debug_print_param;
375
    if (table && table->s->tmp_table == NO_TMP_TABLE)
376
    {
377
      TABLE_LOCK_INFO table_lock_info;
378
      table_lock_info.thread_id= table->in_use->thread_id;
379
      memcpy(table_lock_info.table_name, table->s->table_cache_key.str,
380
	     table->s->table_cache_key.length);
381
      table_lock_info.table_name[strlen(table_lock_info.table_name)]='.';
382
      table_lock_info.waiting=wait;
383
      table_lock_info.lock_text=text;
384
      // lock_type is also obtainable from THR_LOCK_DATA
385
      table_lock_info.type=table->reginfo.lock_type;
386
      VOID(push_dynamic(ar,(uchar*) &table_lock_info));
387
    }
388
  }
389
}
390
391
392
/*
393
  Regarding MERGE tables:
394
395
  For now, the best option is to use the common TABLE *pointer for all
396
  cases;  The drawback is that for MERGE tables we will see many locks
397
  for the merge tables even if some of them are for individual tables.
398
399
  The way to solve this is to add to 'THR_LOCK' structure a pointer to
400
  the filename and use this when printing the data.
401
  (We can for now ignore this and just print the same name for all merge
402
  table parts;  Please add the above as a comment to the display_lock
403
  function so that we can easily add this if we ever need this.
404
*/
405
406
static void display_table_locks(void) 
407
{
408
  LIST *list;
409
  DYNAMIC_ARRAY saved_table_locks;
410
411
  VOID(my_init_dynamic_array(&saved_table_locks,sizeof(TABLE_LOCK_INFO),open_cache.records + 20,50));
412
  VOID(pthread_mutex_lock(&THR_LOCK_lock));
413
  for (list= thr_lock_thread_list; list; list= list_rest(list))
414
  {
415
    THR_LOCK *lock=(THR_LOCK*) list->data;
416
417
    VOID(pthread_mutex_lock(&lock->mutex));
418
    push_locks_into_array(&saved_table_locks, lock->write.data, FALSE,
419
			  "Locked - write");
420
    push_locks_into_array(&saved_table_locks, lock->write_wait.data, TRUE,
421
			  "Waiting - write");
422
    push_locks_into_array(&saved_table_locks, lock->read.data, FALSE,
423
			  "Locked - read");
424
    push_locks_into_array(&saved_table_locks, lock->read_wait.data, TRUE,
425
			  "Waiting - read");
426
    VOID(pthread_mutex_unlock(&lock->mutex));
427
  }
428
  VOID(pthread_mutex_unlock(&THR_LOCK_lock));
429
  if (!saved_table_locks.elements) goto end;
430
  
431
  qsort((uchar*) dynamic_element(&saved_table_locks,0,TABLE_LOCK_INFO *),saved_table_locks.elements,sizeof(TABLE_LOCK_INFO),(qsort_cmp) dl_compare);
432
  freeze_size(&saved_table_locks);
433
434
  puts("\nThread database.table_name          Locked/Waiting        Lock_type\n");
435
  
436
  unsigned int i;
437
  for (i=0 ; i < saved_table_locks.elements ; i++)
438
  {
439
    TABLE_LOCK_INFO *dl_ptr=dynamic_element(&saved_table_locks,i,TABLE_LOCK_INFO*);
440
    printf("%-8ld%-28.28s%-22s%s\n",
441
	   dl_ptr->thread_id,dl_ptr->table_name,dl_ptr->lock_text,lock_descriptions[(int)dl_ptr->type]);
442
  }
443
  puts("\n\n");
444
end:
445
  delete_dynamic(&saved_table_locks);
446
}
447
448
449
static int print_key_cache_status(const char *name, KEY_CACHE *key_cache)
450
{
451
  char llbuff1[22];
452
  char llbuff2[22];
453
  char llbuff3[22];
454
  char llbuff4[22];
455
456
  if (!key_cache->key_cache_inited)
457
  {
458
    printf("%s: Not in use\n", name);
459
  }
460
  else
461
  {
462
    printf("%s\n\
463
Buffer_size:    %10lu\n\
464
Block_size:     %10lu\n\
465
Division_limit: %10lu\n\
466
Age_limit:      %10lu\n\
467
blocks used:    %10lu\n\
468
not flushed:    %10lu\n\
469
w_requests:     %10s\n\
470
writes:         %10s\n\
471
r_requests:     %10s\n\
472
reads:          %10s\n\n",
473
	   name,
474
	   (ulong) key_cache->param_buff_size, key_cache->param_block_size,
475
	   key_cache->param_division_limit, key_cache->param_age_threshold,
476
	   key_cache->blocks_used,key_cache->global_blocks_changed,
477
	   llstr(key_cache->global_cache_w_requests,llbuff1),
478
           llstr(key_cache->global_cache_write,llbuff2),
479
	   llstr(key_cache->global_cache_r_requests,llbuff3),
480
           llstr(key_cache->global_cache_read,llbuff4));
481
  }
482
  return 0;
483
}
484
485
486
void mysql_print_status()
487
{
488
  char current_dir[FN_REFLEN];
489
  STATUS_VAR tmp;
490
491
  calc_sum_of_all_status(&tmp);
492
  printf("\nStatus information:\n\n");
493
  VOID(my_getwd(current_dir, sizeof(current_dir),MYF(0)));
494
  printf("Current dir: %s\n", current_dir);
495
  printf("Running threads: %d  Stack size: %ld\n", thread_count,
496
	 (long) my_thread_stack_size);
497
  thr_print_locks();				// Write some debug info
498
#ifndef DBUG_OFF
499
  print_cached_tables();
500
#endif
501
  /* Print key cache status */
502
  puts("\nKey caches:");
503
  process_key_caches(print_key_cache_status);
504
  pthread_mutex_lock(&LOCK_status);
505
  printf("\nhandler status:\n\
506
read_key:   %10lu\n\
507
read_next:  %10lu\n\
508
read_rnd    %10lu\n\
509
read_first: %10lu\n\
510
write:      %10lu\n\
511
delete      %10lu\n\
512
update:     %10lu\n",
513
	 tmp.ha_read_key_count,
514
	 tmp.ha_read_next_count,
515
	 tmp.ha_read_rnd_count,
516
	 tmp.ha_read_first_count,
517
	 tmp.ha_write_count,
518
	 tmp.ha_delete_count,
519
	 tmp.ha_update_count);
520
  pthread_mutex_unlock(&LOCK_status);
521
  printf("\nTable status:\n\
522
Opened tables: %10lu\n\
523
Open tables:   %10lu\n\
524
Open files:    %10lu\n\
525
Open streams:  %10lu\n",
526
	 tmp.opened_tables,
527
	 (ulong) cached_open_tables(),
528
	 (ulong) my_file_opened,
529
	 (ulong) my_stream_opened);
530
531
  ALARM_INFO alarm_info;
532
#ifndef DONT_USE_THR_ALARM
533
  thr_alarm_info(&alarm_info);
534
  printf("\nAlarm status:\n\
535
Active alarms:   %u\n\
536
Max used alarms: %u\n\
537
Next alarm time: %lu\n",
538
	 alarm_info.active_alarms,
539
	 alarm_info.max_used_alarms,
540
	 alarm_info.next_alarm_time);
541
#endif
542
  display_table_locks();
543
  fflush(stdout);
544
  my_checkmalloc();
545
  fprintf(stdout,"\nBegin safemalloc memory dump:\n"); // tag needed for test suite
546
  TERMINATE(stdout, 1);				// Write malloc information
547
  fprintf(stdout,"\nEnd safemalloc memory dump.\n");  
548
549
#ifdef HAVE_MALLINFO
550
  struct mallinfo info= mallinfo();
551
  printf("\nMemory status:\n\
552
Non-mmapped space allocated from system: %d\n\
553
Number of free chunks:			 %d\n\
554
Number of fastbin blocks:		 %d\n\
555
Number of mmapped regions:		 %d\n\
556
Space in mmapped regions:		 %d\n\
557
Maximum total allocated space:		 %d\n\
558
Space available in freed fastbin blocks: %d\n\
559
Total allocated space:			 %d\n\
560
Total free space:			 %d\n\
561
Top-most, releasable space:		 %d\n\
562
Estimated memory (with thread stack):    %ld\n",
563
	 (int) info.arena	,
564
	 (int) info.ordblks,
565
	 (int) info.smblks,
566
	 (int) info.hblks,
567
	 (int) info.hblkhd,
568
	 (int) info.usmblks,
569
	 (int) info.fsmblks,
570
	 (int) info.uordblks,
571
	 (int) info.fordblks,
572
	 (int) info.keepcost,
573
	 (long) (thread_count * my_thread_stack_size + info.hblkhd + info.arena));
574
#endif
575
576
  puts("");
577
}
578
579
580
#ifndef DBUG_OFF
581
#ifdef EXTRA_DEBUG_DUMP_TABLE_LISTS
582
583
584
/*
585
  A fixed-size FIFO pointer queue that also doesn't allow one to put an
586
  element that has previously been put into it. 
587
  
588
  There is a hard-coded limit of the total number of queue put operations.
589
  The implementation is trivial and is intended for use in debug dumps only.
590
*/
591
592
template <class T> class Unique_fifo_queue
593
{
594
public:
595
  /* Add an element to the queue */
596
  void push_back(T *tbl)
597
  {
598
    if (!tbl)
599
      return;
600
    // check if we've already scheduled and/or dumped the element
601
    for (int i= 0; i < last; i++)
602
    {
603
      if (elems[i] == tbl)
604
        return;
605
    }
606
    elems[last++]=  tbl;
607
  }
608
609
  bool pop_first(T **elem)
610
  {
611
    if (first < last)
612
    {
613
      *elem= elems[first++];
614
      return TRUE;
615
    }
616
    return FALSE;
617
  }
618
619
  void reset()
620
  {
621
    first= last= 0;
622
  }
623
  enum { MAX_ELEMS=1000};
624
  T *elems[MAX_ELEMS];
625
  int first; // First undumped table
626
  int last;  // Last undumped element
627
};
628
629
class Dbug_table_list_dumper
630
{
631
  FILE *out;
632
  Unique_fifo_queue<TABLE_LIST> tables_fifo;
633
  Unique_fifo_queue<List<TABLE_LIST> > tbl_lists;
634
public:
635
  void dump_one_struct(TABLE_LIST *tbl);
636
637
  int dump_graph(st_select_lex *select_lex, TABLE_LIST *first_leaf);
638
};
639
640
641
void dump_TABLE_LIST_graph(SELECT_LEX *select_lex, TABLE_LIST* tl)
642
{
643
  Dbug_table_list_dumper dumper;
644
  dumper.dump_graph(select_lex, tl);
645
}
646
647
648
/* 
649
  - Dump one TABLE_LIST objects and its outgoing edges
650
  - Schedule that other objects seen along the edges are dumped too.
651
*/
652
653
void Dbug_table_list_dumper::dump_one_struct(TABLE_LIST *tbl)
654
{
655
  fprintf(out, "\"%p\" [\n", tbl);
656
  fprintf(out, "  label = \"%p|", tbl);
657
  fprintf(out, "alias=%s|", tbl->alias? tbl->alias : "NULL");
658
  fprintf(out, "<next_leaf>next_leaf=%p|", tbl->next_leaf);
659
  fprintf(out, "<next_local>next_local=%p|", tbl->next_local);
660
  fprintf(out, "<next_global>next_global=%p|", tbl->next_global);
661
  fprintf(out, "<embedding>embedding=%p", tbl->embedding);
662
663
  if (tbl->nested_join)
664
    fprintf(out, "|<nested_j>nested_j=%p", tbl->nested_join);
665
  if (tbl->join_list)
666
    fprintf(out, "|<join_list>join_list=%p", tbl->join_list);
667
  if (tbl->on_expr)
668
    fprintf(out, "|<on_expr>on_expr=%p", tbl->on_expr);
669
  fprintf(out, "\"\n");
670
  fprintf(out, "  shape = \"record\"\n];\n\n");
671
 
672
  if (tbl->next_leaf)
673
  {
674
    fprintf(out, "\n\"%p\":next_leaf -> \"%p\"[ color = \"#000000\" ];\n",  
675
            tbl, tbl->next_leaf);
676
    tables_fifo.push_back(tbl->next_leaf);
677
  }
678
  if (tbl->next_local)
679
  {
680
    fprintf(out, "\n\"%p\":next_local -> \"%p\"[ color = \"#404040\" ];\n",  
681
            tbl, tbl->next_local);
682
    tables_fifo.push_back(tbl->next_local);
683
  }
684
  if (tbl->next_global)
685
  {
686
    fprintf(out, "\n\"%p\":next_global -> \"%p\"[ color = \"#808080\" ];\n",  
687
            tbl, tbl->next_global);
688
    tables_fifo.push_back(tbl->next_global);
689
  }
690
691
  if (tbl->embedding)
692
  {
693
    fprintf(out, "\n\"%p\":embedding -> \"%p\"[ color = \"#FF0000\" ];\n",  
694
            tbl, tbl->embedding);
695
    tables_fifo.push_back(tbl->embedding);
696
  }
697
698
  if (tbl->join_list)
699
  {
700
    fprintf(out, "\n\"%p\":join_list -> \"%p\"[ color = \"#0000FF\" ];\n",  
701
            tbl, tbl->join_list);
702
    tbl_lists.push_back(tbl->join_list);
703
  }
704
}
705
706
707
int Dbug_table_list_dumper::dump_graph(st_select_lex *select_lex, 
708
                                       TABLE_LIST *first_leaf)
709
{
710
  DBUG_ENTER("Dbug_table_list_dumper::dump_graph");
711
  char filename[500];
712
  int no = 0;
713
  do
714
  {
715
    sprintf(filename, "tlist_tree%.3d.g", no);
716
    if ((out= fopen(filename, "rt")))
717
    {
718
      /* File exists, try next name */
719
      fclose(out);
720
    }
721
    no++;
722
  } while (out);
723
 
724
  /* Ok, found an unoccupied name, create the file */
725
  if (!(out= fopen(filename, "wt")))
726
  {
727
    DBUG_PRINT("tree_dump", ("Failed to create output file"));
728
    DBUG_RETURN(1);
729
  }
730
 
731
  DBUG_PRINT("tree_dump", ("dumping tree to %s", filename));
732
     
733
  fputs("digraph g {\n", out);
734
  fputs("graph [", out);
735
  fputs("  rankdir = \"LR\"", out);
736
  fputs("];", out);
737
   
738
  TABLE_LIST *tbl;
739
  tables_fifo.reset();
740
  dump_one_struct(first_leaf);   
741
  while (tables_fifo.pop_first(&tbl))
742
  {
743
    dump_one_struct(tbl);
744
  }
745
746
  List<TABLE_LIST> *plist;
747
  tbl_lists.push_back(&select_lex->top_join_list);
748
  while (tbl_lists.pop_first(&plist))
749
  {
750
    fprintf(out, "\"%p\" [\n", plist);
751
    fprintf(out, "  bgcolor = \"\"");
752
    fprintf(out, "  label = \"L %p\"", plist);
753
    fprintf(out, "  shape = \"record\"\n];\n\n");
754
  }
755
756
  fprintf(out, " { rank = same; ");
757
  for (TABLE_LIST *tl=first_leaf; tl; tl= tl->next_leaf)
758
    fprintf(out, " \"%p\"; ", tl);
759
  fprintf(out, "};\n");
760
  fputs("}", out);
761
  fclose(out);
762
 
763
  char filename2[500];
764
  filename[strlen(filename) - 1]= 0;
765
  filename[strlen(filename) - 1]= 0;
766
  sprintf(filename2, "%s.query", filename);
767
  
768
  if ((out= fopen(filename2, "wt")))
769
  {
770
    fprintf(out, "%s", current_thd->query);
771
    fclose(out);
772
  }
773
  DBUG_RETURN(0);
774
}
775
776
#endif
777
778
#endif 
779