1 /*
2    Copyright (c) 2000, 2015, Oracle and/or its affiliates.
3    Copyright (c) 2010, 2017, MariaDB
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; version 2 of the License.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA
17 */
18 
19 /* Show databases, tables or columns */
20 
21 #define SHOW_VERSION "9.10"
22 
23 #include "client_priv.h"
24 #include <my_sys.h>
25 #include <m_string.h>
26 #include <mysql.h>
27 #include <mysqld_error.h>
28 #include <signal.h>
29 #include <stdarg.h>
30 #include <sslopt-vars.h>
31 #include <welcome_copyright_notice.h>   /* ORACLE_WELCOME_COPYRIGHT_NOTICE */
32 
33 static char * host=0, *opt_password=0, *user=0;
34 static my_bool opt_show_keys= 0, opt_compress= 0, opt_count=0, opt_status= 0;
35 static my_bool tty_password= 0, opt_table_type= 0;
36 static my_bool debug_info_flag= 0, debug_check_flag= 0;
37 static uint my_end_arg= 0;
38 static uint opt_verbose=0;
39 static char *default_charset= (char*) MYSQL_AUTODETECT_CHARSET_NAME;
40 static char *opt_plugin_dir= 0, *opt_default_auth= 0;
41 
42 #ifdef HAVE_SMEM
43 static char *shared_memory_base_name=0;
44 #endif
45 static uint opt_protocol=0;
46 
47 static void get_options(int *argc,char ***argv);
48 static uint opt_mysql_port=0;
49 static int list_dbs(MYSQL *mysql,const char *wild);
50 static int list_tables(MYSQL *mysql,const char *db,const char *table);
51 static int list_table_status(MYSQL *mysql,const char *db,const char *table);
52 static int list_fields(MYSQL *mysql,const char *db,const char *table,
53 		       const char *field);
54 static void print_header(const char *header,size_t head_length,...);
55 static void print_row(const char *header,size_t head_length,...);
56 static void print_trailer(size_t length,...);
57 static void print_res_header(MYSQL_RES *result);
58 static void print_res_top(MYSQL_RES *result);
59 static void print_res_row(MYSQL_RES *result,MYSQL_ROW cur);
60 
61 static const char *load_default_groups[]=
62 { "mysqlshow","client", "client-server", "client-mariadb", 0 };
63 static char * opt_mysql_unix_port=0;
64 
65 int main(int argc, char **argv)
66 {
67   int error;
68   my_bool first_argument_uses_wildcards=0;
69   char *wild;
70   MYSQL mysql;
71   my_bool reconnect;
72   static char **defaults_argv;
73   MY_INIT(argv[0]);
74   sf_leaking_memory=1; /* don't report memory leaks on early exits */
75   load_defaults_or_exit("my", load_default_groups, &argc, &argv);
76   defaults_argv=argv;
77 
78   get_options(&argc,&argv);
79 
80   sf_leaking_memory=0; /* from now on we cleanup properly */
81   wild=0;
82   if (argc)
83   {
84     char *pos= argv[argc-1], *to;
85     for (to= pos ; *pos ; pos++, to++)
86     {
87       switch (*pos) {
88       case '*':
89 	*pos= '%';
90 	first_argument_uses_wildcards= 1;
91 	break;
92       case '?':
93 	*pos= '_';
94 	first_argument_uses_wildcards= 1;
95 	break;
96       case '%':
97       case '_':
98 	first_argument_uses_wildcards= 1;
99 	break;
100       case '\\':
101 	pos++;
102       default: break;
103       }
104       *to= *pos;
105     }
106     *to= *pos; /* just to copy a '\0'  if '\\' was used */
107   }
108   if (first_argument_uses_wildcards)
109     wild= argv[--argc];
110   else if (argc == 3)			/* We only want one field */
111     wild= argv[--argc];
112 
113   if (argc > 2)
114   {
115     fprintf(stderr,"%s: Too many arguments\n",my_progname);
116     exit(1);
117   }
118   mysql_init(&mysql);
119   if (opt_compress)
120     mysql_options(&mysql,MYSQL_OPT_COMPRESS,NullS);
121 #ifdef HAVE_OPENSSL
122   if (opt_use_ssl)
123   {
124     mysql_ssl_set(&mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
125 		  opt_ssl_capath, opt_ssl_cipher);
126     mysql_options(&mysql, MYSQL_OPT_SSL_CRL, opt_ssl_crl);
127     mysql_options(&mysql, MYSQL_OPT_SSL_CRLPATH, opt_ssl_crlpath);
128   }
129   mysql_options(&mysql,MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
130                 (char*)&opt_ssl_verify_server_cert);
131 #endif
132   if (opt_protocol)
133     mysql_options(&mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
134 #ifdef HAVE_SMEM
135   if (shared_memory_base_name)
136     mysql_options(&mysql,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name);
137 #endif
138   if (!strcmp(default_charset,MYSQL_AUTODETECT_CHARSET_NAME))
139     default_charset= (char *)my_default_csname();
140   mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, default_charset);
141 
142   if (opt_plugin_dir && *opt_plugin_dir)
143     mysql_options(&mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir);
144 
145   if (opt_default_auth && *opt_default_auth)
146     mysql_options(&mysql, MYSQL_DEFAULT_AUTH, opt_default_auth);
147 
148   mysql_options(&mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
149   mysql_options4(&mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
150                  "program_name", "mysqlshow");
151   if (!(mysql_real_connect(&mysql,host,user,opt_password,
152 			   (first_argument_uses_wildcards) ? "" :
153                            argv[0],opt_mysql_port,opt_mysql_unix_port,
154 			   0)))
155   {
156     fprintf(stderr,"%s: %s\n",my_progname,mysql_error(&mysql));
157     error= 1;
158     goto error;
159   }
160   reconnect= 1;
161   mysql_options(&mysql, MYSQL_OPT_RECONNECT, &reconnect);
162 
163   switch (argc) {
164   case 0:  error=list_dbs(&mysql,wild); break;
165   case 1:
166     if (opt_status)
167       error=list_table_status(&mysql,argv[0],wild);
168     else
169       error=list_tables(&mysql,argv[0],wild);
170     break;
171   default:
172     if (opt_status && ! wild)
173       error=list_table_status(&mysql,argv[0],argv[1]);
174     else
175       error=list_fields(&mysql,argv[0],argv[1],wild);
176     break;
177   }
178 error:
179   mysql_close(&mysql);			/* Close & free connection */
180   my_free(opt_password);
181   mysql_server_end();
182 #ifdef HAVE_SMEM
183   my_free(shared_memory_base_name);
184 #endif
185   free_defaults(defaults_argv);
186   my_end(my_end_arg);
187   exit(error ? 1 : 0);
188   return 0;				/* No compiler warnings */
189 }
190 
191 static struct my_option my_long_options[] =
192 {
193   {"character-sets-dir", 'c', "Directory for character set files.",
194    (char**) &charsets_dir, (char**) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0,
195    0, 0, 0, 0, 0},
196   {"default-character-set", OPT_DEFAULT_CHARSET,
197    "Set the default character set.", &default_charset,
198    &default_charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
199   {"count", OPT_COUNT,
200    "Show number of rows per table (may be slow for non-MyISAM tables).",
201    &opt_count, &opt_count, 0, GET_BOOL, NO_ARG, 0, 0, 0,
202    0, 0, 0},
203   {"compress", 'C', "Use compression in server/client protocol.",
204    &opt_compress, &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
205    0, 0, 0},
206   {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
207    0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
208   {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit.",
209    &debug_check_flag, &debug_check_flag, 0,
210    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
211   {"debug-info", OPT_DEBUG_INFO, "Print some debug info at exit.",
212    &debug_info_flag, &debug_info_flag,
213    0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
214   {"default_auth", OPT_DEFAULT_AUTH,
215    "Default authentication client-side plugin to use.",
216    &opt_default_auth, &opt_default_auth, 0,
217    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
218   {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG,
219    0, 0, 0, 0, 0, 0},
220   {"host", 'h', "Connect to host.", &host, &host, 0, GET_STR,
221    REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
222   {"status", 'i', "Shows a lot of extra information about each table.",
223    &opt_status, &opt_status, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
224    0, 0},
225   {"keys", 'k', "Show keys for table.", &opt_show_keys,
226    &opt_show_keys, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
227   {"password", 'p',
228    "Password to use when connecting to server. If password is not given, it's "
229    "solicited on the tty.",
230    0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
231   {"plugin_dir", OPT_PLUGIN_DIR, "Directory for client-side plugins.",
232    &opt_plugin_dir, &opt_plugin_dir, 0,
233    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
234   {"port", 'P', "Port number to use for connection or 0 for default to, in "
235    "order of preference, my.cnf, $MYSQL_TCP_PORT, "
236 #if MYSQL_PORT_DEFAULT == 0
237    "/etc/services, "
238 #endif
239    "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").",
240    &opt_mysql_port,
241    &opt_mysql_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0,
242    0},
243 #ifdef __WIN__
244   {"pipe", 'W', "Use named pipes to connect to server.", 0, 0, 0, GET_NO_ARG,
245    NO_ARG, 0, 0, 0, 0, 0, 0},
246 #endif
247   {"protocol", OPT_MYSQL_PROTOCOL,
248    "The protocol to use for connection (tcp, socket, pipe, memory).",
249    0, 0, 0, GET_STR,  REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
250 #ifdef HAVE_SMEM
251   {"shared-memory-base-name", OPT_SHARED_MEMORY_BASE_NAME,
252    "Base name of shared memory.", &shared_memory_base_name,
253    &shared_memory_base_name, 0, GET_STR_ALLOC, REQUIRED_ARG,
254    0, 0, 0, 0, 0, 0},
255 #endif
256   {"show-table-type", 't', "Show table type column.",
257    &opt_table_type, &opt_table_type, 0, GET_BOOL,
258    NO_ARG, 0, 0, 0, 0, 0, 0},
259   {"socket", 'S', "The socket file to use for connection.",
260    &opt_mysql_unix_port, &opt_mysql_unix_port, 0, GET_STR,
261    REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
262 #include <sslopt-longopts.h>
263 #ifndef DONT_ALLOW_USER_CHANGE
264   {"user", 'u', "User for login if not current user.", &user,
265    &user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
266 #endif
267   {"verbose", 'v',
268    "More verbose output; you can use this multiple times to get even more "
269    "verbose output.",
270    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
271   {"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG,
272    NO_ARG, 0, 0, 0, 0, 0, 0},
273   {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
274 };
275 
276 
277 static void print_version(void)
278 {
279   printf("%s  Ver %s Distrib %s, for %s (%s)\n",my_progname,SHOW_VERSION,
280 	 MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE);
281 }
282 
283 
284 static void usage(void)
285 {
286   print_version();
287   puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"));
288   puts("Shows the structure of a MySQL database (databases, tables, and columns).\n");
289   printf("Usage: %s [OPTIONS] [database [table [column]]]\n",my_progname);
290   puts("\n\
291 If last argument contains a shell or SQL wildcard (*,?,% or _) then only\n\
292 what\'s matched by the wildcard is shown.\n\
293 If no database is given then all matching databases are shown.\n\
294 If no table is given, then all matching tables in database are shown.\n\
295 If no column is given, then all matching columns and column types in table\n\
296 are shown.");
297   print_defaults("my",load_default_groups);
298   puts("");
299   my_print_help(my_long_options);
300   my_print_variables(my_long_options);
301 }
302 
303 
304 static my_bool
305 get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
306 	       char *argument)
307 {
308   switch(optid) {
309   case 'v':
310     opt_verbose++;
311     break;
312   case 'p':
313     if (argument == disabled_my_option)
314       argument= (char*) "";			/* Don't require password */
315     if (argument)
316     {
317       char *start=argument;
318       my_free(opt_password);
319       opt_password=my_strdup(argument,MYF(MY_FAE));
320       while (*argument) *argument++= 'x';		/* Destroy argument */
321       if (*start)
322 	start[1]=0;				/* Cut length of argument */
323       tty_password= 0;
324     }
325     else
326       tty_password=1;
327     break;
328   case 'W':
329 #ifdef __WIN__
330     opt_protocol = MYSQL_PROTOCOL_PIPE;
331 #endif
332     break;
333   case OPT_MYSQL_PROTOCOL:
334     if ((opt_protocol= find_type_with_warning(argument, &sql_protocol_typelib,
335                                               opt->name)) <= 0)
336     {
337       sf_leaking_memory= 1; /* no memory leak reports here */
338       exit(1);
339     }
340     break;
341   case '#':
342     DBUG_PUSH(argument ? argument : "d:t:o");
343     debug_check_flag= 1;
344     break;
345 #include <sslopt-case.h>
346   case 'V':
347     print_version();
348     exit(0);
349     break;
350   case '?':
351   case 'I':					/* Info */
352     usage();
353     exit(0);
354   }
355   return 0;
356 }
357 
358 
359 static void
360 get_options(int *argc,char ***argv)
361 {
362   int ho_error;
363 
364   if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option)))
365     exit(ho_error);
366 
367   if (tty_password)
368     opt_password=get_tty_password(NullS);
369   if (opt_count)
370   {
371     /*
372       We need to set verbose to 2 as we need to change the output to include
373       the number-of-rows column
374     */
375     opt_verbose= 2;
376   }
377   if (debug_info_flag)
378     my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO;
379   if (debug_check_flag)
380     my_end_arg= MY_CHECK_ERROR;
381   return;
382 }
383 
384 
385 static int
386 list_dbs(MYSQL *mysql,const char *wild)
387 {
388   const char *header;
389   size_t length = 0;
390   uint counter = 0;
391   ulong rowcount = 0L;
392   char tables[NAME_LEN+1], rows[NAME_LEN+1];
393   char query[NAME_LEN + 100];
394   MYSQL_FIELD *field;
395   MYSQL_RES *result;
396   MYSQL_ROW row= NULL, rrow;
397 
398   if (!(result=mysql_list_dbs(mysql,wild)))
399   {
400     fprintf(stderr,"%s: Cannot list databases: %s\n",my_progname,
401 	    mysql_error(mysql));
402     return 1;
403   }
404 
405   /*
406     If a wildcard was used, but there was only one row and it's name is an
407     exact match, we'll assume they really wanted to see the contents of that
408     database. This is because it is fairly common for database names to
409     contain the underscore (_), like INFORMATION_SCHEMA.
410    */
411   if (wild && mysql_num_rows(result) == 1)
412   {
413     row= mysql_fetch_row(result);
414     if (!my_strcasecmp(&my_charset_latin1, row[0], wild))
415     {
416       mysql_free_result(result);
417       if (opt_status)
418         return list_table_status(mysql, wild, NULL);
419       else
420         return list_tables(mysql, wild, NULL);
421     }
422   }
423 
424   if (wild)
425     printf("Wildcard: %s\n",wild);
426 
427   header="Databases";
428   length= strlen(header);
429   field=mysql_fetch_field(result);
430   if (length < field->max_length)
431     length=field->max_length;
432 
433   if (!opt_verbose)
434     print_header(header,length,NullS);
435   else if (opt_verbose == 1)
436     print_header(header,length,"Tables",6,NullS);
437   else
438     print_header(header,length,"Tables",6,"Total Rows",12,NullS);
439 
440   /* The first row may have already been read up above. */
441   while (row || (row= mysql_fetch_row(result)))
442   {
443     counter++;
444 
445     if (opt_verbose)
446     {
447       if (!(mysql_select_db(mysql,row[0])))
448       {
449 	MYSQL_RES *tresult = mysql_list_tables(mysql,(char*)NULL);
450 	if (mysql_affected_rows(mysql) > 0)
451 	{
452 	  sprintf(tables,"%6lu",(ulong) mysql_affected_rows(mysql));
453 	  rowcount = 0;
454 	  if (opt_verbose > 1)
455 	  {
456             /* Print the count of tables and rows for each database */
457             MYSQL_ROW trow;
458 	    while ((trow = mysql_fetch_row(tresult)))
459 	    {
460               my_snprintf(query, sizeof(query),
461                           "SELECT COUNT(*) FROM `%s`", trow[0]);
462 	      if (!(mysql_query(mysql,query)))
463 	      {
464 		MYSQL_RES *rresult;
465 		if ((rresult = mysql_store_result(mysql)))
466 		{
467 		  rrow = mysql_fetch_row(rresult);
468 		  rowcount += (ulong) strtoull(rrow[0], (char**) 0, 10);
469 		  mysql_free_result(rresult);
470 		}
471 	      }
472 	    }
473 	    sprintf(rows,"%12lu",rowcount);
474 	  }
475 	}
476 	else
477 	{
478 	  sprintf(tables,"%6d",0);
479 	  sprintf(rows,"%12d",0);
480 	}
481 	mysql_free_result(tresult);
482       }
483       else
484       {
485 	strmov(tables,"N/A");
486 	strmov(rows,"N/A");
487       }
488     }
489 
490     if (!opt_verbose)
491       print_row(row[0],length,0);
492     else if (opt_verbose == 1)
493       print_row(row[0],length,tables,6,NullS);
494     else
495       print_row(row[0],length,tables,6,rows,12,NullS);
496 
497     row= NULL;
498   }
499 
500   print_trailer(length,
501 		(opt_verbose > 0 ? 6 : 0),
502 		(opt_verbose > 1 ? 12 :0),
503 		0);
504 
505   if (counter && opt_verbose)
506     printf("%u row%s in set.\n",counter,(counter > 1) ? "s" : "");
507   mysql_free_result(result);
508   return 0;
509 }
510 
511 
512 static int
513 list_tables(MYSQL *mysql,const char *db,const char *table)
514 {
515   const char *header;
516   size_t head_length;
517   uint counter = 0;
518   char query[NAME_LEN + 100], rows[NAME_LEN], fields[16];
519   MYSQL_FIELD *field;
520   MYSQL_RES *result;
521   MYSQL_ROW row, rrow;
522 
523   if (mysql_select_db(mysql,db))
524   {
525     fprintf(stderr,"%s: Cannot connect to db %s: %s\n",my_progname,db,
526 	    mysql_error(mysql));
527     return 1;
528   }
529   if (table)
530   {
531     /*
532       We just hijack the 'rows' variable for a bit to store the escaped
533       table name
534     */
535     mysql_real_escape_string(mysql, rows, table, (unsigned long)strlen(table));
536     my_snprintf(query, sizeof(query), "show%s tables like '%s'",
537                 opt_table_type ? " full" : "", rows);
538   }
539   else
540     my_snprintf(query, sizeof(query), "show%s tables",
541                 opt_table_type ? " full" : "");
542   if (mysql_query(mysql, query) || !(result= mysql_store_result(mysql)))
543   {
544     fprintf(stderr,"%s: Cannot list tables in %s: %s\n",my_progname,db,
545 	    mysql_error(mysql));
546     exit(1);
547   }
548   printf("Database: %s",db);
549   if (table)
550     printf("  Wildcard: %s",table);
551   putchar('\n');
552 
553   header="Tables";
554   head_length= strlen(header);
555   field=mysql_fetch_field(result);
556   if (head_length < field->max_length)
557     head_length=field->max_length;
558 
559   if (opt_table_type)
560   {
561     if (!opt_verbose)
562       print_header(header,head_length,"table_type",10,NullS);
563     else if (opt_verbose == 1)
564       print_header(header,head_length,"table_type",10,"Columns",8,NullS);
565     else
566     {
567       print_header(header,head_length,"table_type",10,"Columns",8,
568 		   "Total Rows",10,NullS);
569     }
570   }
571   else
572   {
573     if (!opt_verbose)
574       print_header(header,head_length,NullS);
575     else if (opt_verbose == 1)
576       print_header(header,head_length,"Columns",8,NullS);
577     else
578       print_header(header,head_length,"Columns",8, "Total Rows",10,NullS);
579   }
580 
581   while ((row = mysql_fetch_row(result)))
582   {
583     counter++;
584     if (opt_verbose > 0)
585     {
586       if (!(mysql_select_db(mysql,db)))
587       {
588 	MYSQL_RES *rresult = mysql_list_fields(mysql,row[0],NULL);
589 	ulong rowcount=0L;
590 	if (!rresult)
591 	{
592 	  strmov(fields,"N/A");
593 	  strmov(rows,"N/A");
594 	}
595 	else
596 	{
597 	  sprintf(fields,"%8u",(uint) mysql_num_fields(rresult));
598 	  mysql_free_result(rresult);
599 
600 	  if (opt_verbose > 1)
601 	  {
602             /* Print the count of rows for each table */
603             my_snprintf(query, sizeof(query), "SELECT COUNT(*) FROM `%s`",
604                         row[0]);
605 	    if (!(mysql_query(mysql,query)))
606 	    {
607 	      if ((rresult = mysql_store_result(mysql)))
608 	      {
609 		rrow = mysql_fetch_row(rresult);
610 		rowcount += (unsigned long) strtoull(rrow[0], (char**) 0, 10);
611 		mysql_free_result(rresult);
612 	      }
613 	      sprintf(rows,"%10lu",rowcount);
614 	    }
615 	    else
616 	      sprintf(rows,"%10d",0);
617 	  }
618 	}
619       }
620       else
621       {
622 	strmov(fields,"N/A");
623 	strmov(rows,"N/A");
624       }
625     }
626     if (opt_table_type)
627     {
628       if (!opt_verbose)
629 	print_row(row[0],head_length,row[1],10,NullS);
630       else if (opt_verbose == 1)
631 	print_row(row[0],head_length,row[1],10,fields,8,NullS);
632       else
633 	print_row(row[0],head_length,row[1],10,fields,8,rows,10,NullS);
634     }
635     else
636     {
637       if (!opt_verbose)
638 	print_row(row[0],head_length,NullS);
639       else if (opt_verbose == 1)
640 	print_row(row[0],head_length, fields,8, NullS);
641       else
642 	print_row(row[0],head_length, fields,8, rows,10, NullS);
643     }
644   }
645 
646   print_trailer(head_length,
647 		(opt_table_type ? 10 : opt_verbose > 0 ? 8 : 0),
648 		(opt_table_type ? (opt_verbose > 0 ? 8 : 0)
649 		 : (opt_verbose > 1 ? 10 :0)),
650 		!opt_table_type ? 0 : opt_verbose > 1 ? 10 :0,
651 		0);
652 
653   if (counter && opt_verbose)
654     printf("%u row%s in set.\n\n",counter,(counter > 1) ? "s" : "");
655 
656   mysql_free_result(result);
657   return 0;
658 }
659 
660 
661 static int
662 list_table_status(MYSQL *mysql,const char *db,const char *wild)
663 {
664   char query[NAME_LEN + 100];
665   size_t len;
666   MYSQL_RES *result;
667   MYSQL_ROW row;
668 
669   len= sizeof(query);
670   len-= my_snprintf(query, len, "show table status from `%s`", db);
671   if (wild && wild[0] && len)
672     strxnmov(query + strlen(query), len - 1, " like '", wild, "'", NullS);
673   if (mysql_query(mysql,query) || !(result=mysql_store_result(mysql)))
674   {
675     fprintf(stderr,"%s: Cannot get status for db: %s, table: %s: %s\n",
676 	    my_progname,db,wild ? wild : "",mysql_error(mysql));
677     if (mysql_errno(mysql) == ER_PARSE_ERROR)
678       fprintf(stderr,"This error probably means that your MySQL server doesn't support the\n\'show table status' command.\n");
679     return 1;
680   }
681 
682   printf("Database: %s",db);
683   if (wild)
684     printf("  Wildcard: %s",wild);
685   putchar('\n');
686 
687   print_res_header(result);
688   while ((row=mysql_fetch_row(result)))
689     print_res_row(result,row);
690   print_res_top(result);
691   mysql_free_result(result);
692   return 0;
693 }
694 
695 /*
696   list fields uses field interface as an example of how to parse
697   a MYSQL FIELD
698 */
699 
700 static int
701 list_fields(MYSQL *mysql,const char *db,const char *table,
702 	    const char *wild)
703 {
704   char query[NAME_LEN + 100];
705   size_t len;
706   MYSQL_RES *result;
707   MYSQL_ROW row;
708   ulong UNINIT_VAR(rows);
709 
710   if (mysql_select_db(mysql,db))
711   {
712     fprintf(stderr,"%s: Cannot connect to db: %s: %s\n",my_progname,db,
713 	    mysql_error(mysql));
714     return 1;
715   }
716 
717   if (opt_count)
718   {
719     my_snprintf(query, sizeof(query), "select count(*) from `%s`", table);
720     if (mysql_query(mysql,query) || !(result=mysql_store_result(mysql)))
721     {
722       fprintf(stderr,"%s: Cannot get record count for db: %s, table: %s: %s\n",
723               my_progname,db,table,mysql_error(mysql));
724       return 1;
725     }
726     row= mysql_fetch_row(result);
727     rows= (ulong) strtoull(row[0], (char**) 0, 10);
728     mysql_free_result(result);
729   }
730 
731   len= sizeof(query);
732   len-= my_snprintf(query, len, "show /*!32332 FULL */ columns from `%s`",
733                     table);
734   if (wild && wild[0] && len)
735     strxnmov(query + strlen(query), len - 1, " like '", wild, "'", NullS);
736   if (mysql_query(mysql,query) || !(result=mysql_store_result(mysql)))
737   {
738     fprintf(stderr,"%s: Cannot list columns in db: %s, table: %s: %s\n",
739 	    my_progname,db,table,mysql_error(mysql));
740     return 1;
741   }
742 
743   printf("Database: %s  Table: %s", db, table);
744   if (opt_count)
745     printf("  Rows: %lu", rows);
746   if (wild && wild[0])
747     printf("  Wildcard: %s",wild);
748   putchar('\n');
749 
750   print_res_header(result);
751   while ((row=mysql_fetch_row(result)))
752     print_res_row(result,row);
753   print_res_top(result);
754   if (opt_show_keys)
755   {
756     my_snprintf(query, sizeof(query), "show keys from `%s`", table);
757     if (mysql_query(mysql,query) || !(result=mysql_store_result(mysql)))
758     {
759       fprintf(stderr,"%s: Cannot list keys in db: %s, table: %s: %s\n",
760 	      my_progname,db,table,mysql_error(mysql));
761       return 1;
762     }
763     if (mysql_num_rows(result))
764     {
765       print_res_header(result);
766       while ((row=mysql_fetch_row(result)))
767 	print_res_row(result,row);
768       print_res_top(result);
769     }
770     else
771       puts("Table has no keys");
772   }
773   mysql_free_result(result);
774   return 0;
775 }
776 
777 
778 /*****************************************************************************
779  General functions to print a nice ascii-table from data
780 *****************************************************************************/
781 
782 static void
783 print_header(const char *header,size_t head_length,...)
784 {
785   va_list args;
786   size_t length,i,str_length,pre_space;
787   const char *field;
788 
789   va_start(args,head_length);
790   putchar('+');
791   field=header; length=head_length;
792   for (;;)
793   {
794     for (i=0 ; i < length+2 ; i++)
795       putchar('-');
796     putchar('+');
797     if (!(field=va_arg(args,char *)))
798       break;
799     length=va_arg(args,uint);
800   }
801   va_end(args);
802   putchar('\n');
803 
804   va_start(args,head_length);
805   field=header; length=head_length;
806   putchar('|');
807   for (;;)
808   {
809     str_length= strlen(field);
810     if (str_length > length)
811       str_length=length+1;
812     pre_space= ((length- str_length)/2)+1;
813     for (i=0 ; i < pre_space ; i++)
814       putchar(' ');
815     for (i = 0 ; i < str_length ; i++)
816       putchar(field[i]);
817     length=length+2-str_length-pre_space;
818     for (i=0 ; i < length ; i++)
819       putchar(' ');
820     putchar('|');
821     if (!(field=va_arg(args,char *)))
822       break;
823     length=va_arg(args,uint);
824   }
825   va_end(args);
826   putchar('\n');
827 
828   va_start(args,head_length);
829   putchar('+');
830   field=header; length=head_length;
831   for (;;)
832   {
833     for (i=0 ; i < length+2 ; i++)
834       putchar('-');
835     putchar('+');
836     if (!(field=va_arg(args,char *)))
837       break;
838     length=va_arg(args,uint);
839   }
840   va_end(args);
841   putchar('\n');
842 }
843 
844 
845 static void
846 print_row(const char *header,size_t head_length,...)
847 {
848   va_list args;
849   const char *field;
850   size_t i,length,field_length;
851 
852   va_start(args,head_length);
853   field=header; length=head_length;
854   for (;;)
855   {
856     putchar('|');
857     putchar(' ');
858     fputs(field,stdout);
859     field_length= strlen(field);
860     for (i=field_length ; i <= length ; i++)
861       putchar(' ');
862     if (!(field=va_arg(args,char *)))
863       break;
864     length=va_arg(args,uint);
865   }
866   va_end(args);
867   putchar('|');
868   putchar('\n');
869 }
870 
871 
872 static void
873 print_trailer(size_t head_length,...)
874 {
875   va_list args;
876   size_t length,i;
877 
878   va_start(args,head_length);
879   length=head_length;
880   putchar('+');
881   for (;;)
882   {
883     for (i=0 ; i < length+2 ; i++)
884       putchar('-');
885     putchar('+');
886     if (!(length=va_arg(args,uint)))
887       break;
888   }
889   va_end(args);
890   putchar('\n');
891 }
892 
893 
894 static void print_res_header(MYSQL_RES *result)
895 {
896   MYSQL_FIELD *field;
897 
898   print_res_top(result);
899   mysql_field_seek(result,0);
900   putchar('|');
901   while ((field = mysql_fetch_field(result)))
902   {
903     printf(" %-*s|",(int) field->max_length+1,field->name);
904   }
905   putchar('\n');
906   print_res_top(result);
907 }
908 
909 
910 static void print_res_top(MYSQL_RES *result)
911 {
912   size_t i,length;
913   MYSQL_FIELD *field;
914 
915   putchar('+');
916   mysql_field_seek(result,0);
917   while((field = mysql_fetch_field(result)))
918   {
919     if ((length= strlen(field->name)) > field->max_length)
920       field->max_length=(ulong)length;
921     else
922       length=field->max_length;
923     for (i=length+2 ; i--> 0 ; )
924       putchar('-');
925     putchar('+');
926   }
927   putchar('\n');
928 }
929 
930 
931 static void print_res_row(MYSQL_RES *result,MYSQL_ROW cur)
932 {
933   uint i,length;
934   MYSQL_FIELD *field;
935   putchar('|');
936   mysql_field_seek(result,0);
937   for (i=0 ; i < mysql_num_fields(result); i++)
938   {
939     field = mysql_fetch_field(result);
940     length=field->max_length;
941     printf(" %-*s|",length+1,cur[i] ? (char*) cur[i] : "");
942   }
943   putchar('\n');
944 }
945