summaryrefslogtreecommitdiff
path: root/client/mysql.cc
diff options
context:
space:
mode:
Diffstat (limited to 'client/mysql.cc')
-rw-r--r--client/mysql.cc506
1 files changed, 394 insertions, 112 deletions
diff --git a/client/mysql.cc b/client/mysql.cc
index fcdd63b31e6..b0db42e0568 100644
--- a/client/mysql.cc
+++ b/client/mysql.cc
@@ -44,7 +44,7 @@
#include <locale.h>
#endif
-const char *VER= "14.7";
+const char *VER= "14.12";
/* Don't try to make a nice table if the data is too big */
#define MAX_COLUMN_LENGTH 1024
@@ -135,7 +135,8 @@ static my_bool info_flag=0,ignore_errors=0,wait_flag=0,quick=0,
opt_xml=0,opt_nopager=1, opt_outfile=0, named_cmds= 0,
tty_password= 0, opt_nobeep=0, opt_reconnect=1,
default_charset_used= 0, opt_secure_auth= 0,
- default_pager_set= 0, opt_sigint_ignore= 0;
+ default_pager_set= 0, opt_sigint_ignore= 0,
+ show_warnings = 0;
static ulong opt_max_allowed_packet, opt_net_buffer_length;
static uint verbose=0,opt_silent=0,opt_mysql_port=0, opt_local_infile=0;
static my_string opt_mysql_unix_port=0;
@@ -144,6 +145,7 @@ static char *current_host,*current_db,*current_user=0,*opt_password=0,
*current_prompt=0, *delimiter_str= 0,
*default_charset= (char*) MYSQL_DEFAULT_CHARSET_NAME;
static char *histfile;
+static char *histfile_tmp;
static String glob_buffer,old_buffer;
static String processed_prompt;
static char *full_username=0,*part_username=0,*default_prompt=0;
@@ -154,6 +156,8 @@ static char mysql_charsets_dir[FN_REFLEN+1];
static const char *xmlmeta[] = {
"&", "&amp;",
"<", "&lt;",
+ ">", "&gt;",
+ "\"", "&quot;",
0, 0
};
static const char *day_names[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
@@ -181,6 +185,7 @@ void tee_fprintf(FILE *file, const char *fmt, ...);
void tee_fputs(const char *s, FILE *file);
void tee_puts(const char *s, FILE *file);
void tee_putc(int c, FILE *file);
+static void tee_print_sized_data(const char *, unsigned int, unsigned int, bool);
/* The names of functions that actually do the manipulation. */
static int get_options(int argc,char **argv);
static int com_quit(String *str,char*),
@@ -191,14 +196,15 @@ static int com_quit(String *str,char*),
com_use(String *str,char*), com_source(String *str, char*),
com_rehash(String *str, char*), com_tee(String *str, char*),
com_notee(String *str, char*), com_charset(String *str,char*),
- com_prompt(String *str, char*), com_delimiter(String *str, char*);
+ com_prompt(String *str, char*), com_delimiter(String *str, char*),
+ com_warnings(String *str, char*), com_nowarnings(String *str, char*);
#ifdef USE_POPEN
static int com_nopager(String *str, char*), com_pager(String *str, char*),
com_edit(String *str,char*), com_shell(String *str, char *);
#endif
-static int read_lines(bool execute_commands);
+static int read_and_execute(bool interactive);
static int sql_connect(char *host,char *database,char *user,char *password,
uint silent);
static int put_info(const char *str,INFO_TYPE info,uint error=0,
@@ -232,7 +238,7 @@ static COMMANDS commands[] = {
{ "connect",'r', com_connect,1,
"Reconnect to the server. Optional arguments are db and host." },
{ "delimiter", 'd', com_delimiter, 1,
- "Set query delimiter. " },
+ "Set statement delimiter. NOTE: Takes the rest of the line as new delimiter." },
#ifdef USE_POPEN
{ "edit", 'e', com_edit, 0, "Edit command with $EDITOR."},
#endif
@@ -263,8 +269,12 @@ static COMMANDS commands[] = {
"Set outfile [to_outfile]. Append everything into given outfile." },
{ "use", 'u', com_use, 1,
"Use another database. Takes database name as argument." },
- { "charset_name", 'C', com_charset, 1,
- "Switch to another charset. Might be needed for processing binlog." },
+ { "charset", 'C', com_charset, 1,
+ "Switch to another charset. Might be needed for processing binlog with multi-byte charsets." },
+ { "warnings", 'W', com_warnings, 0,
+ "Show warnings after every statement." },
+ { "nowarning", 'w', com_nowarnings, 0,
+ "Don't show warnings after every statement." },
/* Get bash-like expansion for some commands */
{ "create table", 0, 0, 0, ""},
{ "create database", 0, 0, 0, ""},
@@ -313,7 +323,7 @@ static void initialize_readline (char *name);
static void fix_history(String *final_command);
#endif
-static COMMANDS *find_command (char *name,char cmd_name);
+static COMMANDS *find_command(char *name,char cmd_name);
static bool add_line(String &buffer,char *line,char *in_string,
bool *ml_comment);
static void remove_cntrl(String &buffer);
@@ -322,6 +332,7 @@ static void print_table_data_html(MYSQL_RES *result);
static void print_table_data_xml(MYSQL_RES *result);
static void print_tab_data(MYSQL_RES *result);
static void print_table_data_vertically(MYSQL_RES *result);
+static void print_warnings(void);
static ulong start_timer(void);
static void end_timer(ulong start_time,char *buff);
static void mysql_end_timer(ulong start_time,char *buff);
@@ -332,16 +343,15 @@ static sig_handler mysql_end(int sig);
int main(int argc,char *argv[])
{
char buff[80];
- char *defaults, *extra_defaults;
- char *emb_argv[3];
- int emb_argc= 1;
+ char *defaults, *extra_defaults, *group_suffix;
+ char *emb_argv[4];
+ int emb_argc;
- emb_argv[0]= argv[0];
- get_defaults_files(argc, argv, &defaults, &extra_defaults);
- if (defaults)
- emb_argv[emb_argc++]= defaults;
- if (extra_defaults)
- emb_argv[emb_argc++]= extra_defaults;
+ /* Get --defaults-xxx args for mysql_server_init() */
+ emb_argc= get_defaults_options(argc, argv, &defaults, &extra_defaults,
+ &group_suffix)+1;
+ memcpy((char*) emb_argv, (char*) argv, emb_argc * sizeof(*argv));
+ emb_argv[emb_argc]= 0;
MY_INIT(argv[0]);
DBUG_ENTER("main");
@@ -425,7 +435,7 @@ int main(int argc,char *argv[])
put_info((char*) glob_buffer.ptr(),INFO_INFO);
#ifdef HAVE_READLINE
- initialize_readline(my_progname);
+ initialize_readline((char*) my_progname);
if (!status.batch && !quick && !opt_html && !opt_xml)
{
/* read-history from file, default ~/.mysql_history*/
@@ -438,12 +448,27 @@ int main(int argc,char *argv[])
MYF(MY_WME));
if (histfile)
sprintf(histfile,"%s/.mysql_history",getenv("HOME"));
+ char link_name[FN_REFLEN];
+ if (my_readlink(link_name, histfile, 0) == 0 &&
+ strncmp(link_name, "/dev/null", 10) == 0)
+ {
+ /* The .mysql_history file is a symlink to /dev/null, don't use it */
+ my_free(histfile, MYF(MY_ALLOW_ZERO_PTR));
+ histfile= 0;
+ }
}
if (histfile)
{
if (verbose)
tee_fprintf(stdout, "Reading history-file %s\n",histfile);
read_history(histfile);
+ if (!(histfile_tmp= (char*) my_malloc((uint) strlen(histfile) + 5,
+ MYF(MY_WME))))
+ {
+ fprintf(stderr, "Couldn't allocate memory for temp histfile!\n");
+ exit(1);
+ }
+ sprintf(histfile_tmp, "%s.TMP", histfile);
}
}
#endif
@@ -454,7 +479,7 @@ int main(int argc,char *argv[])
"Type 'help [[%]function name[%]]' to get help on usage of function.\n");
#endif
put_info(buff,INFO_INFO);
- status.exit_status=read_lines(1); // read lines and execute them
+ status.exit_status= read_and_execute(!status.batch);
if (opt_outfile)
end_tee();
mysql_end(0);
@@ -467,12 +492,13 @@ sig_handler mysql_end(int sig)
{
mysql_close(&mysql);
#ifdef HAVE_READLINE
- if (!status.batch && !quick && !opt_html && !opt_xml)
+ if (!status.batch && !quick && !opt_html && !opt_xml && histfile)
{
/* write-history */
if (verbose)
tee_fprintf(stdout, "Writing history-file %s\n",histfile);
- write_history(histfile);
+ if (!write_history(histfile_tmp))
+ my_rename(histfile_tmp, histfile, MYF(MY_WME));
}
batch_readline_end(status.line_buff);
completion_hash_free(&ht);
@@ -487,6 +513,7 @@ sig_handler mysql_end(int sig)
my_free(opt_password,MYF(MY_ALLOW_ZERO_PTR));
my_free(opt_mysql_unix_port,MYF(MY_ALLOW_ZERO_PTR));
my_free(histfile,MYF(MY_ALLOW_ZERO_PTR));
+ my_free(histfile_tmp,MYF(MY_ALLOW_ZERO_PTR));
my_free(current_db,MYF(MY_ALLOW_ZERO_PTR));
my_free(current_host,MYF(MY_ALLOW_ZERO_PTR));
my_free(current_user,MYF(MY_ALLOW_ZERO_PTR));
@@ -612,7 +639,7 @@ static struct my_option my_long_options[] =
NO_ARG, 0, 0, 0, 0, 0, 0},
#endif
{"port", 'P', "Port number to use for connection.", (gptr*) &opt_mysql_port,
- (gptr*) &opt_mysql_port, 0, GET_UINT, REQUIRED_ARG, MYSQL_PORT, 0, 0, 0, 0,
+ (gptr*) &opt_mysql_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0,
0},
{"prompt", OPT_PROMPT, "Set the mysql prompt to this value.",
(gptr*) &current_prompt, (gptr*) &current_prompt, 0, GET_STR_ALLOC,
@@ -687,6 +714,9 @@ static struct my_option my_long_options[] =
{"secure-auth", OPT_SECURE_AUTH, "Refuse client connecting to server if it"
" uses old (pre-4.1.1) protocol", (gptr*) &opt_secure_auth,
(gptr*) &opt_secure_auth, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"show-warnings", OPT_SHOW_WARNINGS, "Show warnings after every statement.",
+ (gptr*) &show_warnings, (gptr*) &show_warnings, 0, GET_BOOL, NO_ARG,
+ 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
@@ -709,7 +739,7 @@ static void usage(int version)
my_progname, VER, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE,
readline, rl_library_version);
#else
- printf("%s Ver %s Distrib %s, for %s (%s)", my_progname, VER,
+ printf("%s Ver %s Distrib %s, for %s (%s)\n", my_progname, VER,
MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE);
#endif
@@ -793,6 +823,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
case OPT_NOPAGER:
printf("WARNING: option deprecated; use --disable-pager instead.\n");
opt_nopager= 1;
+ break;
case OPT_MYSQL_PROTOCOL:
{
if ((opt_protocol= find_type(argument, &sql_protocol_typelib,0)) <= 0)
@@ -938,7 +969,7 @@ static int get_options(int argc, char **argv)
return(0);
}
-static int read_lines(bool execute_commands)
+static int read_and_execute(bool interactive)
{
#if defined(OS2) || defined(__NETWARE__)
char linebuffer[254];
@@ -958,7 +989,7 @@ static int read_lines(bool execute_commands)
for (;;)
{
- if (status.batch || !execute_commands)
+ if (!interactive)
{
line=batch_readline(status.line_buff);
line_number++;
@@ -991,11 +1022,12 @@ static int read_lines(bool execute_commands)
#elif defined(__WIN__)
if (!tmpbuf.is_alloced())
tmpbuf.alloc(65535);
+ tmpbuf.length(0);
buffer.length(0);
unsigned long clen;
do
{
- line= my_cgets((char *) tmpbuf.ptr(), tmpbuf.alloced_length()-1, &clen);
+ line= my_cgets((char*)tmpbuf.ptr(), tmpbuf.alloced_length()-1, &clen);
buffer.append(line, clen);
/*
if we got buffer fully filled than there is a chance that
@@ -1051,15 +1083,15 @@ static int read_lines(bool execute_commands)
Check if line is a mysql command line
(We want to allow help, print and clear anywhere at line start
*/
- if (execute_commands && (named_cmds || glob_buffer.is_empty())
- && !in_string && (com=find_command(line,0)))
+ if ((named_cmds || glob_buffer.is_empty())
+ && !ml_comment && !in_string && (com=find_command(line,0)))
{
if ((*com->func)(&glob_buffer,line) > 0)
break;
if (glob_buffer.is_empty()) // If buffer was emptied
in_string=0;
#ifdef HAVE_READLINE
- if (status.add_to_history && not_in_history(line))
+ if (interactive && status.add_to_history && not_in_history(line))
add_history(line);
#endif
continue;
@@ -1069,7 +1101,7 @@ static int read_lines(bool execute_commands)
}
/* if in batch mode, send last query even if it doesn't end with \g or go */
- if ((status.batch || !execute_commands) && !status.exit_status)
+ if (!interactive && !status.exit_status)
{
remove_cntrl(glob_buffer);
if (!glob_buffer.is_empty())
@@ -1091,10 +1123,12 @@ static int read_lines(bool execute_commands)
}
-static COMMANDS *find_command (char *name,char cmd_char)
+static COMMANDS *find_command(char *name,char cmd_char)
{
uint len;
char *end;
+ DBUG_ENTER("find_command");
+ DBUG_PRINT("enter",("name: '%s' char: %d", name ? name : "NULL", cmd_char));
if (!name)
{
@@ -1105,8 +1139,18 @@ static COMMANDS *find_command (char *name,char cmd_char)
{
while (my_isspace(charset_info,*name))
name++;
- if (strstr(name, delimiter) || strstr(name, "\\g"))
- return ((COMMANDS *) 0);
+ /*
+ If there is an \\g in the row or if the row has a delimiter but
+ this is not a delimiter command, let add_line() take care of
+ parsing the row and calling find_command()
+ */
+ if (strstr(name, "\\g") || (strstr(name, delimiter) &&
+ !(strlen(name) >= 9 &&
+ !my_strnncoll(charset_info,
+ (uchar*) name, 9,
+ (const uchar*) "delimiter",
+ 9))))
+ DBUG_RETURN((COMMANDS *) 0);
if ((end=strcont(name," \t")))
{
len=(uint) (end - name);
@@ -1122,15 +1166,18 @@ static COMMANDS *find_command (char *name,char cmd_char)
for (uint i= 0; commands[i].name; i++)
{
if (commands[i].func &&
- ((name &&
+ ((name &&
!my_strnncoll(charset_info,(uchar*)name,len,
(uchar*)commands[i].name,len) &&
!commands[i].name[len] &&
(!end || (end && commands[i].takes_params))) ||
!name && commands[i].cmd_char == cmd_char))
- return (&commands[i]);
+ {
+ DBUG_PRINT("exit",("found command: %s", commands[i].name));
+ DBUG_RETURN(&commands[i]);
+ }
}
- return ((COMMANDS *) 0);
+ DBUG_RETURN((COMMANDS *) 0);
}
@@ -1140,15 +1187,17 @@ static bool add_line(String &buffer,char *line,char *in_string,
uchar inchar;
char buff[80], *pos, *out;
COMMANDS *com;
+ bool need_space= 0;
+ DBUG_ENTER("add_line");
if (!line[0] && buffer.is_empty())
- return 0;
+ DBUG_RETURN(0);
#ifdef HAVE_READLINE
if (status.add_to_history && line[0] && not_in_history(line))
add_history(line);
#endif
#ifdef USE_MB
- char *strend=line+(uint) strlen(line);
+ char *end_of_line=line+(uint) strlen(line);
#endif
for (pos=out=line ; (inchar= (uchar) *pos) ; pos++)
@@ -1157,18 +1206,18 @@ static bool add_line(String &buffer,char *line,char *in_string,
buffer.is_empty())
continue;
#ifdef USE_MB
- int l;
+ int length;
if (use_mb(charset_info) &&
- (l= my_ismbchar(charset_info, pos, strend)))
+ (length= my_ismbchar(charset_info, pos, end_of_line)))
{
if (!*ml_comment)
{
- while (l--)
+ while (length--)
*out++ = *pos++;
pos--;
}
else
- pos+= l - 1;
+ pos+= length - 1;
continue;
}
#endif
@@ -1189,7 +1238,7 @@ static bool add_line(String &buffer,char *line,char *in_string,
const String tmp(line,(uint) (out-line), charset_info);
buffer.append(tmp);
if ((*com->func)(&buffer,pos-1) > 0)
- return 1; // Quit
+ DBUG_RETURN(1); // Quit
if (com->takes_params)
{
for (pos++ ;
@@ -1207,29 +1256,40 @@ static bool add_line(String &buffer,char *line,char *in_string,
{
sprintf(buff,"Unknown command '\\%c'.",inchar);
if (put_info(buff,INFO_ERROR) > 0)
- return 1;
+ DBUG_RETURN(1);
*out++='\\';
*out++=(char) inchar;
continue;
}
}
-
- else if (!*ml_comment && (*pos == *delimiter &&
- is_prefix(pos + 1, delimiter + 1)) &&
- !*in_string)
+ else if (!*ml_comment && !*in_string &&
+ (*pos == *delimiter && is_prefix(pos + 1, delimiter + 1) ||
+ buffer.length() == 0 && (out - line) >= 9 &&
+ !my_strcasecmp(charset_info, line, "delimiter")))
{
uint old_delimiter_length= delimiter_length;
if (out != line)
buffer.append(line, (uint) (out - line)); // Add this line
if ((com= find_command(buffer.c_ptr(), 0)))
{
+ if (com->func == com_delimiter)
+ {
+ /*
+ Delimiter wants the get rest of the given line as argument to
+ allow one to change ';' to ';;' and back
+ */
+ char *end= strend(pos);
+ buffer.append(pos, (uint) (end - pos));
+ /* Ensure pos will point at \0 after the pos+= below */
+ pos= end - old_delimiter_length + 1;
+ }
if ((*com->func)(&buffer, buffer.c_ptr()) > 0)
- return 1; // Quit
+ DBUG_RETURN(1); // Quit
}
else
{
if (com_go(&buffer, 0) > 0) // < 0 is not fatal
- return 1;
+ DBUG_RETURN(1);
}
buffer.length(0);
out= line;
@@ -1254,6 +1314,7 @@ static bool add_line(String &buffer,char *line,char *in_string,
{
pos++;
*ml_comment= 0;
+ need_space= 1;
}
else
{ // Add found char to buffer
@@ -1263,7 +1324,14 @@ static bool add_line(String &buffer,char *line,char *in_string,
(inchar == '\'' || inchar == '"' || inchar == '`'))
*in_string= (char) inchar;
if (!*ml_comment)
+ {
+ if (need_space && !my_isspace(charset_info, (char)inchar))
+ {
+ *out++= ' ';
+ need_space= 0;
+ }
*out++= (char) inchar;
+ }
}
}
if (out != line || !buffer.is_empty())
@@ -1273,9 +1341,9 @@ static bool add_line(String &buffer,char *line,char *in_string,
if (buffer.length() + length >= buffer.alloced_length())
buffer.realloc(buffer.length()+length+IO_SIZE);
if (!(*ml_comment) && buffer.append(line,length))
- return 1;
+ DBUG_RETURN(1);
}
- return 0;
+ DBUG_RETURN(0);
}
/*****************************************************************
@@ -1681,11 +1749,12 @@ int mysql_real_query_for_lazy(const char *buf, int length)
{
for (uint retry=0;; retry++)
{
+ int error;
if (!mysql_real_query(&mysql,buf,length))
return 0;
- int error= put_error(&mysql);
+ error= put_error(&mysql);
if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR || retry > 1 ||
- !opt_reconnect)
+ !opt_reconnect)
return error;
if (reconnect())
return error;
@@ -1967,7 +2036,7 @@ com_go(String *buffer,char *line __attribute__((unused)))
time_buff[0]=0;
if (result)
{
- if (!mysql_num_rows(result) && ! quick)
+ if (!mysql_num_rows(result) && ! quick && !info_flag)
{
strmov(buff, "Empty set");
}
@@ -2024,6 +2093,13 @@ com_go(String *buffer,char *line __attribute__((unused)))
if (err >= 1)
error= put_error(&mysql);
+ if (show_warnings == 1 && warnings >= 1) /* Show warnings if any */
+ {
+ init_pager();
+ print_warnings();
+ end_pager();
+ }
+
if (!error && !status.batch &&
(mysql.server_status & SERVER_STATUS_DB_DROPPED))
get_current_db();
@@ -2083,6 +2159,7 @@ static void end_tee()
return;
}
+
static int
com_ego(String *buffer,char *line)
{
@@ -2094,18 +2171,95 @@ com_ego(String *buffer,char *line)
return result;
}
+
+static const char *fieldtype2str(enum enum_field_types type)
+{
+ switch (type) {
+ case FIELD_TYPE_BIT: return "BIT";
+ case FIELD_TYPE_BLOB: return "BLOB";
+ case FIELD_TYPE_DATE: return "DATE";
+ case FIELD_TYPE_DATETIME: return "DATETIME";
+ case FIELD_TYPE_NEWDECIMAL: return "NEWDECIMAL";
+ case FIELD_TYPE_DECIMAL: return "DECIMAL";
+ case FIELD_TYPE_DOUBLE: return "DOUBLE";
+ case FIELD_TYPE_ENUM: return "ENUM";
+ case FIELD_TYPE_FLOAT: return "FLOAT";
+ case FIELD_TYPE_GEOMETRY: return "GEOMETRY";
+ case FIELD_TYPE_INT24: return "INT24";
+ case FIELD_TYPE_LONG: return "LONG";
+ case FIELD_TYPE_LONGLONG: return "LONGLONG";
+ case FIELD_TYPE_LONG_BLOB: return "LONG_BLOB";
+ case FIELD_TYPE_MEDIUM_BLOB: return "MEDIUM_BLOB";
+ case FIELD_TYPE_NEWDATE: return "NEWDATE";
+ case FIELD_TYPE_NULL: return "NULL";
+ case FIELD_TYPE_SET: return "SET";
+ case FIELD_TYPE_SHORT: return "SHORT";
+ case FIELD_TYPE_STRING: return "STRING";
+ case FIELD_TYPE_TIME: return "TIME";
+ case FIELD_TYPE_TIMESTAMP: return "TIMESTAMP";
+ case FIELD_TYPE_TINY: return "TINY";
+ case FIELD_TYPE_TINY_BLOB: return "TINY_BLOB";
+ case FIELD_TYPE_VAR_STRING: return "VAR_STRING";
+ case FIELD_TYPE_YEAR: return "YEAR";
+ default: return "?-unknown-?";
+ }
+}
+
+static char *fieldflags2str(uint f) {
+ static char buf[1024];
+ char *s=buf;
+ *s=0;
+#define ff2s_check_flag(X) \
+ if (f & X ## _FLAG) { s=strmov(s, # X " "); f &= ~ X ## _FLAG; }
+ ff2s_check_flag(NOT_NULL);
+ ff2s_check_flag(PRI_KEY);
+ ff2s_check_flag(UNIQUE_KEY);
+ ff2s_check_flag(MULTIPLE_KEY);
+ ff2s_check_flag(BLOB);
+ ff2s_check_flag(UNSIGNED);
+ ff2s_check_flag(ZEROFILL);
+ ff2s_check_flag(BINARY);
+ ff2s_check_flag(ENUM);
+ ff2s_check_flag(AUTO_INCREMENT);
+ ff2s_check_flag(TIMESTAMP);
+ ff2s_check_flag(SET);
+ ff2s_check_flag(NO_DEFAULT_VALUE);
+ ff2s_check_flag(NUM);
+ ff2s_check_flag(PART_KEY);
+ ff2s_check_flag(GROUP);
+ ff2s_check_flag(UNIQUE);
+ ff2s_check_flag(BINCMP);
+#undef ff2s_check_flag
+ if (f)
+ sprintf(s, " unknows=0x%04x", f);
+ return buf;
+}
+
static void
print_field_types(MYSQL_RES *result)
{
- MYSQL_FIELD *field;
+ MYSQL_FIELD *field;
+ uint i=0;
+
while ((field = mysql_fetch_field(result)))
{
- tee_fprintf(PAGER,"Catalog: '%s'\nDatabase: '%s'\nTable: '%s'\nName: '%s'\nType: %d\nLength: %ld\nMax length: %ld\nIs_null: %d\nFlags: %u\nDecimals: %u\n\n",
- field->catalog, field->db, field->table, field->name,
- (int) field->type,
- field->length, field->max_length,
- !IS_NOT_NULL(field->flags),
- field->flags, field->decimals);
+ tee_fprintf(PAGER, "Field %3u: `%s`\n"
+ "Catalog: `%s`\n"
+ "Database: `%s`\n"
+ "Table: `%s`\n"
+ "Org_table: `%s`\n"
+ "Type: %s\n"
+ "Collation: %s (%u)\n"
+ "Length: %lu\n"
+ "Max_length: %lu\n"
+ "Decimals: %u\n"
+ "Flags: %s\n\n",
+ ++i,
+ field->name, field->catalog, field->db, field->table,
+ field->org_table, fieldtype2str(field->type),
+ get_charset_name(field->charsetnr), field->charsetnr,
+ field->length, field->max_length, field->decimals,
+ fieldflags2str(field->flags));
}
tee_puts("", PAGER);
}
@@ -2118,11 +2272,15 @@ print_table_data(MYSQL_RES *result)
MYSQL_ROW cur;
MYSQL_FIELD *field;
bool *num_flag;
+ bool *not_null_flag;
num_flag=(bool*) my_alloca(sizeof(bool)*mysql_num_fields(result));
+ not_null_flag=(bool*) my_alloca(sizeof(bool)*mysql_num_fields(result));
if (info_flag)
{
print_field_types(result);
+ if (!mysql_num_rows(result))
+ return;
mysql_field_seek(result,0);
}
separator.copy("+",1,charset_info);
@@ -2135,7 +2293,7 @@ print_table_data(MYSQL_RES *result)
length=max(length,field->max_length);
if (length < 4 && !IS_NOT_NULL(field->flags))
length=4; // Room for "NULL"
- field->max_length=length+1;
+ field->max_length=length;
separator.fill(separator.length()+length+2,'-');
separator.append('+');
}
@@ -2147,10 +2305,11 @@ print_table_data(MYSQL_RES *result)
(void) tee_fputs("|", PAGER);
for (uint off=0; (field = mysql_fetch_field(result)) ; off++)
{
- tee_fprintf(PAGER, " %-*s|",(int) min(field->max_length,
+ tee_fprintf(PAGER, " %-*s |",(int) min(field->max_length,
MAX_COLUMN_LENGTH),
field->name);
num_flag[off]= IS_NUM(field->type);
+ not_null_flag[off]= IS_NOT_NULL(field->flags);
}
(void) tee_fputs("\n", PAGER);
tee_puts((char*) separator.ptr(), PAGER);
@@ -2159,35 +2318,93 @@ print_table_data(MYSQL_RES *result)
while ((cur= mysql_fetch_row(result)))
{
ulong *lengths= mysql_fetch_lengths(result);
- (void) tee_fputs("|", PAGER);
+ (void) tee_fputs("| ", PAGER);
mysql_field_seek(result, 0);
for (uint off= 0; off < mysql_num_fields(result); off++)
{
- const char *str= cur[off] ? cur[off] : "NULL";
- field= mysql_fetch_field(result);
- uint maxlength= field->max_length;
- if (maxlength > MAX_COLUMN_LENGTH)
+ const char *buffer;
+ uint data_length;
+ uint field_max_length;
+ bool right_justified;
+ uint visible_length;
+ uint extra_padding;
+
+ /* If this column may have a null value, use "NULL" for empty. */
+ if (! not_null_flag[off] && (cur[off] == NULL))
{
- tee_fputs(str, PAGER);
- tee_fputs(" |", PAGER);
+ buffer= "NULL";
+ data_length= 4;
+ }
+ else
+ {
+ buffer= cur[off];
+ data_length= (uint) lengths[off];
}
+
+ field= mysql_fetch_field(result);
+ field_max_length= field->max_length;
+
+ /*
+ How many text cells on the screen will this string span? If it contains
+ multibyte characters, then the number of characters we occupy on screen
+ will be fewer than the number of bytes we occupy in memory.
+
+ We need to find how much screen real-estate we will occupy to know how
+ many extra padding-characters we should send with the printing function.
+ */
+ visible_length= charset_info->cset->numcells(charset_info, buffer, buffer + data_length);
+ extra_padding= data_length - visible_length;
+
+ if (field_max_length > MAX_COLUMN_LENGTH)
+ tee_print_sized_data(buffer, data_length, MAX_COLUMN_LENGTH+extra_padding, FALSE);
else
{
- uint currlength= (uint) lengths[off];
- uint numcells= charset_info->cset->numcells(charset_info,
- str, str + currlength);
- tee_fprintf(PAGER, num_flag[off] ? "%*s |" : " %-*s|",
- maxlength + currlength - numcells, str);
+ if (num_flag[off] != 0) /* if it is numeric, we right-justify it */
+ tee_print_sized_data(buffer, data_length, field_max_length+extra_padding, TRUE);
+ else
+ tee_print_sized_data(buffer, data_length, field_max_length+extra_padding, FALSE);
}
+ tee_fputs(" | ", PAGER);
}
(void) tee_fputs("\n", PAGER);
}
tee_puts((char*) separator.ptr(), PAGER);
my_afree((gptr) num_flag);
+ my_afree((gptr) not_null_flag);
}
static void
+tee_print_sized_data(const char *data, unsigned int data_length, unsigned int total_bytes_to_send, bool right_justified)
+{
+ /*
+ For '\0's print ASCII spaces instead, as '\0' is eaten by (at
+ least my) console driver, and that messes up the pretty table
+ grid. (The \0 is also the reason we can't use fprintf() .)
+ */
+ unsigned int i;
+ const char *p;
+
+ if (right_justified)
+ for (i= data_length; i < total_bytes_to_send; i++)
+ tee_putc((int)' ', PAGER);
+
+ for (i= 0, p= data; i < data_length; i+= 1, p+= 1)
+ {
+ if (*p == '\0')
+ tee_putc((int)' ', PAGER);
+ else
+ tee_putc((int)*p, PAGER);
+ }
+
+ if (! right_justified)
+ for (i= data_length; i < total_bytes_to_send; i++)
+ tee_putc((int)' ', PAGER);
+}
+
+
+
+static void
print_table_data_html(MYSQL_RES *result)
{
MYSQL_ROW cur;
@@ -2240,13 +2457,11 @@ print_table_data_xml(MYSQL_RES *result)
(void) tee_fputs("\n <row>\n", PAGER);
for (uint i=0; i < mysql_num_fields(result); i++)
{
- tee_fprintf(PAGER, "\t<%s>", (fields[i].name ?
- (fields[i].name[0] ? fields[i].name :
- " &nbsp; ") : "NULL"));
+ tee_fprintf(PAGER, "\t<field name=\"");
+ xmlencode_print(fields[i].name, (uint) strlen(fields[i].name));
+ tee_fprintf(PAGER, "\">");
xmlencode_print(cur[i], lengths[i]);
- tee_fprintf(PAGER, "</%s>\n", (fields[i].name ?
- (fields[i].name[0] ? fields[i].name :
- " &nbsp; ") : "NULL"));
+ tee_fprintf(PAGER, "</field>\n");
}
(void) tee_fputs(" </row>\n", PAGER);
}
@@ -2285,13 +2500,41 @@ print_table_data_vertically(MYSQL_RES *result)
}
-static const char
-*array_value(const char **array, char key)
+/* print_warnings should be called right after executing a statement */
+
+static void print_warnings()
+{
+ const char *query;
+ MYSQL_RES *result;
+ MYSQL_ROW cur;
+ my_ulonglong num_rows;
+
+ /* Get the warnings */
+ query= "show warnings";
+ mysql_real_query_for_lazy(query, strlen(query));
+ mysql_store_result_for_lazy(&result);
+
+ /* Bail out when no warnings */
+ if (!(num_rows= mysql_num_rows(result)))
+ {
+ mysql_free_result(result);
+ return;
+ }
+
+ /* Print the warnings */
+ while ((cur= mysql_fetch_row(result)))
+ {
+ tee_fprintf(PAGER, "%s (Code %s): %s\n", cur[0], cur[1], cur[2]);
+ }
+ mysql_free_result(result);
+}
+
+
+static const char *array_value(const char **array, char key)
{
- int x;
- for (x= 0; array[x]; x+= 2)
- if (*array[x] == key)
- return array[x + 1];
+ for (; *array; array+= 2)
+ if (**array == key)
+ return array[1];
return 0;
}
@@ -2695,7 +2938,7 @@ static int com_source(String *buffer, char *line)
status.line_buff=line_buff;
status.file_name=source_name;
glob_buffer.length(0); // Empty command buffer
- error=read_lines(0); // Read lines from file
+ error= read_and_execute(false);
status=old_status; // Continue as before
my_fclose(sql_file,MYF(0));
batch_readline_end(line_buff);
@@ -2729,6 +2972,7 @@ static int
com_use(String *buffer __attribute__((unused)), char *line)
{
char *tmp, buff[FN_REFLEN + 1];
+ int select_db;
bzero(buff, sizeof(buff));
strmov(buff, line);
@@ -2748,38 +2992,73 @@ com_use(String *buffer __attribute__((unused)), char *line)
if (!current_db || cmp_database(charset_info, current_db,tmp))
{
if (one_database)
+ {
skip_updates= 1;
+ select_db= 0; // don't do mysql_select_db()
+ }
else
- {
- /*
- reconnect once if connection is down or if connection was found to
- be down during query
- */
- if (!connected && reconnect())
+ select_db= 2; // do mysql_select_db() and build_completion_hash()
+ }
+ else
+ {
+ /*
+ USE to the current db specified.
+ We do need to send mysql_select_db() to make server
+ update database level privileges, which might
+ change since last USE (see bug#10979).
+ For performance purposes, we'll skip rebuilding of completion hash.
+ */
+ skip_updates= 0;
+ select_db= 1; // do only mysql_select_db(), without completion
+ }
+
+ if (select_db)
+ {
+ /*
+ reconnect once if connection is down or if connection was found to
+ be down during query
+ */
+ if (!connected && reconnect())
return opt_reconnect ? -1 : 1; // Fatal error
- if (mysql_select_db(&mysql,tmp))
- {
- if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR)
- return put_error(&mysql);
+ if (mysql_select_db(&mysql,tmp))
+ {
+ if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR)
+ return put_error(&mysql);
- if (reconnect())
+ if (reconnect())
return opt_reconnect ? -1 : 1; // Fatal error
- if (mysql_select_db(&mysql,tmp))
- return put_error(&mysql);
- }
- my_free(current_db,MYF(MY_ALLOW_ZERO_PTR));
- current_db=my_strdup(tmp,MYF(MY_WME));
+ if (mysql_select_db(&mysql,tmp))
+ return put_error(&mysql);
+ }
+ my_free(current_db,MYF(MY_ALLOW_ZERO_PTR));
+ current_db=my_strdup(tmp,MYF(MY_WME));
#ifdef HAVE_READLINE
+ if (select_db > 1)
build_completion_hash(rehash, 1);
#endif
- }
}
- else
- skip_updates= 0;
+
put_info("Database changed",INFO_INFO);
return 0;
}
+static int
+com_warnings(String *buffer __attribute__((unused)),
+ char *line __attribute__((unused)))
+{
+ show_warnings = 1;
+ put_info("Show warnings enabled.",INFO_INFO);
+ return 0;
+}
+
+static int
+com_nowarnings(String *buffer __attribute__((unused)),
+ char *line __attribute__((unused)))
+{
+ show_warnings = 0;
+ put_info("Show warnings disabled.",INFO_INFO);
+ return 0;
+}
/*
Gets argument from a command on the command line. If get_next_arg is
@@ -2869,6 +3148,8 @@ sql_real_connect(char *host,char *database,char *user,char *password,
if (opt_use_ssl)
mysql_ssl_set(&mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
opt_ssl_capath, opt_ssl_cipher);
+ mysql_options(&mysql,MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
+ (char*)&opt_ssl_verify_server_cert);
#endif
if (opt_protocol)
mysql_options(&mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
@@ -2903,6 +3184,8 @@ sql_real_connect(char *host,char *database,char *user,char *password,
connected=1;
#ifndef EMBEDDED_LIBRARY
mysql.reconnect=info_flag ? 1 : 0; // We want to know if this happens
+#else
+ mysql.reconnect= 1;
#endif
#ifdef HAVE_READLINE
build_completion_hash(rehash, 1);
@@ -2977,10 +3260,9 @@ com_status(String *buffer __attribute__((unused)),
mysql_free_result(result);
}
#ifdef HAVE_OPENSSL
- if (mysql.net.vio && mysql.net.vio->ssl_arg &&
- SSL_get_cipher((SSL*) mysql.net.vio->ssl_arg))
+ if ((status= mysql_get_ssl_cipher(&mysql)))
tee_fprintf(stdout, "SSL:\t\t\tCipher in use is %s\n",
- SSL_get_cipher((SSL*) mysql.net.vio->ssl_arg));
+ status);
else
#endif /* HAVE_OPENSSL */
tee_puts("SSL:\t\t\tNot in use", stdout);
@@ -3126,7 +3408,7 @@ put_info(const char *str,INFO_TYPE info_type, uint error, const char *sqlstate)
if (info_type == INFO_ERROR)
{
if (!opt_nobeep)
- putchar('\007'); /* This should make a bell */
+ putchar('\a'); /* This should make a bell */
vidattr(A_STANDOUT);
if (error)
{