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