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