Commit 77b8e5d9 authored by Magne Mahre's avatar Magne Mahre

Bug#26780: automatic vertical output for wide results

  
Feature from Eric Bergen, CLA signed 2007-06-27.
  
Adds new mysql client option "--auto-vertical-output", which causes
the client to test whether a result table is too wide for the current
window (where available) and emit vertical results in that case.
Otherwise, it sends normal tabular results.


client/client_priv.h:
  Add another enum value to client options, for automatic vertical output.
client/mysql.cc:
  Add another command-line option, "auto-vertical-output".
      
  Add functions to get the terminal width and functions to get the widths of fields.
      
  Use them together to emit vertical output when some output table is too wide to
  fit in the terminal.  If the terminal doesn't support reading its width, then assume
  80-positions wide.
mysql-test/r/mysql.result:
  Show that various select statements do work as expected.  Wide tables become vertical
  and narrow ones do not.
mysql-test/t/mysql.test:
  Show that various select statements do work as expected.
      
  These should be suitable for a wide range of window capabilities and sizes.  Under
  extreme circumstances, the results could be arbitrary.
parent 17a6040f
...@@ -78,6 +78,7 @@ enum options_client ...@@ -78,6 +78,7 @@ enum options_client
OPT_SLAP_DETACH, OPT_SLAP_DETACH,
OPT_MYSQL_REPLACE_INTO, OPT_BASE64_OUTPUT_MODE, OPT_SERVER_ID, OPT_MYSQL_REPLACE_INTO, OPT_BASE64_OUTPUT_MODE, OPT_SERVER_ID,
OPT_FIX_TABLE_NAMES, OPT_FIX_DB_NAMES, OPT_SSL_VERIFY_SERVER_CERT, OPT_FIX_TABLE_NAMES, OPT_FIX_DB_NAMES, OPT_SSL_VERIFY_SERVER_CERT,
OPT_AUTO_VERTICAL_OUTPUT,
OPT_DEBUG_INFO, OPT_DEBUG_CHECK, OPT_COLUMN_TYPES, OPT_ERROR_LOG_FILE, OPT_DEBUG_INFO, OPT_DEBUG_CHECK, OPT_COLUMN_TYPES, OPT_ERROR_LOG_FILE,
OPT_WRITE_BINLOG, OPT_DUMP_DATE, OPT_WRITE_BINLOG, OPT_DUMP_DATE,
OPT_INIT_COMMAND, OPT_INIT_COMMAND,
......
...@@ -143,6 +143,7 @@ static my_bool ignore_errors=0,wait_flag=0,quick=0, ...@@ -143,6 +143,7 @@ static my_bool ignore_errors=0,wait_flag=0,quick=0,
tty_password= 0, opt_nobeep=0, opt_reconnect=1, tty_password= 0, opt_nobeep=0, opt_reconnect=1,
opt_secure_auth= 0, opt_secure_auth= 0,
default_pager_set= 0, opt_sigint_ignore= 0, default_pager_set= 0, opt_sigint_ignore= 0,
auto_vertical_output= 0,
show_warnings= 0, executing_query= 0, interrupted_query= 0, show_warnings= 0, executing_query= 0, interrupted_query= 0,
ignore_spaces= 0; ignore_spaces= 0;
static my_bool debug_info_flag, debug_check_flag; static my_bool debug_info_flag, debug_check_flag;
...@@ -185,6 +186,7 @@ static MEM_ROOT hash_mem_root; ...@@ -185,6 +186,7 @@ static MEM_ROOT hash_mem_root;
static uint prompt_counter; static uint prompt_counter;
static char delimiter[16]= DEFAULT_DELIMITER; static char delimiter[16]= DEFAULT_DELIMITER;
static uint delimiter_length= 1; static uint delimiter_length= 1;
unsigned short terminal_width= 80;
#ifdef HAVE_SMEM #ifdef HAVE_SMEM
static char *shared_memory_base_name=0; static char *shared_memory_base_name=0;
...@@ -238,6 +240,8 @@ static const char* construct_prompt(); ...@@ -238,6 +240,8 @@ static const char* construct_prompt();
static char *get_arg(char *line, my_bool get_next_arg); static char *get_arg(char *line, my_bool get_next_arg);
static void init_username(); static void init_username();
static void add_int_to_prompt(int toadd); static void add_int_to_prompt(int toadd);
static int get_result_width(MYSQL_RES *res);
static int get_field_disp_length(MYSQL_FIELD * field);
/* A structure which contains information on the commands this program /* A structure which contains information on the commands this program
can understand. */ can understand. */
...@@ -1065,6 +1069,10 @@ static void mysql_end_timer(ulong start_time,char *buff); ...@@ -1065,6 +1069,10 @@ static void mysql_end_timer(ulong start_time,char *buff);
static void nice_time(double sec,char *buff,bool part_second); static void nice_time(double sec,char *buff,bool part_second);
extern "C" sig_handler mysql_end(int sig); extern "C" sig_handler mysql_end(int sig);
extern "C" sig_handler handle_sigint(int sig); extern "C" sig_handler handle_sigint(int sig);
#if defined(HAVE_TERMIOS_H)
static sig_handler window_resize(int sig);
#endif
int main(int argc,char *argv[]) int main(int argc,char *argv[])
{ {
...@@ -1144,8 +1152,8 @@ int main(int argc,char *argv[]) ...@@ -1144,8 +1152,8 @@ int main(int argc,char *argv[])
if (sql_connect(current_host,current_db,current_user,opt_password, if (sql_connect(current_host,current_db,current_user,opt_password,
opt_silent)) opt_silent))
{ {
quick=1; // Avoid history quick= 1; // Avoid history
status.exit_status=1; status.exit_status= 1;
mysql_end(-1); mysql_end(-1);
} }
if (!status.batch) if (!status.batch)
...@@ -1157,6 +1165,13 @@ int main(int argc,char *argv[]) ...@@ -1157,6 +1165,13 @@ int main(int argc,char *argv[])
signal(SIGINT, handle_sigint); // Catch SIGINT to clean up signal(SIGINT, handle_sigint); // Catch SIGINT to clean up
signal(SIGQUIT, mysql_end); // Catch SIGQUIT to clean up signal(SIGQUIT, mysql_end); // Catch SIGQUIT to clean up
#if defined(HAVE_TERMIOS_H)
/* Readline will call this if it installs a handler */
signal(SIGWINCH, window_resize);
/* call the SIGWINCH handler to get the default term width */
window_resize(0);
#endif
put_info("Welcome to the MySQL monitor. Commands end with ; or \\g.", put_info("Welcome to the MySQL monitor. Commands end with ; or \\g.",
INFO_INFO); INFO_INFO);
sprintf((char*) glob_buffer.ptr(), sprintf((char*) glob_buffer.ptr(),
...@@ -1315,6 +1330,16 @@ err: ...@@ -1315,6 +1330,16 @@ err:
} }
#if defined(HAVE_TERMIOS_H)
sig_handler window_resize(int sig)
{
struct winsize window_size;
if (ioctl(fileno(stdin), TIOCGWINSZ, &window_size) == 0)
terminal_width= window_size.ws_col;
}
#endif
static struct my_option my_long_options[] = static struct my_option my_long_options[] =
{ {
{"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
...@@ -1332,6 +1357,9 @@ static struct my_option my_long_options[] = ...@@ -1332,6 +1357,9 @@ static struct my_option my_long_options[] =
{"no-auto-rehash", 'A', {"no-auto-rehash", 'A',
"No automatic rehashing. One has to use 'rehash' to get table and field completion. This gives a quicker start of mysql and disables rehashing on reconnect. WARNING: options deprecated; use --disable-auto-rehash instead.", "No automatic rehashing. One has to use 'rehash' to get table and field completion. This gives a quicker start of mysql and disables rehashing on reconnect. WARNING: options deprecated; use --disable-auto-rehash instead.",
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
{"auto-vertical-output", OPT_AUTO_VERTICAL_OUTPUT,
"Automatically switch to vertical output mode if the result is wider than the terminal width.",
(uchar**) &auto_vertical_output, (uchar**) &auto_vertical_output, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"batch", 'B', {"batch", 'B',
"Don't use history file. Disable interactive behavior. (Enables --silent)", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, "Don't use history file. Disable interactive behavior. (Enables --silent)", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
{"character-sets-dir", OPT_CHARSETS_DIR, {"character-sets-dir", OPT_CHARSETS_DIR,
...@@ -3038,7 +3066,7 @@ com_go(String *buffer,char *line __attribute__((unused))) ...@@ -3038,7 +3066,7 @@ com_go(String *buffer,char *line __attribute__((unused)))
print_table_data_html(result); print_table_data_html(result);
else if (opt_xml) else if (opt_xml)
print_table_data_xml(result); print_table_data_xml(result);
else if (vertical) else if (vertical || (auto_vertical_output && (terminal_width < get_result_width(result))))
print_table_data_vertically(result); print_table_data_vertically(result);
else if (opt_silent && verbose <= 2 && !output_tables) else if (opt_silent && verbose <= 2 && !output_tables)
print_tab_data(result); print_tab_data(result);
...@@ -3369,6 +3397,65 @@ print_table_data(MYSQL_RES *result) ...@@ -3369,6 +3397,65 @@ print_table_data(MYSQL_RES *result)
my_afree((uchar*) num_flag); my_afree((uchar*) num_flag);
} }
/**
Return the length of a field after it would be rendered into text.
This doesn't know or care about multibyte characters. Assume we're
using such a charset. We can't know that all of the upcoming rows
for this column will have bytes that each render into some fraction
of a character. It's at least possible that a row has bytes that
all render into one character each, and so the maximum length is
still the number of bytes. (Assumption 1: This can't be better
because we can never know the number of characters that the DB is
going to send -- only the number of bytes. 2: Chars <= Bytes.)
@param field Pointer to a field to be inspected
@returns number of character positions to be used, at most
*/
static int get_field_disp_length(MYSQL_FIELD *field)
{
uint length= column_names ? field->name_length : 0;
if (quick)
length= max(length, field->length);
else
length= max(length, field->max_length);
if (length < 4 && !IS_NOT_NULL(field->flags))
length= 4; /* Room for "NULL" */
return length;
}
/**
For a new result, return the max number of characters that any
upcoming row may return.
@param result Pointer to the result to judge
@returns The max number of characters in any row of this result
*/
static int get_result_width(MYSQL_RES *result)
{
unsigned int len= 0;
MYSQL_FIELD *field;
MYSQL_FIELD_OFFSET offset;
#ifndef DBUG_OFF
offset= mysql_field_tell(result);
DBUG_ASSERT(offset == 0);
#else
offset= 0;
#endif
while ((field= mysql_fetch_field(result)) != NULL)
len+= get_field_disp_length(field) + 3; /* plus bar, space, & final space */
(void) mysql_field_seek(result, offset);
return len + 1; /* plus final bar. */
}
static void static void
tee_print_sized_data(const char *data, unsigned int data_length, unsigned int total_bytes_to_send, bool right_justified) tee_print_sized_data(const char *data, unsigned int data_length, unsigned int total_bytes_to_send, bool right_justified)
......
...@@ -201,6 +201,202 @@ COUNT (*) ...@@ -201,6 +201,202 @@ COUNT (*)
ERROR 2005 (HY000) at line 1: Unknown MySQL server host 'invalid_hostname' (errno) ERROR 2005 (HY000) at line 1: Unknown MySQL server host 'invalid_hostname' (errno)
End of 5.0 tests End of 5.0 tests
WARNING: --server-arg option not supported in this configuration. WARNING: --server-arg option not supported in this configuration.
*************************** 1. row ***************************
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
0: 0
+---+
| 1 |
+---+
| 1 |
+---+
Warning (Code 1286): Unknown table engine 'nonexistent' Warning (Code 1286): Unknown table engine 'nonexistent'
Warning (Code 1266): Using storage engine MyISAM for table 't2' Warning (Code 1266): Using storage engine MyISAM for table 't2'
Warning (Code 1286): Unknown table engine 'nonexistent2' Warning (Code 1286): Unknown table engine 'nonexistent2'
......
...@@ -366,6 +366,14 @@ remove_file $MYSQLTEST_VARDIR/tmp/bug31060.sql; ...@@ -366,6 +366,14 @@ remove_file $MYSQLTEST_VARDIR/tmp/bug31060.sql;
--exec $MYSQL --server-arg=no-defaults test -e "quit" --exec $MYSQL --server-arg=no-defaults test -e "quit"
--enable_query_log --enable_query_log
#
# Bug#26780: patch to add auto vertical output option to the cli.
#
# Make this wide enough that it will wrap almost everywhere.
--exec $MYSQL test --auto-vertical-output --table -e "SELECT 1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0;"
# Too short to wrap.
--exec $MYSQL test --auto-vertical-output --table -e "SELECT 1;"
# #
# Bug #25146: Some warnings/errors not shown when using --show-warnings # Bug #25146: Some warnings/errors not shown when using --show-warnings
# #
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment