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