mysql.cc 107 KB
Newer Older
1
/* Copyright (C) 2000-2003 MySQL AB
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3 4
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
5
   the Free Software Foundation; version 2 of the License.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6

bk@work.mysql.com's avatar
bk@work.mysql.com committed
7 8 9 10
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
11

bk@work.mysql.com's avatar
bk@work.mysql.com committed
12 13 14 15 16 17 18 19 20
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/* mysql command tool
 * Commands compatible with mSQL by David J. Hughes
 *
 * Written by:
 *   Michael 'Monty' Widenius
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
21 22 23 24 25 26
 *   Andi Gutmans  <andi@zend.com>
 *   Zeev Suraski  <zeev@zend.com>
 *   Jani Tolonen  <jani@mysql.com>
 *   Matt Wagner   <matt@mysql.com>
 *   Jeremy Cole   <jcole@mysql.com>
 *   Tonu Samuel   <tonu@mysql.com>
27
 *   Harrison Fisk <harrison@mysql.com>
bk@work.mysql.com's avatar
bk@work.mysql.com committed
28 29 30
 *
 **/

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
31
#include "client_priv.h"
bk@work.mysql.com's avatar
bk@work.mysql.com committed
32
#include <m_ctype.h>
33
#include <stdarg.h>
bk@work.mysql.com's avatar
bk@work.mysql.com committed
34 35
#include <my_dir.h>
#ifndef __GNU_LIBRARY__
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
36
#define __GNU_LIBRARY__		      // Skip warnings in getopt.h
bk@work.mysql.com's avatar
bk@work.mysql.com committed
37 38 39
#endif
#include "my_readline.h"
#include <signal.h>
tonu@x153.internalnet's avatar
tonu@x153.internalnet committed
40
#include <violite.h>
bk@work.mysql.com's avatar
bk@work.mysql.com committed
41

42 43 44 45
#if defined(USE_LIBEDIT_INTERFACE) && defined(HAVE_LOCALE_H)
#include <locale.h>
#endif

46
const char *VER= "14.13";
47

monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
48 49 50
/* Don't try to make a nice table if the data is too big */
#define MAX_COLUMN_LENGTH	     1024

51 52 53
/* Buffer to hold 'version' and 'version_comment' */
#define MAX_SERVER_VERSION_LENGTH     128

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
54
gptr sql_alloc(unsigned size);	     // Don't use mysqld alloc for these
bk@work.mysql.com's avatar
bk@work.mysql.com committed
55 56 57 58 59 60 61 62 63 64 65 66 67 68
void sql_element_free(void *ptr);
#include "sql_string.h"

extern "C" {
#if defined(HAVE_CURSES_H) && defined(HAVE_TERM_H)
#include <curses.h>
#include <term.h>
#else
#if defined(HAVE_TERMIOS_H)
#include <termios.h>
#include <unistd.h>
#elif defined(HAVE_TERMBITS_H)
#include <termbits.h>
#elif defined(HAVE_ASM_TERMBITS_H) && (!defined __GLIBC__ || !(__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ > 0))
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
69
#include <asm/termbits.h>		// Standard linux
bk@work.mysql.com's avatar
bk@work.mysql.com committed
70 71 72 73 74 75 76 77
#endif
#undef VOID
#if defined(HAVE_TERMCAP_H)
#include <termcap.h>
#else
#ifdef HAVE_CURSES_H
#include <curses.h>
#endif
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
78
#undef SYSV				// hack to avoid syntax error
bk@work.mysql.com's avatar
bk@work.mysql.com committed
79 80 81 82 83 84
#ifdef HAVE_TERM_H
#include <term.h>
#endif
#endif
#endif

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
85
#undef bcmp				// Fix problem with new readline
86
#if defined( __WIN__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
87
#include <conio.h>
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
88
#elif !defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
89 90 91 92 93 94 95 96
#include <readline/readline.h>
#define HAVE_READLINE
#endif
  //int vidattr(long unsigned int attrs);	// Was missing in sun curses
}

#if !defined(HAVE_VIDATTR)
#undef vidattr
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
97
#define vidattr(A) {}			// Can't get this to work
bk@work.mysql.com's avatar
bk@work.mysql.com committed
98 99
#endif

100
#ifdef FN_NO_CASE_SENCE
101
#define cmp_database(cs,A,B) my_strcasecmp((cs), (A), (B))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
102
#else
103
#define cmp_database(cs,A,B) strcmp((A),(B))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
104 105
#endif

106
#if !defined( __WIN__) && !defined(__NETWARE__) && !defined(THREAD)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
107 108 109
#define USE_POPEN
#endif

bk@work.mysql.com's avatar
bk@work.mysql.com committed
110 111
#include "completion_hash.h"

jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
112
#define PROMPT_CHAR '\\'
113
#define DEFAULT_DELIMITER ";"
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
114

bk@work.mysql.com's avatar
bk@work.mysql.com committed
115 116 117 118 119 120 121 122 123 124 125
typedef struct st_status
{
  int exit_status;
  ulong query_start_line;
  char *file_name;
  LINE_BUFFER *line_buff;
  bool batch,add_to_history;
} STATUS;


static HashTable ht;
126
static char **defaults_argv;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
127 128 129 130 131

enum enum_info_type { INFO_INFO,INFO_ERROR,INFO_RESULT};
typedef enum enum_info_type INFO_TYPE;

static MYSQL mysql;			/* The connection */
132 133
static my_bool info_flag=0,ignore_errors=0,wait_flag=0,quick=0,
               connected=0,opt_raw_data=0,unbuffered=0,output_tables=0,
134
	       opt_rehash=1,skip_updates=0,safe_updates=0,one_database=0,
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
135
	       opt_compress=0, using_opt_local_infile=0,
136 137
	       vertical=0, line_numbers=1, column_names=1,opt_html=0,
               opt_xml=0,opt_nopager=1, opt_outfile=0, named_cmds= 0,
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
138
	       tty_password= 0, opt_nobeep=0, opt_reconnect=1,
139
	       default_charset_used= 0, opt_secure_auth= 0,
140
               default_pager_set= 0, opt_sigint_ignore= 0,
141
               show_warnings= 0, executing_query= 0, interrupted_query= 0;
142
static my_bool column_types_flag;
143
static ulong opt_max_allowed_packet, opt_net_buffer_length;
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
144
static uint verbose=0,opt_silent=0,opt_mysql_port=0, opt_local_infile=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
145 146 147
static my_string opt_mysql_unix_port=0;
static int connect_flag=CLIENT_INTERACTIVE;
static char *current_host,*current_db,*current_user=0,*opt_password=0,
148
            *current_prompt=0, *delimiter_str= 0,
149
            *default_charset= (char*) MYSQL_DEFAULT_CHARSET_NAME;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
150
static char *histfile;
151
static char *histfile_tmp;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
152
static String glob_buffer,old_buffer;
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
153 154
static String processed_prompt;
static char *full_username=0,*part_username=0,*default_prompt=0;
155
static int wait_time = 5;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
156
static STATUS status;
157
static ulong select_limit,max_join_size,opt_connect_timeout=0;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
158
static char mysql_charsets_dir[FN_REFLEN+1];
159
static const char *xmlmeta[] = {
160 161
  "&", "&amp;",
  "<", "&lt;",
162 163
  ">", "&gt;",
  "\"", "&quot;",
164 165
  0, 0
};
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
166 167 168 169 170 171 172 173
static const char *day_names[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
static const char *month_names[]={"Jan","Feb","Mar","Apr","May","Jun","Jul",
			    "Aug","Sep","Oct","Nov","Dec"};
static char default_pager[FN_REFLEN];
static char pager[FN_REFLEN], outfile[FN_REFLEN];
static FILE *PAGER, *OUTFILE;
static MEM_ROOT hash_mem_root;
static uint prompt_counter;
174 175
static char delimiter[16]= DEFAULT_DELIMITER;
static uint delimiter_length= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
176

177 178 179 180
#ifdef HAVE_SMEM
static char *shared_memory_base_name=0;
#endif
static uint opt_protocol=0;
181
static CHARSET_INFO *charset_info= &my_charset_latin1;
182

bk@work.mysql.com's avatar
bk@work.mysql.com committed
183 184 185 186
#include "sslopt-vars.h"

const char *default_dbug_option="d:t:o,/tmp/mysql.trace";

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
187 188 189
void tee_fprintf(FILE *file, const char *fmt, ...);
void tee_fputs(const char *s, FILE *file);
void tee_puts(const char *s, FILE *file);
190
void tee_putc(int c, FILE *file);
191
static void tee_print_sized_data(const char *, unsigned int, unsigned int, bool);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
192 193 194 195
/* The names of functions that actually do the manipulation. */
static int get_options(int argc,char **argv);
static int com_quit(String *str,char*),
	   com_go(String *str,char*), com_ego(String *str,char*),
196
	   com_print(String *str,char*),
bk@work.mysql.com's avatar
bk@work.mysql.com committed
197 198 199
	   com_help(String *str,char*), com_clear(String *str,char*),
	   com_connect(String *str,char*), com_status(String *str,char*),
	   com_use(String *str,char*), com_source(String *str, char*),
200
	   com_rehash(String *str, char*), com_tee(String *str, char*),
201
           com_notee(String *str, char*), com_charset(String *str,char*),
202 203
           com_prompt(String *str, char*), com_delimiter(String *str, char*),
     com_warnings(String *str, char*), com_nowarnings(String *str, char*);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
204

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
205
#ifdef USE_POPEN
206
static int com_nopager(String *str, char*), com_pager(String *str, char*),
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
207
           com_edit(String *str,char*), com_shell(String *str, char *);
208 209
#endif

210
static int read_and_execute(bool interactive);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
211 212
static int sql_connect(char *host,char *database,char *user,char *password,
		       uint silent);
213
static const char *server_version_string(MYSQL *mysql);
214 215 216
static int put_info(const char *str,INFO_TYPE info,uint error=0,
		    const char *sql_state=0);
static int put_error(MYSQL *mysql);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
217
static void safe_put_field(const char *pos,ulong length);
218
static void xmlencode_print(const char *src, uint length);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
219 220
static void init_pager();
static void end_pager();
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
221
static void init_tee(const char *);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
222
static void end_tee();
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
223
static const char* construct_prompt();
224
static char *get_arg(char *line, my_bool get_next_arg);
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
225 226
static void init_username();
static void add_int_to_prompt(int toadd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
227 228 229 230 231 232 233 234 235 236 237 238 239

/* A structure which contains information on the commands this program
   can understand. */

typedef struct {
  const char *name;		/* User printable name of the function. */
  char cmd_char;		/* msql command character */
  int (*func)(String *str,char *); /* Function to call to do the job. */
  bool takes_params;		/* Max parameters for command */
  const char *doc;		/* Documentation for this function.  */
} COMMANDS;

static COMMANDS commands[] = {
240
  { "?",      '?', com_help,   1, "Synonym for `help'." },
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
241
  { "clear",  'c', com_clear,  0, "Clear command."},
242
  { "connect",'r', com_connect,1,
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
243
    "Reconnect to the server. Optional arguments are db and host." },
244
  { "delimiter", 'd', com_delimiter,    1,
245
    "Set statement delimiter. NOTE: Takes the rest of the line as new delimiter." },
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
246
#ifdef USE_POPEN
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
247
  { "edit",   'e', com_edit,   0, "Edit command with $EDITOR."},
248
#endif
249
  { "ego",    'G', com_ego,    0,
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
250 251 252
    "Send command to mysql server, display result vertically."},
  { "exit",   'q', com_quit,   0, "Exit mysql. Same as quit."},
  { "go",     'g', com_go,     0, "Send command to mysql server." },
253
  { "help",   'h', com_help,   1, "Display this help." },
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
254
#ifdef USE_POPEN
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
255 256 257
  { "nopager",'n', com_nopager,0, "Disable pager, print to stdout." },
#endif
  { "notee",  't', com_notee,  0, "Don't write into outfile." },
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
258
#ifdef USE_POPEN
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
259 260 261 262
  { "pager",  'P', com_pager,  1, 
    "Set PAGER [to_pager]. Print the query results via PAGER." },
#endif
  { "print",  'p', com_print,  0, "Print current command." },
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
263
  { "prompt", 'R', com_prompt, 1, "Change your mysql prompt."},
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
264 265
  { "quit",   'q', com_quit,   0, "Quit mysql." },
  { "rehash", '#', com_rehash, 0, "Rebuild completion hash." },
bk@work.mysql.com's avatar
bk@work.mysql.com committed
266
  { "source", '.', com_source, 1,
paul@snake-hub.snake.net's avatar
paul@snake-hub.snake.net committed
267
    "Execute an SQL script file. Takes a file name as an argument."},
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
268
  { "status", 's', com_status, 0, "Get status information from the server."},
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
269
#ifdef USE_POPEN
270 271
  { "system", '!', com_shell,  1, "Execute a system shell command."},
#endif
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
272 273
  { "tee",    'T', com_tee,    1, 
    "Set outfile [to_outfile]. Append everything into given outfile." },
274
  { "use",    'u', com_use,    1,
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
275
    "Use another database. Takes database name as argument." },
276 277
  { "charset",    'C', com_charset,    1,
    "Switch to another charset. Might be needed for processing binlog with multi-byte charsets." },
278
  { "warnings", 'W', com_warnings,  0,
279
    "Show warnings after every statement." },
280
  { "nowarning", 'w', com_nowarnings, 0,
281
    "Don't show warnings after every statement." },
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
282
  /* Get bash-like expansion for some commands */
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
  { "create table",     0, 0, 0, ""},
  { "create database",  0, 0, 0, ""},
  { "drop",             0, 0, 0, ""},
  { "select",           0, 0, 0, ""},
  { "insert",           0, 0, 0, ""},
  { "replace",          0, 0, 0, ""},
  { "update",           0, 0, 0, ""},
  { "delete",           0, 0, 0, ""},
  { "explain",          0, 0, 0, ""},
  { "show databases",   0, 0, 0, ""},
  { "show fields from", 0, 0, 0, ""},
  { "show keys from",   0, 0, 0, ""},
  { "show tables",      0, 0, 0, ""},
  { "load data from",   0, 0, 0, ""},
  { "alter table",      0, 0, 0, ""},
  { "set option",       0, 0, 0, ""},
  { "lock tables",      0, 0, 0, ""},
  { "unlock tables",    0, 0, 0, ""},
  { (char *)NULL,       0, 0, 0, ""}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
302 303 304
};

static const char *load_default_groups[]= { "mysql","client",0 };
305 306
static const char *server_default_groups[]=
{ "server", "embedded", "mysql_SERVER", 0 };
bk@work.mysql.com's avatar
bk@work.mysql.com committed
307 308

#ifdef HAVE_READLINE
309 310 311 312
/*
 HIST_ENTRY is defined for libedit, but not for the real readline
 Need to redefine it for real readline to find it
*/
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
313
#if !defined(HAVE_HIST_ENTRY)
314 315 316 317 318 319
typedef struct _hist_entry {
  const char      *line;
  const char      *data;
} HIST_ENTRY; 
#endif

320 321 322
extern "C" int add_history(const char *command); /* From readline directory */
extern "C" int read_history(const char *command);
extern "C" int write_history(const char *command);
323 324 325
extern "C" HIST_ENTRY *history_get(int num);
extern "C" int history_length;
static int not_in_history(const char *line);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
326
static void initialize_readline (char *name);
327
static void fix_history(String *final_command);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
328 329
#endif

330
static COMMANDS *find_command(char *name,char cmd_name);
331 332
static bool add_line(String &buffer,char *line,char *in_string,
                     bool *ml_comment);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
333 334 335
static void remove_cntrl(String &buffer);
static void print_table_data(MYSQL_RES *result);
static void print_table_data_html(MYSQL_RES *result);
336
static void print_table_data_xml(MYSQL_RES *result);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
337 338
static void print_tab_data(MYSQL_RES *result);
static void print_table_data_vertically(MYSQL_RES *result);
339
static void print_warnings(void);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
340 341 342 343 344
static ulong start_timer(void);
static void end_timer(ulong start_time,char *buff);
static void mysql_end_timer(ulong start_time,char *buff);
static void nice_time(double sec,char *buff,bool part_second);
static sig_handler mysql_end(int sig);
345
static sig_handler handle_sigint(int sig);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
346 347 348 349

int main(int argc,char *argv[])
{
  char buff[80];
monty@mishka.local's avatar
monty@mishka.local committed
350 351 352
  char *defaults, *extra_defaults, *group_suffix;
  char *emb_argv[4];
  int emb_argc;
353

monty@mishka.local's avatar
monty@mishka.local committed
354 355 356 357 358
  /* 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;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
359 360 361 362

  MY_INIT(argv[0]);
  DBUG_ENTER("main");
  DBUG_PROCESS(argv[0]);
363 364
  
  delimiter_str= delimiter;
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
365 366 367 368 369 370
  default_prompt = my_strdup(getenv("MYSQL_PS1") ? 
			     getenv("MYSQL_PS1") : 
			     "mysql> ",MYF(MY_WME));
  current_prompt = my_strdup(default_prompt,MYF(MY_WME));
  prompt_counter=0;

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
371 372
  outfile[0]=0;			// no (default) outfile
  strmov(pager, "stdout");	// the default, if --pager wasn't given
373 374
  {
    char *tmp=getenv("PAGER");
375 376 377 378 379
    if (tmp && strlen(tmp))
    {
      default_pager_set= 1;
      strmov(default_pager, tmp);
    }
380
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
381 382 383 384 385 386 387 388
  if (!isatty(0) || !isatty(1))
  {
    status.batch=1; opt_silent=1;
    ignore_errors=0;
  }
  else
    status.add_to_history=1;
  status.exit_status=1;
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403

  {
    /* 
     The file descriptor-layer may be out-of-sync with the file-number layer,
     so we make sure that "stdout" is really open.  If its file is closed then
     explicitly close the FD layer. 
    */
    int stdout_fileno_copy;
    stdout_fileno_copy= dup(fileno(stdout)); /* Okay if fileno fails. */
    if (stdout_fileno_copy == -1)
      fclose(stdout);
    else
      close(stdout_fileno_copy);             /* Clean up dup(). */
  }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
404
  load_defaults("my",load_default_groups,&argc,&argv);
405
  defaults_argv=argv;
406
  if (get_options(argc, (char **) argv))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
407
  {
408
    free_defaults(defaults_argv);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
409 410 411 412
    my_end(0);
    exit(1);
  }
  if (status.batch && !status.line_buff &&
413
      !(status.line_buff=batch_readline_init(opt_max_allowed_packet+512,stdin)))
414 415
  {
    free_defaults(defaults_argv);
416
    my_end(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
417
    exit(1);
418
  }
419
  if (mysql_server_init(emb_argc, emb_argv, (char**) server_default_groups))
420 421
  {
    free_defaults(defaults_argv);
422
    my_end(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
423
    exit(1);
424
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
425
  glob_buffer.realloc(512);
426 427
  completion_hash_init(&ht, 128);
  init_alloc_root(&hash_mem_root, 16384, 0);
428
  bzero((char*) &mysql, sizeof(mysql));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
429 430 431
  if (sql_connect(current_host,current_db,current_user,opt_password,
		  opt_silent))
  {
432 433 434
    quick=1;					// Avoid history
    status.exit_status=1;
    mysql_end(-1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
435 436 437
  }
  if (!status.batch)
    ignore_errors=1;				// Don't abort monitor
438 439 440 441

  if (opt_sigint_ignore)
    signal(SIGINT, SIG_IGN);
  else
442
    signal(SIGINT, handle_sigint);              // Catch SIGINT to clean up
443
  signal(SIGQUIT, mysql_end);			// Catch SIGQUIT to clean up
bk@work.mysql.com's avatar
bk@work.mysql.com committed
444 445 446 447

  put_info("Welcome to the MySQL monitor.  Commands end with ; or \\g.",
	   INFO_INFO);
  sprintf((char*) glob_buffer.ptr(),
448 449
	  "Your MySQL connection id is %lu\nServer version: %s\n",
	  mysql_thread_id(&mysql), server_version_string(&mysql));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
450 451 452
  put_info((char*) glob_buffer.ptr(),INFO_INFO);

#ifdef HAVE_READLINE
453
  initialize_readline((char*) my_progname);
454
  if (!status.batch && !quick && !opt_html && !opt_xml)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
455
  {
456
    /* read-history from file, default ~/.mysql_history*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
457 458 459 460
    if (getenv("MYSQL_HISTFILE"))
      histfile=my_strdup(getenv("MYSQL_HISTFILE"),MYF(MY_WME));
    else if (getenv("HOME"))
    {
461 462
      histfile=(char*) my_malloc((uint) strlen(getenv("HOME"))
				 + (uint) strlen("/.mysql_history")+2,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
463 464 465
				 MYF(MY_WME));
      if (histfile)
	sprintf(histfile,"%s/.mysql_history",getenv("HOME"));
466 467 468 469 470 471 472 473
      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;
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
474 475 476 477
    }
    if (histfile)
    {
      if (verbose)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
478
	tee_fprintf(stdout, "Reading history-file %s\n",histfile);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
479
      read_history(histfile);
480 481 482 483 484 485 486
      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);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
487 488 489
    }
  }
#endif
490
  sprintf(buff, "%s",
491
#ifndef NOT_YET
492
	  "Type 'help;' or '\\h' for help. Type '\\c' to clear the buffer.\n");
493
#else
494
	  "Type 'help [[%]function name[%]]' to get help on usage of function.\n");
495
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
496
  put_info(buff,INFO_INFO);
497
  status.exit_status= read_and_execute(!status.batch);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
498 499
  if (opt_outfile)
    end_tee();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
500 501 502 503 504 505 506 507
  mysql_end(0);
#ifndef _lint
  DBUG_RETURN(0);				// Keep compiler happy
#endif
}

sig_handler mysql_end(int sig)
{
508
  mysql_close(&mysql);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
509
#ifdef HAVE_READLINE
510
  if (!status.batch && !quick && !opt_html && !opt_xml && histfile)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
511 512 513
  {
    /* write-history */
    if (verbose)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
514
      tee_fprintf(stdout, "Writing history-file %s\n",histfile);
515 516
    if (!write_history(histfile_tmp))
      my_rename(histfile_tmp, histfile, MYF(MY_WME));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
517 518 519
  }
  batch_readline_end(status.line_buff);
  completion_hash_free(&ht);
520 521
  free_root(&hash_mem_root,MYF(0));

bk@work.mysql.com's avatar
bk@work.mysql.com committed
522
#endif
523 524
  if (sig >= 0)
    put_info(sig ? "Aborted" : "Bye", INFO_RESULT);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
525 526
  glob_buffer.free();
  old_buffer.free();
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
527
  processed_prompt.free();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
528 529 530
  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));
531
  my_free(histfile_tmp,MYF(MY_ALLOW_ZERO_PTR));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
532 533 534
  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));
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
535 536 537
  my_free(full_username,MYF(MY_ALLOW_ZERO_PTR));
  my_free(part_username,MYF(MY_ALLOW_ZERO_PTR));
  my_free(default_prompt,MYF(MY_ALLOW_ZERO_PTR));
538 539 540
#ifdef HAVE_SMEM
  my_free(shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR));
#endif
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
541
  my_free(current_prompt,MYF(MY_ALLOW_ZERO_PTR));
542
  mysql_server_end();
543
  free_defaults(defaults_argv);
544
  my_end(info_flag ? MY_CHECK_ERROR : 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
545 546 547
  exit(status.exit_status);
}

548

549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
/*
  This function handles sigint calls
  If query is in process, kill query
  no query in process, terminate like previous behavior
 */
sig_handler handle_sigint(int sig)
{
  char kill_buffer[40];
  MYSQL *kill_mysql= NULL;

  /* terminate if no query being executed, or we already tried interrupting */
  if (!executing_query || interrupted_query)
    mysql_end(sig);

  kill_mysql= mysql_init(kill_mysql);
  if (!mysql_real_connect(kill_mysql,current_host, current_user, opt_password,
                          "", opt_mysql_port, opt_mysql_unix_port,0))
    mysql_end(sig);

  /* kill_buffer is always big enough because max length of %lu is 15 */
  sprintf(kill_buffer, "KILL /*!50000 QUERY */ %lu", mysql_thread_id(&mysql));
  mysql_real_query(kill_mysql, kill_buffer, strlen(kill_buffer));
  mysql_close(kill_mysql);
  tee_fprintf(stdout, "Query aborted by Ctrl+C\n");

  interrupted_query= 1;
}


578
static struct my_option my_long_options[] =
bk@work.mysql.com's avatar
bk@work.mysql.com committed
579
{
580 581
  {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
   0, 0, 0, 0, 0},
582 583
  {"help", 'I', "Synonym for -?", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
   0, 0, 0, 0, 0},
584
#ifdef __NETWARE__
585
  {"autoclose", OPT_AUTO_CLOSE, "Auto close the screen on exit for Netware.",
586 587
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
#endif
588 589
  {"auto-rehash", OPT_AUTO_REHASH,
   "Enable automatic rehashing. One doesn't need to use 'rehash' to get table and field completion, but startup and reconnecting may take a longer time. Disable with --disable-auto-rehash.",
590 591
   (gptr*) &opt_rehash, (gptr*) &opt_rehash, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0,
   0, 0},
592
  {"no-auto-rehash", 'A',
593
   "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.",
594 595
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"batch", 'B',
serg@serg.mylan's avatar
serg@serg.mylan committed
596
   "Don't use history file. Disable interactive behavior. (Enables --silent)", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
597 598 599
  {"character-sets-dir", OPT_CHARSETS_DIR,
   "Directory where character sets are.", (gptr*) &charsets_dir,
   (gptr*) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
600 601 602
  {"column-type-info", OPT_COLUMN_TYPES, "Display column type information.",
   (gptr*) &column_types_flag, (gptr*) &column_types_flag,
   0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
603 604 605
  {"compress", 'C', "Use compression in server/client protocol.",
   (gptr*) &opt_compress, (gptr*) &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
   0, 0, 0},
606

607 608
#ifdef DBUG_OFF
  {"debug", '#', "This is a non-debug version. Catch this and exit",
serg@serg.mylan's avatar
serg@serg.mylan committed
609
   0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
610 611
#else
  {"debug", '#', "Output debug log", (gptr*) &default_dbug_option,
612 613
   (gptr*) &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
#endif
614 615
  {"debug-info", 'T', "Print some debug info at exit.", (gptr*) &info_flag,
   (gptr*) &info_flag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
616
  {"database", 'D', "Database to use.", (gptr*) &current_db,
617
   (gptr*) &current_db, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
618 619 620
  {"default-character-set", OPT_DEFAULT_CHARSET,
   "Set the default character set.", (gptr*) &default_charset,
   (gptr*) &default_charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
621 622
  {"delimiter", OPT_DELIMITER, "Delimiter to be used.", (gptr*) &delimiter_str,
   (gptr*) &delimiter_str, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
serg@serg.mylan's avatar
serg@serg.mylan committed
623
  {"execute", 'e', "Execute command and quit. (Disables --force and history file)", 0,
624 625 626 627 628 629 630 631
   0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"vertical", 'E', "Print the output of a query (rows) vertically.",
   (gptr*) &vertical, (gptr*) &vertical, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
   0},
  {"force", 'f', "Continue even if we get an sql error.",
   (gptr*) &ignore_errors, (gptr*) &ignore_errors, 0, GET_BOOL, NO_ARG, 0, 0,
   0, 0, 0, 0},
  {"named-commands", 'G',
632
   "Enable named commands. Named commands mean this program's internal commands; see mysql> help . When enabled, the named commands can be used from any line of the query, otherwise only from the first line, before an enter. Disable with --disable-named-commands. This option is disabled by default.",
633 634
   (gptr*) &named_cmds, (gptr*) &named_cmds, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
   0, 0},
635 636 637
  {"no-named-commands", 'g',
   "Named commands are disabled. Use \\* form only, or use named commands only in the beginning of a line ending with a semicolon (;) Since version 10.9 the client now starts with this option ENABLED by default! Disable with '-G'. Long format commands still work from the first line. WARNING: option deprecated; use --disable-named-commands instead.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
638
  {"ignore-spaces", 'i', "Ignore space after function names.", 0, 0, 0,
639
   GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
640
  {"local-infile", OPT_LOCAL_INFILE, "Enable/disable LOAD DATA LOCAL INFILE.",
641 642
   (gptr*) &opt_local_infile,
   (gptr*) &opt_local_infile, 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
643 644
  {"no-beep", 'b', "Turn off beep on error.", (gptr*) &opt_nobeep,
   (gptr*) &opt_nobeep, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, 
645
  {"host", 'h', "Connect to host.", (gptr*) &current_host,
646
   (gptr*) &current_host, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
647 648 649 650 651 652
  {"html", 'H', "Produce HTML output.", (gptr*) &opt_html, (gptr*) &opt_html,
   0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"xml", 'X', "Produce XML output", (gptr*) &opt_xml, (gptr*) &opt_xml, 0,
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"line-numbers", OPT_LINE_NUMBERS, "Write line numbers for errors.",
   (gptr*) &line_numbers, (gptr*) &line_numbers, 0, GET_BOOL,
653
   NO_ARG, 1, 0, 0, 0, 0, 0},  
654
  {"skip-line-numbers", 'L', "Don't write line number for errors. WARNING: -L is deprecated, use long version of this option instead.", 0, 0, 0, GET_NO_ARG,
655 656 657 658 659
   NO_ARG, 0, 0, 0, 0, 0, 0},
  {"unbuffered", 'n', "Flush buffer after each query.", (gptr*) &unbuffered,
   (gptr*) &unbuffered, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"column-names", OPT_COLUMN_NAMES, "Write column names in results.",
   (gptr*) &column_names, (gptr*) &column_names, 0, GET_BOOL,
660
   NO_ARG, 1, 0, 0, 0, 0, 0},
661
  {"skip-column-names", 'N',
662
   "Don't write column names in results. WARNING: -N is deprecated, use long version of this options instead.",
663 664
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"set-variable", 'O',
665
   "Change the value of a variable. Please note that this option is deprecated; you can set variables directly with --variable-name=value.",
666
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
667 668 669
  {"sigint-ignore", OPT_SIGINT_IGNORE, "Ignore SIGINT (CTRL-C)",
   (gptr*) &opt_sigint_ignore,  (gptr*) &opt_sigint_ignore, 0, GET_BOOL,
   NO_ARG, 0, 0, 0, 0, 0, 0},
670 671 672
  {"one-database", 'o',
   "Only update the default database. This is useful for skipping updates to other database in the update log.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
673
#ifdef USE_POPEN
674
  {"pager", OPT_PAGER,
675
   "Pager to use to display results. If you don't supply an option the default pager is taken from your ENV variable PAGER. Valid pagers are less, more, cat [> filename], etc. See interactive help (\\h) also. This option does not work in batch mode. Disable with --disable-pager. This option is disabled by default.",
676
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
677 678 679
  {"no-pager", OPT_NOPAGER,
   "Disable pager and print to stdout. See interactive help (\\h) also. WARNING: option deprecated; use --disable-pager instead.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
680
#endif
681 682 683
  {"password", 'p',
   "Password to use when connecting to server. If password is not given it's asked from the tty.",
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
bk@work.mysql.com's avatar
bk@work.mysql.com committed
684
#ifdef __WIN__
685 686
  {"pipe", 'W', "Use named pipes to connect to server.", 0, 0, 0, GET_NO_ARG,
   NO_ARG, 0, 0, 0, 0, 0, 0},
bk@work.mysql.com's avatar
bk@work.mysql.com committed
687
#endif
688
  {"port", 'P', "Port number to use for connection.", (gptr*) &opt_mysql_port,
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
689
   (gptr*) &opt_mysql_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0,
690
   0},
691
  {"prompt", OPT_PROMPT, "Set the mysql prompt to this value.",
692
   (gptr*) &current_prompt, (gptr*) &current_prompt, 0, GET_STR_ALLOC,
693
   REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
694
  {"protocol", OPT_MYSQL_PROTOCOL, "The protocol of connection (tcp,socket,pipe,memory).",
695
   0, 0, 0, GET_STR,  REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
696
  {"quick", 'q',
697
   "Don't cache result, print it row by row. This may slow down the server if the output is suspended. Doesn't use history file.",
698
   (gptr*) &quick, (gptr*) &quick, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
699
  {"raw", 'r', "Write fields without conversion. Used with --batch.",
700 701
   (gptr*) &opt_raw_data, (gptr*) &opt_raw_data, 0, GET_BOOL, NO_ARG, 0, 0, 0,
   0, 0, 0},
702 703
  {"reconnect", OPT_RECONNECT, "Reconnect if the connection is lost. Disable with --disable-reconnect. This option is enabled by default.", 
   (gptr*) &opt_reconnect, (gptr*) &opt_reconnect, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
serg@serg.mylan's avatar
serg@serg.mylan committed
704
  {"silent", 's', "Be more silent. Print results with a tab as separator, each row on new line.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0,
705
   0, 0},
706
#ifdef HAVE_SMEM
707
  {"shared-memory-base-name", OPT_SHARED_MEMORY_BASE_NAME,
708
   "Base name of shared memory.", (gptr*) &shared_memory_base_name, (gptr*) &shared_memory_base_name, 
709 710
   0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#endif
serg@mysql.com's avatar
serg@mysql.com committed
711
  {"socket", 'S', "Socket file to use for connection.",
712
   (gptr*) &opt_mysql_unix_port, (gptr*) &opt_mysql_unix_port, 0, GET_STR_ALLOC,
713
   REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
bk@work.mysql.com's avatar
bk@work.mysql.com committed
714
#include "sslopt-longopts.h"
715 716 717
  {"table", 't', "Output in table format.", (gptr*) &output_tables,
   (gptr*) &output_tables, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"tee", OPT_TEE,
718
   "Append everything into outfile. See interactive help (\\h) also. Does not work in batch mode. Disable with --disable-tee. This option is disabled by default.",
719
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
720 721
  {"no-tee", OPT_NOTEE, "Disable outfile. See interactive help (\\h) also. WARNING: option deprecated; use --disable-tee instead", 0, 0, 0, GET_NO_ARG,
   NO_ARG, 0, 0, 0, 0, 0, 0},
bk@work.mysql.com's avatar
bk@work.mysql.com committed
722
#ifndef DONT_ALLOW_USER_CHANGE
723
  {"user", 'u', "User for login if not current user.", (gptr*) &current_user,
724
   (gptr*) &current_user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
bk@work.mysql.com's avatar
bk@work.mysql.com committed
725
#endif
726
  {"safe-updates", 'U', "Only allow UPDATE and DELETE that uses keys.",
727
   (gptr*) &safe_updates, (gptr*) &safe_updates, 0, GET_BOOL, NO_ARG, 0, 0,
728
   0, 0, 0, 0},
729
  {"i-am-a-dummy", 'U', "Synonym for option --safe-updates, -U.",
730
   (gptr*) &safe_updates, (gptr*) &safe_updates, 0, GET_BOOL, NO_ARG, 0, 0,
731
   0, 0, 0, 0},
732
  {"verbose", 'v', "Write more. (-v -v -v gives the table output format).", 0,
733 734 735 736 737
   0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"version", 'V', "Output version information and exit.", 0, 0, 0,
   GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"wait", 'w', "Wait and retry if connection is down.", 0, 0, 0, GET_NO_ARG,
   NO_ARG, 0, 0, 0, 0, 0, 0},
738 739 740
  {"connect_timeout", OPT_CONNECT_TIMEOUT,
   "Number of seconds before connection timeout.",
   (gptr*) &opt_connect_timeout,
741
   (gptr*) &opt_connect_timeout, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 3600*12, 0,
742
   0, 1},
743 744
  {"max_allowed_packet", OPT_MAX_ALLOWED_PACKET,
   "Max packet length to send to, or receive from server",
745
   (gptr*) &opt_max_allowed_packet, (gptr*) &opt_max_allowed_packet, 0, GET_ULONG,
746 747
   REQUIRED_ARG, 16 *1024L*1024L, 4096, (longlong) 2*1024L*1024L*1024L,
   MALLOC_OVERHEAD, 1024, 0},
748 749
  {"net_buffer_length", OPT_NET_BUFFER_LENGTH,
   "Buffer for TCP/IP and socket communication",
750
   (gptr*) &opt_net_buffer_length, (gptr*) &opt_net_buffer_length, 0, GET_ULONG,
751
   REQUIRED_ARG, 16384, 1024, 512*1024*1024L, MALLOC_OVERHEAD, 1024, 0},
752 753 754
  {"select_limit", OPT_SELECT_LIMIT,
   "Automatic limit for SELECT when using --safe-updates",
   (gptr*) &select_limit,
755
   (gptr*) &select_limit, 0, GET_ULONG, REQUIRED_ARG, 1000L, 1, ~0L, 0, 1, 0},
756 757 758
  {"max_join_size", OPT_MAX_JOIN_SIZE,
   "Automatic limit for rows in a join when using --safe-updates",
   (gptr*) &max_join_size,
759
   (gptr*) &max_join_size, 0, GET_ULONG, REQUIRED_ARG, 1000000L, 1, ~0L, 0, 1,
760
   0},
761 762 763
  {"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},
764 765 766
  {"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},
767
  { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
768 769 770 771 772
};


static void usage(int version)
{
monty@mysql.com's avatar
monty@mysql.com committed
773 774 775 776
  /* Divert all help information on NetWare to logger screen. */
#ifdef __NETWARE__
#define printf	consoleprintf
#endif
777 778 779 780 781 782 783

#if defined(USE_LIBEDIT_INTERFACE)
  const char* readline= "";
#else
  const char* readline= "readline";
#endif

784
#ifdef HAVE_READLINE
785 786 787
  printf("%s  Ver %s Distrib %s, for %s (%s) using %s %s\n",
	 my_progname, VER, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE,
         readline, rl_library_version);
788
#else
789
  printf("%s  Ver %s Distrib %s, for %s (%s)\n", my_progname, VER,
790 791 792
	MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE);
#endif

bk@work.mysql.com's avatar
bk@work.mysql.com committed
793 794
  if (version)
    return;
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
795
  printf("\
796
Copyright (C) 2002 MySQL AB\n\
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
797 798
This software comes with ABSOLUTELY NO WARRANTY. This is free software,\n\
and you are welcome to modify and redistribute it under the GPL license\n");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
799
  printf("Usage: %s [OPTIONS] [database]\n", my_progname);
800
  my_print_help(my_long_options);
801
  print_defaults("my", load_default_groups);
802
  my_print_variables(my_long_options);
monty@mysql.com's avatar
monty@mysql.com committed
803 804 805 806
  NETWARE_SET_SCREEN_MODE(1);
#ifdef __NETWARE__
#undef printf
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
807 808
}

monty@mysql.com's avatar
monty@mysql.com committed
809

810 811 812
static my_bool
get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
	       char *argument)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
813
{
814
  switch(optid) {
815 816 817 818 819
#ifdef __NETWARE__
  case OPT_AUTO_CLOSE:
    setscreenmode(SCR_AUTOCLOSE_ON_EXIT);
    break;
#endif
820 821 822 823
  case OPT_CHARSETS_DIR:
    strmov(mysql_charsets_dir, argument);
    charsets_dir = mysql_charsets_dir;
    break;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
824 825 826
  case  OPT_DEFAULT_CHARSET:
    default_charset_used= 1;
    break;
827
  case OPT_DELIMITER:
828 829
    if (argument == disabled_my_option) 
    {
830
      strmov(delimiter, DEFAULT_DELIMITER);
831 832 833 834 835 836 837 838 839 840 841 842 843 844
    }
    else 
    {
      /* Check that delimiter does not contain a backslash */
      if (!strstr(argument, "\\")) 
      {
        strmake(delimiter, argument, sizeof(delimiter) - 1);
      }
      else 
      {
        put_info("DELIMITER cannot contain a backslash character", INFO_ERROR);
        return 0;
      } 
    }
845
    delimiter_length= (uint)strlen(delimiter);
846 847
    delimiter_str= delimiter;
    break;
848 849 850 851 852 853
  case OPT_LOCAL_INFILE:
    using_opt_local_infile=1;
    break;
  case OPT_TEE:
    if (argument == disabled_my_option)
    {
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
854 855
      if (opt_outfile)
	end_tee();
856 857
    }
    else
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
858
      init_tee(argument);
859 860
    break;
  case OPT_NOTEE:
861
    printf("WARNING: option deprecated; use --disable-tee instead.\n");
862 863 864 865
    if (opt_outfile)
      end_tee();
    break;
  case OPT_PAGER:
866 867
    if (argument == disabled_my_option)
      opt_nopager= 1;
868
    else
869 870
    {
      opt_nopager= 0;
871 872 873
      if (argument && strlen(argument))
      {
	default_pager_set= 1;
874
	strmov(pager, argument);
875 876 877
	strmov(default_pager, pager);
      }
      else if (default_pager_set)
878
	strmov(pager, default_pager);
879 880
      else
	opt_nopager= 1;
881
    }
882 883
    break;
  case OPT_NOPAGER:
884
    printf("WARNING: option deprecated; use --disable-pager instead.\n");
885
    opt_nopager= 1;
886
    break;
887 888
  case OPT_MYSQL_PROTOCOL:
  {
889
    if ((opt_protocol= find_type(argument, &sql_protocol_typelib,0)) <= 0)
890 891 892 893 894 895
    {
      fprintf(stderr, "Unknown option to protocol: %s\n", argument);
      exit(1);
    }
    break;
  }
896
  break;
897
  case 'A':
898
    opt_rehash= 0;
899 900 901 902 903 904 905
    break;
  case 'N':
    column_names= 0;
    break;
  case 'e':
    status.batch= 1;
    status.add_to_history= 0;
serg@serg.mylan's avatar
serg@serg.mylan committed
906 907 908
    if (!status.line_buff)
      ignore_errors= 0;                         // do it for the first -e only
    if (!(status.line_buff= batch_readline_command(status.line_buff, argument)))
909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927
      return 1;
    break;
  case 'o':
    if (argument == disabled_my_option)
      one_database= 0;
    else
      one_database= skip_updates= 1;
    break;
  case 'p':
    if (argument == disabled_my_option)
      argument= (char*) "";			// Don't require password
    if (argument)
    {
      char *start= argument;
      my_free(opt_password, MYF(MY_ALLOW_ZERO_PTR));
      opt_password= my_strdup(argument, MYF(MY_FAE));
      while (*argument) *argument++= 'x';		// Destroy argument
      if (*start)
	start[1]=0 ;
928
      tty_password= 0;
929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949
    }
    else
      tty_password= 1;
    break;
  case '#':
    DBUG_PUSH(argument ? argument : default_dbug_option);
    info_flag= 1;
    break;
  case 's':
    if (argument == disabled_my_option)
      opt_silent= 0;
    else
      opt_silent++;
    break;
  case 'v':
    if (argument == disabled_my_option)
      verbose= 0;
    else
      verbose++;
    break;
  case 'B':
serg@serg.mylan's avatar
serg@serg.mylan committed
950 951
    status.batch= 1;
    status.add_to_history= 0;
952
    set_if_bigger(opt_silent,1);                         // more silent
953 954
    break;
  case 'W':
bk@work.mysql.com's avatar
bk@work.mysql.com committed
955
#ifdef __WIN__
956
    opt_protocol = MYSQL_PROTOCOL_PIPE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
957
#endif
958
    break;
959
#include <sslopt-case.h>
960 961 962 963 964 965 966
  case 'V':
    usage(1);
    exit(0);
  case 'I':
  case '?':
    usage(0);
    exit(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
967
  }
968 969 970 971 972 973 974 975
  return 0;
}


static int get_options(int argc, char **argv)
{
  char *tmp, *pagpoint;
  int ho_error;
976
  MYSQL_PARAMETERS *mysql_params= mysql_get_parameters();
977 978 979 980 981 982 983 984 985 986 987 988 989 990 991

  tmp= (char *) getenv("MYSQL_HOST");
  if (tmp)
    current_host= my_strdup(tmp, MYF(MY_WME));

  pagpoint= getenv("PAGER");
  if (!((char*) (pagpoint)))
  {
    strmov(pager, "stdout");
    opt_nopager= 1;
  }
  else
    strmov(pager, pagpoint);
  strmov(default_pager, pager);

992 993 994
  opt_max_allowed_packet= *mysql_params->p_max_allowed_packet;
  opt_net_buffer_length= *mysql_params->p_net_buffer_length;

995
  if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option)))
996
    exit(ho_error);
997

998 999 1000
  *mysql_params->p_max_allowed_packet= opt_max_allowed_packet;
  *mysql_params->p_net_buffer_length= opt_net_buffer_length;

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1001 1002 1003 1004
  if (status.batch) /* disable pager and outfile in this case */
  {
    strmov(default_pager, "stdout");
    strmov(pager, "stdout");
1005
    opt_nopager= 1;
1006
    default_pager_set= 0;
1007
    opt_outfile= 0;
1008
    opt_reconnect= 0;
1009
    connect_flag= 0; /* Not in interactive mode */
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1010
  }
serg@mysql.com's avatar
serg@mysql.com committed
1011
  
1012 1013
  if (strcmp(default_charset, charset_info->csname) &&
      !(charset_info= get_charset_by_csname(default_charset, 
1014
					    MY_CS_PRIMARY, MYF(MY_WME))))
1015
    exit(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1016 1017 1018 1019 1020 1021 1022
  if (argc > 1)
  {
    usage(0);
    exit(1);
  }
  if (argc == 1)
  {
1023
    skip_updates= 0;
1024 1025
    my_free(current_db, MYF(MY_ALLOW_ZERO_PTR));
    current_db= my_strdup(*argv, MYF(MY_WME));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1026 1027
  }
  if (tty_password)
1028
    opt_password= get_tty_password(NullS);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1029 1030 1031
  return(0);
}

1032
static int read_and_execute(bool interactive)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1033
{
1034
#if defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1035
  char linebuffer[254];
1036
  String buffer;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1037
#endif
1038 1039 1040 1041 1042
#if defined(__WIN__)
  String tmpbuf;
  String buffer;
#endif

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1043 1044 1045
  char	*line;
  char	in_string=0;
  ulong line_number=0;
1046
  bool ml_comment= 0;  
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1047 1048
  COMMANDS *com;
  status.exit_status=1;
1049
  
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1050 1051
  for (;;)
  {
1052
    if (!interactive)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1053 1054 1055 1056 1057 1058 1059
    {
      line=batch_readline(status.line_buff);
      line_number++;
      if (!glob_buffer.length())
	status.query_start_line=line_number;
    }
    else
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1060
    {
1061 1062
      char *prompt= (char*) (ml_comment ? "   /*> " :
                             glob_buffer.is_empty() ?  construct_prompt() :
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1063 1064
			     !in_string ? "    -> " :
			     in_string == '\'' ?
1065 1066 1067
			     "    '> " : (in_string == '`' ?
			     "    `> " :
			     "    \"> "));
1068 1069
      if (opt_outfile && glob_buffer.is_empty())
	fflush(OUTFILE);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1070

1071
#if defined( __WIN__) || defined(__NETWARE__)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1072
      tee_fputs(prompt, stdout);
1073
#if defined(__NETWARE__)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1074 1075
      line=fgets(linebuffer, sizeof(linebuffer)-1, stdin);
      /* Remove the '\n' */
monty@mysql.com's avatar
monty@mysql.com committed
1076
      if (line)
1077
      {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1078
        char *p = strrchr(line, '\n');
1079 1080
        if (p != NULL)
          *p = '\0';
1081
      }
1082
#else defined(__WIN__)
1083 1084
      if (!tmpbuf.is_alloced())
        tmpbuf.alloc(65535);
1085
      tmpbuf.length(0);
1086 1087 1088 1089
      buffer.length(0);
      unsigned long clen;
      do
      {
1090
	line= my_cgets((char*)tmpbuf.ptr(), tmpbuf.alloced_length()-1, &clen);
1091 1092 1093 1094 1095
        buffer.append(line, clen);
        /* 
           if we got buffer fully filled than there is a chance that
           something else is still in console input buffer
        */
1096
      } while (tmpbuf.alloced_length() <= clen);
1097
      line= buffer.c_ptr();
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1098 1099 1100 1101 1102
#endif /* __NETWARE__ */
#else
      if (opt_outfile)
	fputs(prompt, OUTFILE);
      line= readline(prompt);
1103
#endif /* defined( __WIN__) || defined(__NETWARE__) */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1104

monty@mysql.com's avatar
monty@mysql.com committed
1105 1106 1107 1108 1109
      /*
        When Ctrl+d or Ctrl+z is pressed, the line may be NULL on some OS
        which may cause coredump.
      */
      if (opt_outfile && line)
1110
	fprintf(OUTFILE, "%s\n", line);
1111
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1112 1113 1114 1115 1116 1117 1118 1119
    if (!line)					// End of file
    {
      status.exit_status=0;
      break;
    }
    if (!in_string && (line[0] == '#' ||
		       (line[0] == '-' && line[1] == '-') ||
		       line[0] == 0))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1120
      continue;					// Skip comment lines
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1121

1122 1123 1124 1125
    /*
      Check if line is a mysql command line
      (We want to allow help, print and clear anywhere at line start
    */
1126
    if ((named_cmds || glob_buffer.is_empty())
1127
	&& !ml_comment && !in_string && (com=find_command(line,0)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1128 1129 1130 1131 1132 1133
    {
      if ((*com->func)(&glob_buffer,line) > 0)
	break;
      if (glob_buffer.is_empty())		// If buffer was emptied
	in_string=0;
#ifdef HAVE_READLINE
1134
      if (interactive && status.add_to_history && not_in_history(line))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1135 1136 1137 1138
	add_history(line);
#endif
      continue;
    }
1139
    if (add_line(glob_buffer,line,&in_string,&ml_comment))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1140 1141 1142 1143
      break;
  }
  /* if in batch mode, send last query even if it doesn't end with \g or go */

1144
  if (!interactive && !status.exit_status)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1145 1146 1147 1148 1149 1150 1151 1152 1153
  {
    remove_cntrl(glob_buffer);
    if (!glob_buffer.is_empty())
    {
      status.exit_status=1;
      if (com_go(&glob_buffer,line) <= 0)
	status.exit_status=0;
    }
  }
1154

1155
#if defined( __WIN__) || defined(__NETWARE__)
1156 1157
  buffer.free();
#endif
1158 1159 1160 1161
#if defined( __WIN__)
  tmpbuf.free();
#endif

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1162 1163 1164 1165
  return status.exit_status;
}


1166
static COMMANDS *find_command(char *name,char cmd_char)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1167 1168 1169
{
  uint len;
  char *end;
1170 1171
  DBUG_ENTER("find_command");
  DBUG_PRINT("enter",("name: '%s'  char: %d", name ? name : "NULL", cmd_char));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1172 1173 1174 1175 1176 1177 1178 1179

  if (!name)
  {
    len=0;
    end=0;
  }
  else
  {
1180
    while (my_isspace(charset_info,*name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1181
      name++;
1182
    /*
1183 1184 1185
      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()
1186 1187
    */
    if (strstr(name, "\\g") || (strstr(name, delimiter) &&
1188 1189 1190 1191 1192
                                !(strlen(name) >= 9 &&
                                  !my_strnncoll(charset_info,
                                                (uchar*) name, 9,
                                                (const uchar*) "delimiter",
                                                9))))
1193
      DBUG_RETURN((COMMANDS *) 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1194 1195 1196
    if ((end=strcont(name," \t")))
    {
      len=(uint) (end - name);
1197
      while (my_isspace(charset_info,*end))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1198 1199 1200 1201 1202
	end++;
      if (!*end)
	end=0;					// no arguments to function
    }
    else
1203
      len=(uint) strlen(name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1204 1205 1206 1207 1208
  }

  for (uint i= 0; commands[i].name; i++)
  {
    if (commands[i].func &&
1209
	((name &&
1210 1211
	  !my_strnncoll(charset_info,(uchar*)name,len,
				     (uchar*)commands[i].name,len) &&
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1212 1213 1214
	  !commands[i].name[len] &&
	  (!end || (end && commands[i].takes_params))) ||
	 !name && commands[i].cmd_char == cmd_char))
1215 1216 1217 1218
    {
      DBUG_PRINT("exit",("found command: %s", commands[i].name));
      DBUG_RETURN(&commands[i]);
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1219
  }
1220
  DBUG_RETURN((COMMANDS *) 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1221 1222 1223
}


1224 1225
static bool add_line(String &buffer,char *line,char *in_string,
                     bool *ml_comment)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1226 1227
{
  uchar inchar;
1228
  char buff[80], *pos, *out;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1229
  COMMANDS *com;
antony@ltantony.mysql.com's avatar
antony@ltantony.mysql.com committed
1230
  bool need_space= 0;
1231
  DBUG_ENTER("add_line");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1232 1233

  if (!line[0] && buffer.is_empty())
1234
    DBUG_RETURN(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1235
#ifdef HAVE_READLINE
1236
  if (status.add_to_history && line[0] && not_in_history(line))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1237 1238 1239
    add_history(line);
#endif
#ifdef USE_MB
1240
  char *end_of_line=line+(uint) strlen(line);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1241 1242 1243 1244
#endif

  for (pos=out=line ; (inchar= (uchar) *pos) ; pos++)
  {
1245
    if (my_isspace(charset_info,inchar) && out == line && 
1246
        buffer.is_empty())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1247 1248
      continue;
#ifdef USE_MB
1249
    int length;
1250
    if (use_mb(charset_info) &&
bar@mysql.com's avatar
bar@mysql.com committed
1251
        (length= my_ismbchar(charset_info, pos, end_of_line)))
1252
    {
1253 1254
      if (!*ml_comment)
      {
bar@mysql.com's avatar
bar@mysql.com committed
1255
        while (length--)
1256 1257 1258 1259
          *out++ = *pos++;
        pos--;
      }
      else
bar@mysql.com's avatar
bar@mysql.com committed
1260
        pos+= length - 1;
1261
      continue;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1262 1263
    }
#endif
1264 1265
    if (!*ml_comment && inchar == '\\' &&
        !(mysql.server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES))
1266 1267 1268
    {
      // Found possbile one character command like \c

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1269 1270
      if (!(inchar = (uchar) *++pos))
	break;				// readline adds one '\'
1271
      if (*in_string || inchar == 'N')	// \N is short for NULL
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1272 1273 1274 1275 1276 1277 1278
      {					// Don't allow commands in string
	*out++='\\';
	*out++= (char) inchar;
	continue;
      }
      if ((com=find_command(NullS,(char) inchar)))
      {
1279
	const String tmp(line,(uint) (out-line), charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1280 1281
	buffer.append(tmp);
	if ((*com->func)(&buffer,pos-1) > 0)
1282
	  DBUG_RETURN(1);                       // Quit
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1283 1284
	if (com->takes_params)
	{
1285 1286 1287 1288
	  for (pos++ ;
	       *pos && (*pos != *delimiter ||
			!is_prefix(pos + 1, delimiter + 1)) ; pos++)
	    ;	// Remove parameters
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1289 1290
	  if (!*pos)
	    pos--;
1291 1292
	  else 
	    pos+= delimiter_length - 1; // Point at last delim char
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1293 1294 1295 1296 1297 1298 1299
	}
	out=line;
      }
      else
      {
	sprintf(buff,"Unknown command '\\%c'.",inchar);
	if (put_info(buff,INFO_ERROR) > 0)
1300
	  DBUG_RETURN(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1301 1302 1303 1304 1305
	*out++='\\';
	*out++=(char) inchar;
	continue;
      }
    }
1306 1307 1308 1309
    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")))
1310 1311
    {					
      uint old_delimiter_length= delimiter_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1312
      if (out != line)
1313 1314
	buffer.append(line, (uint) (out - line));	// Add this line
      if ((com= find_command(buffer.c_ptr(), 0)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1315
      {
1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326
        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;
        }
1327
	if ((*com->func)(&buffer, buffer.c_ptr()) > 0)
1328
	  DBUG_RETURN(1);                       // Quit
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1329 1330 1331
      }
      else
      {
serg@serg.mylan's avatar
serg@serg.mylan committed
1332
	if (com_go(&buffer, 0) > 0)             // < 0 is not fatal
1333
	  DBUG_RETURN(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1334 1335
      }
      buffer.length(0);
1336 1337
      out= line;
      pos+= old_delimiter_length - 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1338
    }
1339 1340
    else if (!*ml_comment && (!*in_string && (inchar == '#' ||
			      inchar == '-' && pos[1] == '-' &&
1341
			      my_isspace(charset_info,pos[2]))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1342
      break;					// comment to end of line
1343 1344
    else if (!*in_string && inchar == '/' && *(pos+1) == '*' &&
	     *(pos+2) != '!')
1345 1346 1347 1348 1349 1350 1351 1352 1353
    {
      pos++;
      *ml_comment= 1;
      if (out != line)
      {
        buffer.append(line,(uint) (out-line));
        out=line;
      }
    }
1354
    else if (*ml_comment && inchar == '*' && *(pos + 1) == '/')
1355 1356 1357
    {
      pos++;
      *ml_comment= 0;
antony@ltantony.mysql.com's avatar
antony@ltantony.mysql.com committed
1358
      need_space= 1;
1359
    }      
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1360 1361 1362
    else
    {						// Add found char to buffer
      if (inchar == *in_string)
1363
	*in_string= 0;
1364 1365 1366 1367
      else if (!*ml_comment && !*in_string &&
	       (inchar == '\'' || inchar == '"' || inchar == '`'))
	*in_string= (char) inchar;
      if (!*ml_comment)
antony@ltantony.mysql.com's avatar
antony@ltantony.mysql.com committed
1368 1369 1370 1371 1372 1373
      {
        if (need_space && !my_isspace(charset_info, (char)inchar))
        {
          *out++= ' ';
          need_space= 0;
        }
1374
	*out++= (char) inchar;
antony@ltantony.mysql.com's avatar
antony@ltantony.mysql.com committed
1375
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1376 1377 1378 1379 1380 1381 1382 1383
    }
  }
  if (out != line || !buffer.is_empty())
  {
    *out++='\n';
    uint length=(uint) (out-line);
    if (buffer.length() + length >= buffer.alloced_length())
      buffer.realloc(buffer.length()+length+IO_SIZE);
1384
    if (!(*ml_comment) && buffer.append(line,length))
1385
      DBUG_RETURN(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1386
  }
1387
  DBUG_RETURN(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1388 1389
}

1390 1391 1392
/*****************************************************************
	    Interface to Readline Completion
******************************************************************/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1393 1394 1395

#ifdef HAVE_READLINE

1396 1397
static char *new_command_generator(const char *text, int);
static char **new_mysql_completion (const char *text, int start, int end);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1398

1399 1400 1401 1402 1403
/*
  Tell the GNU Readline library how to complete.  We want to try to complete
  on command names if this is the first word in the line, or on filenames
  if not.
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1404

1405 1406 1407
#if defined(USE_NEW_READLINE_INTERFACE) || defined(USE_LIBEDIT_INTERFACE)
char *no_completion(const char*,int)
#else
1408
char *no_completion()
1409
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1410 1411 1412 1413
{
  return 0;					/* No filename completion */
}

1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481
/*	glues pieces of history back together if in pieces   */
static void fix_history(String *final_command) 
{
  int total_lines = 1;
  char *ptr = final_command->c_ptr();
  String fixed_buffer; 	/* Converted buffer */
  char str_char = '\0';  /* Character if we are in a string or not */
  
  /* find out how many lines we have and remove newlines */
  while (*ptr != '\0') 
  {
    switch (*ptr) {
      /* string character */
    case '"':
    case '\'':
    case '`':
      if (str_char == '\0')	/* open string */
	str_char = *ptr;
      else if (str_char == *ptr)   /* close string */
	str_char = '\0';
      fixed_buffer.append(ptr,1);
      break;
    case '\n':
      /* 
	 not in string, change to space
	 if in string, leave it alone 
      */
      fixed_buffer.append(str_char == '\0' ? " " : "\n");
      total_lines++;
      break;
    case '\\':
      fixed_buffer.append('\\');
      /* need to see if the backslash is escaping anything */
      if (str_char) 
      {
	ptr++;
	/* special characters that need escaping */
	if (*ptr == '\'' || *ptr == '"' || *ptr == '\\')
	  fixed_buffer.append(ptr,1);
	else
	  ptr--;
      }
      break;
      
    default:
      fixed_buffer.append(ptr,1);
    }
    ptr++;
  }
  if (total_lines > 1)			
    add_history(fixed_buffer.ptr());
}

/*	
  returns 0 if line matches the previous history entry
  returns 1 if the line doesn't match the previous history entry
*/
static int not_in_history(const char *line) 
{
  HIST_ENTRY *oldhist = history_get(history_length);
  
  if (oldhist == 0)
    return 1;
  if (strcmp(oldhist->line,line) == 0)
    return 0;
  return 1;
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1482 1483 1484 1485 1486 1487
static void initialize_readline (char *name)
{
  /* Allow conditional parsing of the ~/.inputrc file. */
  rl_readline_name = name;

  /* Tell the completer that we want a crack first. */
1488 1489 1490 1491
#if defined(USE_NEW_READLINE_INTERFACE)
  rl_attempted_completion_function= (rl_completion_func_t*)&new_mysql_completion;
  rl_completion_entry_function= (rl_compentry_func_t*)&no_completion;
#elif defined(USE_LIBEDIT_INTERFACE)
1492 1493 1494
#ifdef HAVE_LOCALE_H
  setlocale(LC_ALL,""); /* so as libedit use isprint */
#endif
1495
  rl_attempted_completion_function= (CPPFunction*)&new_mysql_completion;
1496
  rl_completion_entry_function= &no_completion;
1497
#else
1498
  rl_attempted_completion_function= (CPPFunction*)&new_mysql_completion;
1499
  rl_completion_entry_function= &no_completion;
1500
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1501 1502
}

1503 1504 1505 1506 1507 1508
/*
  Attempt to complete on the contents of TEXT.  START and END show the
  region of TEXT that contains the word to complete.  We can use the
  entire line in case we want to do some simple parsing.  Return the
  array of matches, or NULL if there aren't any.
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1509

1510
static char **new_mysql_completion (const char *text,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1511 1512 1513 1514
				    int start __attribute__((unused)),
				    int end __attribute__((unused)))
{
  if (!status.batch && !quick)
1515
#if defined(USE_NEW_READLINE_INTERFACE)
1516
    return rl_completion_matches(text, new_command_generator);
1517 1518 1519
#else
    return completion_matches((char *)text, (CPFunction *)new_command_generator);
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1520 1521 1522 1523
  else
    return (char**) 0;
}

1524
static char *new_command_generator(const char *text,int state)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1525 1526 1527 1528 1529 1530 1531
{
  static int textlen;
  char *ptr;
  static Bucket *b;
  static entry *e;
  static uint i;

1532
  if (!state)
1533
    textlen=(uint) strlen(text);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1534

1535 1536 1537 1538
  if (textlen>0)
  {						/* lookup in the hash */
    if (!state)
    {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1539 1540
      uint len;

1541
      b = find_all_matches(&ht,text,(uint) strlen(text),&len);
1542
      if (!b)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1543 1544 1545 1546
	return NullS;
      e = b->pData;
    }

1547 1548
    if (e)
    {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1549 1550 1551 1552
      ptr= strdup(e->str);
      e = e->pNext;
      return ptr;
    }
1553 1554 1555
  }
  else
  { /* traverse the entire hash, ugly but works */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1556

1557 1558
    if (!state)
    {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1559
      /* find the first used bucket */
1560 1561 1562 1563
      for (i=0 ; i < ht.nTableSize ; i++)
      {
	if (ht.arBuckets[i])
	{
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1564 1565 1566 1567 1568 1569 1570
	  b = ht.arBuckets[i];
	  e = b->pData;
	  break;
	}
      }
    }
    ptr= NullS;
1571 1572 1573
    while (e && !ptr)
    {					/* find valid entry in bucket */
      if ((uint) strlen(e->str) == b->nKeyLength)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1574 1575 1576
	ptr = strdup(e->str);
      /* find the next used entry */
      e = e->pNext;
1577 1578
      if (!e)
      { /* find the next used bucket */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1579
	b = b->pNext;
1580 1581 1582 1583 1584 1585
	if (!b)
	{
	  for (i++ ; i<ht.nTableSize; i++)
	  {
	    if (ht.arBuckets[i])
	    {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1586 1587 1588 1589 1590 1591
	      b = ht.arBuckets[i];
	      e = b->pData;
	      break;
	    }
	  }
	}
1592 1593
	else
	  e = b->pData;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1594 1595
      }
    }
1596
    if (ptr)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1597 1598 1599 1600 1601 1602 1603 1604
      return ptr;
  }
  return NullS;
}


/* Build up the completion hash */

1605
static void build_completion_hash(bool rehash, bool write_info)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1606 1607
{
  COMMANDS *cmd=commands;
1608 1609
  MYSQL_RES *databases=0,*tables=0;
  MYSQL_RES *fields;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1610 1611 1612 1613 1614 1615 1616
  static char ***field_names= 0;
  MYSQL_ROW database_row,table_row;
  MYSQL_FIELD *sql_field;
  char buf[NAME_LEN*2+2];		 // table name plus field name plus 2
  int i,j,num_fields;
  DBUG_ENTER("build_completion_hash");

1617
  if (status.batch || quick || !current_db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1618 1619 1620 1621 1622 1623 1624
    DBUG_VOID_RETURN;			// We don't need completion in batches

  /* hash SQL commands */
  while (cmd->name) {
    add_word(&ht,(char*) cmd->name);
    cmd++;
  }
1625
  if (!rehash)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1626 1627
    DBUG_VOID_RETURN;

1628 1629 1630 1631 1632 1633
  /* Free old used memory */
  if (field_names)
    field_names=0;
  completion_hash_clean(&ht);
  free_root(&hash_mem_root,MYF(0));

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1634 1635 1636
  /* hash MySQL functions (to be implemented) */

  /* hash all database names */
1637 1638
  if (mysql_query(&mysql,"show databases") == 0)
  {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1639 1640 1641 1642 1643
    if (!(databases = mysql_store_result(&mysql)))
      put_info(mysql_error(&mysql),INFO_INFO);
    else
    {
      while ((database_row=mysql_fetch_row(databases)))
1644 1645 1646 1647 1648 1649
      {
	char *str=strdup_root(&hash_mem_root, (char*) database_row[0]);
	if (str)
	  add_word(&ht,(char*) str);
      }
      mysql_free_result(databases);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660
    }
  }
  /* hash all table names */
  if (mysql_query(&mysql,"show tables")==0)
  {
    if (!(tables = mysql_store_result(&mysql)))
      put_info(mysql_error(&mysql),INFO_INFO);
    else
    {
      if (mysql_num_rows(tables) > 0 && !opt_silent && write_info)
      {
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1661
	tee_fprintf(stdout, "\
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1662 1663 1664 1665 1666
Reading table information for completion of table and column names\n\
You can turn off this feature to get a quicker startup with -A\n\n");
      }
      while ((table_row=mysql_fetch_row(tables)))
      {
1667 1668 1669 1670
	char *str=strdup_root(&hash_mem_root, (char*) table_row[0]);
	if (str &&
	    !completion_hash_exists(&ht,(char*) str, (uint) strlen(str)))
	  add_word(&ht,str);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1671 1672 1673 1674 1675
      }
    }
  }

  /* hash all field names, both with the table prefix and without it */
1676 1677
  if (!tables)					/* no tables */
  {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1678 1679 1680
    DBUG_VOID_RETURN;
  }
  mysql_data_seek(tables,0);
1681 1682 1683 1684
  if (!(field_names= (char ***) alloc_root(&hash_mem_root,sizeof(char **) *
					   (uint) (mysql_num_rows(tables)+1))))
  {
    mysql_free_result(tables);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1685
    DBUG_VOID_RETURN;
1686
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1687 1688 1689 1690 1691 1692
  i=0;
  while ((table_row=mysql_fetch_row(tables)))
  {
    if ((fields=mysql_list_fields(&mysql,(const char*) table_row[0],NullS)))
    {
      num_fields=mysql_num_fields(fields);
1693 1694 1695
      if (!(field_names[i] = (char **) alloc_root(&hash_mem_root,
						  sizeof(char *) *
						  (num_fields*2+1))))
1696 1697 1698 1699
      {
        mysql_free_result(fields);
        break;
      }
1700
      field_names[i][num_fields*2]= '\0';
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1701 1702 1703
      j=0;
      while ((sql_field=mysql_fetch_field(fields)))
      {
1704
	sprintf(buf,"%.64s.%.64s",table_row[0],sql_field->name);
1705
	field_names[i][j] = strdup_root(&hash_mem_root,buf);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1706
	add_word(&ht,field_names[i][j]);
1707 1708
	field_names[i][num_fields+j] = strdup_root(&hash_mem_root,
						   sql_field->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1709
	if (!completion_hash_exists(&ht,field_names[i][num_fields+j],
1710
				    (uint) strlen(field_names[i][num_fields+j])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1711 1712 1713
	  add_word(&ht,field_names[i][num_fields+j]);
	j++;
      }
1714
      mysql_free_result(fields);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1715 1716
    }
    else
1717
      field_names[i]= 0;
patg@krsna.patg.net's avatar
patg@krsna.patg.net committed
1718

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1719 1720
    i++;
  }
1721
  mysql_free_result(tables);
1722
  field_names[i]=0;				// End pointer
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1723 1724 1725 1726 1727 1728 1729
  DBUG_VOID_RETURN;
}

	/* for gnu readline */

#ifndef HAVE_INDEX
extern "C" {
1730
extern char *index(const char *,int c),*rindex(const char *,int);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1731

1732
char *index(const char *s,int c)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1733 1734 1735 1736 1737 1738 1739 1740
{
  for (;;)
  {
     if (*s == (char) c) return (char*) s;
     if (!*s++) return NullS;
  }
}

1741
char *rindex(const char *s,int c)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752
{
  reg3 char *t;

  t = NullS;
  do if (*s == (char) c) t = (char*) s; while (*s++);
  return (char*) t;
}
}
#endif
#endif /* HAVE_READLINE */

1753

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1754 1755
static int reconnect(void)
{
1756
  /* purecov: begin tested */
1757
  if (opt_reconnect)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1758 1759 1760
  {
    put_info("No connection. Trying to reconnect...",INFO_INFO);
    (void) com_connect((String *) 0, 0);
1761
    if (opt_rehash)
1762
      com_rehash(NULL, NULL);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1763 1764 1765
  }
  if (!connected)
    return put_info("Can't connect to the server\n",INFO_ERROR);
1766
  /* purecov: end */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1767 1768 1769
  return 0;
}

1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785
static void get_current_db()
{
  MYSQL_RES *res;

  my_free(current_db, MYF(MY_ALLOW_ZERO_PTR));
  current_db= NULL;
  /* In case of error below current_db will be NULL */
  if (!mysql_query(&mysql, "SELECT DATABASE()") &&
      (res= mysql_use_result(&mysql)))
  {
    MYSQL_ROW row= mysql_fetch_row(res);
    if (row[0])
      current_db= my_strdup(row[0], MYF(MY_WME));
    mysql_free_result(res);
  }
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1786 1787 1788 1789 1790

/***************************************************************************
 The different commands
***************************************************************************/

1791 1792 1793 1794
int mysql_real_query_for_lazy(const char *buf, int length)
{
  for (uint retry=0;; retry++)
  {
1795
    int error;
1796
    if (!mysql_real_query(&mysql,buf,length))
1797
      return 0;
1798
    error= put_error(&mysql);
1799
    if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR || retry > 1 ||
1800
        !opt_reconnect)
1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812
      return error;
    if (reconnect())
      return error;
  }
}

int mysql_store_result_for_lazy(MYSQL_RES **result)
{
  if ((*result=mysql_store_result(&mysql)))
    return 0;

  if (mysql_error(&mysql)[0])
1813
    return put_error(&mysql);
1814 1815 1816
  return 0;
}

vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1817 1818 1819 1820 1821
static void print_help_item(MYSQL_ROW *cur, int num_name, int num_cat, char *last_char)
{
  char ccat= (*cur)[num_cat][0];
  if (*last_char != ccat)
  {
paul@kite-hub.kitebird.com's avatar
paul@kite-hub.kitebird.com committed
1822
    put_info(ccat == 'Y' ? "categories:" : "topics:", INFO_INFO);
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1823 1824 1825 1826
    *last_char= ccat;
  }
  tee_fprintf(PAGER, "   %s\n", (*cur)[num_name]);
}
1827

1828

1829
static int com_server_help(String *buffer __attribute__((unused)),
1830
			   char *line __attribute__((unused)), char *help_arg)
1831 1832 1833 1834
{
  MYSQL_ROW cur;
  const char *server_cmd= buffer->ptr();
  char cmd_buf[100];
1835 1836
  MYSQL_RES *result;
  int error;
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1837
  
1838
  if (help_arg[0] != '\'')
1839
  {
1840 1841 1842 1843 1844 1845 1846 1847
	char *end_arg= strend(help_arg);
	if(--end_arg)
	{
		while (my_isspace(charset_info,*end_arg))
          end_arg--;
		*++end_arg= '\0';
	}
	(void) strxnmov(cmd_buf, sizeof(cmd_buf), "help '", help_arg, "'", NullS);
1848 1849
    server_cmd= cmd_buf;
  }
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1850
  
1851 1852 1853 1854 1855 1856 1857 1858 1859
  if (!status.batch)
  {
    old_buffer= *buffer;
    old_buffer.copy();
  }

  if (!connected && reconnect())
    return 1;

1860
  if ((error= mysql_real_query_for_lazy(server_cmd,(int)strlen(server_cmd))) ||
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1861
      (error= mysql_store_result_for_lazy(&result)))
1862 1863 1864 1865
    return error;

  if (result)
  {
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1866 1867
    unsigned int num_fields= mysql_num_fields(result);
    my_ulonglong num_rows= mysql_num_rows(result);
monty@mysql.com's avatar
monty@mysql.com committed
1868
    mysql_fetch_fields(result);
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1869
    if (num_fields==3 && num_rows==1)
1870 1871
    {
      if (!(cur= mysql_fetch_row(result)))
1872 1873 1874 1875
      {
	error= -1;
	goto err;
      }
1876 1877

      init_pager();
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1878 1879 1880 1881 1882
      tee_fprintf(PAGER,   "Name: \'%s\'\n", cur[0]);
      tee_fprintf(PAGER,   "Description:\n%s", cur[1]);
      if (cur[2] && *((char*)cur[2]))
	tee_fprintf(PAGER, "Examples:\n%s", cur[2]);
      tee_fprintf(PAGER,   "\n");
1883 1884
      end_pager();
    }
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1885
    else if (num_fields >= 2 && num_rows)
1886 1887
    {
      init_pager();
1888 1889
      char last_char= 0;

1890
      int num_name= 0, num_cat= 0;
1891 1892 1893
      LINT_INIT(num_name);
      LINT_INIT(num_cat);

vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1894
      if (num_fields == 2)
1895
      {
paul@kite-hub.kitebird.com's avatar
paul@kite-hub.kitebird.com committed
1896
	put_info("Many help items for your request exist.", INFO_INFO);
paul@frost.snake.net's avatar
paul@frost.snake.net committed
1897
	put_info("To make a more specific request, please type 'help <item>',\nwhere <item> is one of the following", INFO_INFO);
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1898 1899
	num_name= 0;
	num_cat= 1;
1900
      }
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1901 1902
      else if ((cur= mysql_fetch_row(result)))
      {
paul@kite-hub.kitebird.com's avatar
paul@kite-hub.kitebird.com committed
1903
	tee_fprintf(PAGER, "You asked for help about help category: \"%s\"\n", cur[0]);
paul@frost.snake.net's avatar
paul@frost.snake.net committed
1904
	put_info("For more information, type 'help <item>', where <item> is one of the following", INFO_INFO);
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1905 1906 1907 1908
	num_name= 1;
	num_cat= 2;
	print_help_item(&cur,1,2,&last_char);
      }
1909

vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1910 1911
      while ((cur= mysql_fetch_row(result)))
	print_help_item(&cur,num_name,num_cat,&last_char);
1912 1913 1914 1915 1916
      tee_fprintf(PAGER, "\n");
      end_pager();
    }
    else
    {
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1917
      put_info("\nNothing found", INFO_INFO);
paul@kite-hub.kitebird.com's avatar
paul@kite-hub.kitebird.com committed
1918
      put_info("Please try to run 'help contents' for a list of all accessible topics\n", INFO_INFO);
1919 1920 1921
    }
  }

1922
err:
1923 1924 1925 1926
  mysql_free_result(result);
  return error;
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1927
static int
1928 1929
com_help(String *buffer __attribute__((unused)),
	 char *line __attribute__((unused)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1930
{
1931 1932
  reg1 int i, j;
  char * help_arg= strchr(line,' '), buff[32], *end;
1933
  if (help_arg)
1934 1935 1936 1937 1938 1939
  {
    while (my_isspace(charset_info,*help_arg))
      help_arg++;
	if (*help_arg)	  
	  return com_server_help(buffer,line,help_arg);
  }
1940

1941 1942 1943 1944 1945 1946 1947
  put_info("\nFor information about MySQL products and services, visit:\n"
           "   http://www.mysql.com/\n"
           "For developer information, including the MySQL Reference Manual, "
           "visit:\n"
           "   http://dev.mysql.com/\n"
           "To buy MySQL Network Support, training, or other products, visit:\n"
           "   https://shop.mysql.com/\n", INFO_INFO);
1948 1949 1950 1951
  put_info("List of all MySQL commands:", INFO_INFO);
  if (!named_cmds)
    put_info("Note that all text commands must be first on line and end with ';'",INFO_INFO);
  for (i = 0; commands[i].name; i++)
1952
  {
1953
    end= strmov(buff, commands[i].name);
1954
    for (j= (int)strlen(commands[i].name); j < 10; j++)
1955
      end= strmov(end, " ");
1956
    if (commands[i].func)
1957
      tee_fprintf(stdout, "%s(\\%c) %s\n", buff,
1958
		  commands[i].cmd_char, commands[i].doc);
1959
  }
1960
  if (connected && mysql_get_server_version(&mysql) >= 40100)
serg@serg.mylan's avatar
serg@serg.mylan committed
1961
    put_info("\nFor server side help, type 'help contents'\n", INFO_INFO);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1962 1963 1964 1965 1966 1967 1968 1969
  return 0;
}


	/* ARGSUSED */
static int
com_clear(String *buffer,char *line __attribute__((unused)))
{
1970 1971 1972 1973
#ifdef HAVE_READLINE
  if (status.add_to_history)
    fix_history(buffer);
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1974 1975 1976 1977
  buffer->length(0);
  return 0;
}

1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994
	/* ARGSUSED */
static int
com_charset(String *buffer __attribute__((unused)), char *line)
{
  char buff[256], *param;
  CHARSET_INFO * new_cs;
  strmake(buff, line, sizeof(buff) - 1);
  param= get_arg(buff, 0);
  if (!param || !*param)
  {
    return put_info("Usage: \\C char_setname | charset charset_name", 
		    INFO_ERROR, 0);
  }
  new_cs= get_charset_by_csname(param, MY_CS_PRIMARY, MYF(MY_WME));
  if (new_cs)
  {
    charset_info= new_cs;
1995 1996 1997
    mysql_set_character_set(&mysql, charset_info->csname);
    default_charset= (char *)charset_info->csname;
    default_charset_used= 1;
1998 1999 2000 2001 2002
    put_info("Charset changed", INFO_INFO);
  }
  else put_info("Charset is not found", INFO_INFO);
  return 0;
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2003 2004

/*
2005 2006 2007 2008
  Execute command
  Returns: 0  if ok
          -1 if not fatal error
	  1  if fatal error
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2009 2010 2011 2012 2013 2014
*/


static int
com_go(String *buffer,char *line __attribute__((unused)))
{
2015
  char		buff[200], time_buff[32], *pos;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2016
  MYSQL_RES	*result;
2017
  ulong		timer, warnings;
2018 2019
  uint		error= 0;
  int           err= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2020

2021
  interrupted_query= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2022 2023 2024 2025 2026 2027
  if (!status.batch)
  {
    old_buffer= *buffer;			// Save for edit command
    old_buffer.copy();
  }

2028
  /* Remove garbage for nicer messages */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041
  LINT_INIT(buff[0]);
  remove_cntrl(*buffer);

  if (buffer->is_empty())
  {
    if (status.batch)				// Ignore empty quries
      return 0;
    return put_info("No query specified\n",INFO_ERROR);

  }
  if (!connected && reconnect())
  {
    buffer->length(0);				// Remove query on error
2042
    return opt_reconnect ? -1 : 1;          // Fatal error
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2043 2044 2045 2046 2047
  }
  if (verbose)
    (void) com_print(buffer,0);

  if (skip_updates &&
2048
      (buffer->length() < 4 || my_strnncoll(charset_info,
2049 2050
					    (const uchar*)buffer->ptr(),4,
					    (const uchar*)"SET ",4)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2051 2052 2053 2054 2055 2056
  {
    (void) put_info("Ignoring query to other database",INFO_INFO);
    return 0;
  }

  timer=start_timer();
2057
  executing_query= 1;
2058
  error= mysql_real_query_for_lazy(buffer->ptr(),buffer->length());
2059 2060 2061 2062 2063 2064 2065 2066 2067 2068

#ifdef HAVE_READLINE
  if (status.add_to_history) 
  {  
    buffer->append(vertical ? "\\G" : delimiter);
    /* Append final command onto history */
    fix_history(buffer);
  }
#endif

2069
  if (error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2070
  {
2071
    executing_query= 0;
2072 2073
    buffer->length(0); // Remove query on error
    return error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2074 2075 2076 2077
  }
  error=0;
  buffer->length(0);

2078
  do
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2079
  {
2080
    if (quick)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2081
    {
2082
      if (!(result=mysql_use_result(&mysql)) && mysql_field_count(&mysql))
2083 2084 2085 2086
      {
        executing_query= 0;
        return put_error(&mysql);
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2087 2088 2089
    }
    else
    {
2090 2091
      error= mysql_store_result_for_lazy(&result);
      if (error)
2092 2093 2094 2095
      {
        executing_query= 0;
        return error;
      }
2096 2097 2098 2099 2100 2101 2102 2103
    }

    if (verbose >= 3 || !opt_silent)
      mysql_end_timer(timer,time_buff);
    else
      time_buff[0]=0;
    if (result)
    {
2104
      if (!mysql_num_rows(result) && ! quick && !column_types_flag)
2105 2106 2107
      {
	strmov(buff, "Empty set");
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2108
      else
2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124
      {
	init_pager();
	if (opt_html)
	  print_table_data_html(result);
	else if (opt_xml)
	  print_table_data_xml(result);
	else if (vertical)
	  print_table_data_vertically(result);
	else if (opt_silent && verbose <= 2 && !output_tables)
	  print_tab_data(result);
	else
	  print_table_data(result);
	sprintf(buff,"%ld %s in set",
		(long) mysql_num_rows(result),
		(long) mysql_num_rows(result) == 1 ? "row" : "rows");
	end_pager();
2125 2126
        if (mysql_errno(&mysql))
          error= put_error(&mysql);
2127
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2128
    }
2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140
    else if (mysql_affected_rows(&mysql) == ~(ulonglong) 0)
      strmov(buff,"Query OK");
    else
      sprintf(buff,"Query OK, %ld %s affected",
	      (long) mysql_affected_rows(&mysql),
	      (long) mysql_affected_rows(&mysql) == 1 ? "row" : "rows");

    pos=strend(buff);
    if ((warnings= mysql_warning_count(&mysql)))
    {
      *pos++= ',';
      *pos++= ' ';
2141
      pos=int10_to_str(warnings, pos, 10);
2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156
      pos=strmov(pos, " warning");
      if (warnings != 1)
	*pos++= 's';
    }
    strmov(pos, time_buff);
    put_info(buff,INFO_RESULT);
    if (mysql_info(&mysql))
      put_info(mysql_info(&mysql),INFO_RESULT);
    put_info("",INFO_RESULT);			// Empty row

    if (result && !mysql_eof(result))	/* Something wrong when using quick */
      error= put_error(&mysql);
    else if (unbuffered)
      fflush(stdout);
    mysql_free_result(result);
2157 2158 2159
  } while (!(err= mysql_next_result(&mysql)));
  if (err >= 1)
    error= put_error(&mysql);
2160

2161
  if (show_warnings == 1 && warnings >= 1) /* Show warnings if any */
2162 2163 2164 2165 2166 2167
  {
    init_pager();
    print_warnings();
    end_pager();
  }

ramil@mysql.com's avatar
ramil@mysql.com committed
2168 2169
  if (!error && !status.batch && 
      (mysql.server_status & SERVER_STATUS_DB_DROPPED))
2170 2171
    get_current_db();

2172
  executing_query= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2173 2174 2175
  return error;				/* New command follows */
}

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2176 2177 2178

static void init_pager()
{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2179
#ifdef USE_POPEN
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194
  if (!opt_nopager)
  {
    if (!(PAGER= popen(pager, "w")))
    {
      tee_fprintf(stdout, "popen() failed! defaulting PAGER to stdout!\n");
      PAGER= stdout;
    }
  }
  else
#endif
    PAGER= stdout;
}

static void end_pager()
{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2195
#ifdef USE_POPEN
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2196 2197 2198 2199 2200
  if (!opt_nopager)
    pclose(PAGER);
#endif
}

2201

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2202
static void init_tee(const char *file_name)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2203
{
2204
  FILE* new_outfile;
2205
  if (opt_outfile)
2206
    end_tee();
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2207 2208 2209 2210 2211
  if (!(new_outfile= my_fopen(file_name, O_APPEND | O_WRONLY, MYF(MY_WME))))
  {
    tee_fprintf(stdout, "Error logging to file '%s'\n", file_name);
    return;
  }
2212
  OUTFILE = new_outfile;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2213 2214 2215 2216
  strmake(outfile, file_name, FN_REFLEN-1);
  tee_fprintf(stdout, "Logging to file '%s'\n", file_name);
  opt_outfile= 1;
  return;
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2217 2218
}

2219

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2220 2221 2222
static void end_tee()
{
  my_fclose(OUTFILE, MYF(0));
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2223
  OUTFILE= 0;
2224
  opt_outfile= 0;
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2225 2226 2227
  return;
}

monty@mishka.local's avatar
monty@mishka.local committed
2228

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239
static int
com_ego(String *buffer,char *line)
{
  int result;
  bool oldvertical=vertical;
  vertical=1;
  result=com_go(buffer,line);
  vertical=oldvertical;
  return result;
}

monty@mishka.local's avatar
monty@mishka.local committed
2240 2241 2242 2243

static const char *fieldtype2str(enum enum_field_types type)
{
  switch (type) {
2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269
    case MYSQL_TYPE_BIT:         return "BIT";
    case MYSQL_TYPE_BLOB:        return "BLOB";
    case MYSQL_TYPE_DATE:        return "DATE";
    case MYSQL_TYPE_DATETIME:    return "DATETIME";
    case MYSQL_TYPE_NEWDECIMAL:  return "NEWDECIMAL";
    case MYSQL_TYPE_DECIMAL:     return "DECIMAL";
    case MYSQL_TYPE_DOUBLE:      return "DOUBLE";
    case MYSQL_TYPE_ENUM:        return "ENUM";
    case MYSQL_TYPE_FLOAT:       return "FLOAT";
    case MYSQL_TYPE_GEOMETRY:    return "GEOMETRY";
    case MYSQL_TYPE_INT24:       return "INT24";
    case MYSQL_TYPE_LONG:        return "LONG";
    case MYSQL_TYPE_LONGLONG:    return "LONGLONG";
    case MYSQL_TYPE_LONG_BLOB:   return "LONG_BLOB";
    case MYSQL_TYPE_MEDIUM_BLOB: return "MEDIUM_BLOB";
    case MYSQL_TYPE_NEWDATE:     return "NEWDATE";
    case MYSQL_TYPE_NULL:        return "NULL";
    case MYSQL_TYPE_SET:         return "SET";
    case MYSQL_TYPE_SHORT:       return "SHORT";
    case MYSQL_TYPE_STRING:      return "STRING";
    case MYSQL_TYPE_TIME:        return "TIME";
    case MYSQL_TYPE_TIMESTAMP:   return "TIMESTAMP";
    case MYSQL_TYPE_TINY:        return "TINY";
    case MYSQL_TYPE_TINY_BLOB:   return "TINY_BLOB";
    case MYSQL_TYPE_VAR_STRING:  return "VAR_STRING";
    case MYSQL_TYPE_YEAR:        return "YEAR";
serg@serg.mylan's avatar
serg@serg.mylan committed
2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303
    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;
}

2304 2305 2306
static void
print_field_types(MYSQL_RES *result)
{
serg@serg.mylan's avatar
serg@serg.mylan committed
2307 2308 2309
  MYSQL_FIELD   *field;
  uint i=0;

2310 2311
  while ((field = mysql_fetch_field(result)))
  {
serg@serg.mylan's avatar
serg@serg.mylan committed
2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328
    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));
2329 2330 2331 2332
  }
  tee_puts("", PAGER);
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2333 2334 2335 2336 2337 2338 2339 2340 2341 2342

static void
print_table_data(MYSQL_RES *result)
{
  String separator(256);
  MYSQL_ROW	cur;
  MYSQL_FIELD	*field;
  bool		*num_flag;

  num_flag=(bool*) my_alloca(sizeof(bool)*mysql_num_fields(result));
2343
  if (column_types_flag)
2344 2345
  {
    print_field_types(result);
serg@serg.mylan's avatar
serg@serg.mylan committed
2346 2347
    if (!mysql_num_rows(result))
      return;
2348 2349
    mysql_field_seek(result,0);
  }
2350
  separator.copy("+",1,charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2351 2352
  while ((field = mysql_fetch_field(result)))
  {
2353
    uint length= column_names ? field->name_length : 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2354 2355 2356 2357 2358 2359
    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"
2360
    field->max_length=length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2361 2362 2363
    separator.fill(separator.length()+length+2,'-');
    separator.append('+');
  }
monty@mysql.com's avatar
monty@mysql.com committed
2364 2365
  separator.append('\0');                       // End marker for \0
  tee_puts((char*) separator.ptr(), PAGER);
2366
  if (column_names)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2367 2368
  {
    mysql_field_seek(result,0);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2369
    (void) tee_fputs("|", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2370 2371
    for (uint off=0; (field = mysql_fetch_field(result)) ; off++)
    {
2372 2373 2374 2375 2376
      uint name_length= (uint) strlen(field->name);
      uint numcells= charset_info->cset->numcells(charset_info,
                                                  field->name,
                                                  field->name + name_length);
      uint display_length= field->max_length + name_length - numcells;
2377
      tee_fprintf(PAGER, " %-*s |",(int) min(display_length,
2378
                                            MAX_COLUMN_LENGTH),
2379
                  field->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2380 2381
      num_flag[off]= IS_NUM(field->type);
    }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2382
    (void) tee_fputs("\n", PAGER);
monty@mysql.com's avatar
monty@mysql.com committed
2383
    tee_puts((char*) separator.ptr(), PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2384 2385
  }

2386
  while ((cur= mysql_fetch_row(result)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2387
  {
2388 2389
    if (interrupted_query)
      break;
bar@mysql.com's avatar
bar@mysql.com committed
2390
    ulong *lengths= mysql_fetch_lengths(result);
2391
    (void) tee_fputs("| ", PAGER);
2392 2393
    mysql_field_seek(result, 0);
    for (uint off= 0; off < mysql_num_fields(result); off++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2394
    {
2395 2396 2397 2398 2399 2400
      const char *buffer;
      uint data_length;
      uint field_max_length;
      uint visible_length;
      uint extra_padding;

2401
      if (cur[off] == NULL)
2402 2403 2404 2405 2406
      {
        buffer= "NULL";
        data_length= 4;
      } 
      else 
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2407
      {
2408 2409
        buffer= cur[off];
        data_length= (uint) lengths[off];
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2410
      }
2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427

      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);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2428
      else
bar@mysql.com's avatar
bar@mysql.com committed
2429
      {
2430 2431
        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);
2432
        else 
2433
          tee_print_sized_data(buffer, data_length, field_max_length+extra_padding, FALSE);
bar@mysql.com's avatar
bar@mysql.com committed
2434
      }
2435
      tee_fputs(" | ", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2436
    }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2437
    (void) tee_fputs("\n", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2438
  }
monty@mysql.com's avatar
monty@mysql.com committed
2439
  tee_puts((char*) separator.ptr(), PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2440 2441 2442
  my_afree((gptr) num_flag);
}

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2443

2444
static void
2445
tee_print_sized_data(const char *data, unsigned int data_length, unsigned int total_bytes_to_send, bool right_justified)
2446 2447 2448 2449 2450 2451 2452 2453 2454
{
  /* 
    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;

2455
  if (right_justified) 
2456
    for (i= data_length; i < total_bytes_to_send; i++)
2457
      tee_putc((int)' ', PAGER);
2458

2459
  for (i= 0, p= data; i < data_length; i+= 1, p+= 1)
2460 2461 2462 2463 2464 2465 2466
  {
    if (*p == '\0')
      tee_putc((int)' ', PAGER);
    else
      tee_putc((int)*p, PAGER);
  }

2467
  if (! right_justified) 
2468
    for (i= data_length; i < total_bytes_to_send; i++)
2469
      tee_putc((int)' ', PAGER);
2470 2471 2472 2473
}



bk@work.mysql.com's avatar
bk@work.mysql.com committed
2474 2475 2476
static void
print_table_data_html(MYSQL_RES *result)
{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2477 2478
  MYSQL_ROW	cur;
  MYSQL_FIELD	*field;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2479 2480

  mysql_field_seek(result,0);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2481
  (void) tee_fputs("<TABLE BORDER=1><TR>", PAGER);
2482
  if (column_names)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2483 2484 2485
  {
    while((field = mysql_fetch_field(result)))
    {
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2486 2487 2488
      tee_fprintf(PAGER, "<TH>%s</TH>", (field->name ? 
					 (field->name[0] ? field->name : 
					  " &nbsp; ") : "NULL"));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2489
    }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2490
    (void) tee_fputs("</TR>", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2491 2492 2493
  }
  while ((cur = mysql_fetch_row(result)))
  {
2494 2495
    if (interrupted_query)
      break;
2496
    ulong *lengths=mysql_fetch_lengths(result);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2497
    (void) tee_fputs("<TR>", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2498 2499
    for (uint i=0; i < mysql_num_fields(result); i++)
    {
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2500
      (void) tee_fputs("<TD>", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2501
      safe_put_field(cur[i],lengths[i]);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2502
      (void) tee_fputs("</TD>", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2503
    }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2504
    (void) tee_fputs("</TR>", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2505
  }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2506
  (void) tee_fputs("</TABLE>", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2507 2508 2509
}


2510 2511 2512 2513 2514 2515 2516 2517
static void
print_table_data_xml(MYSQL_RES *result)
{
  MYSQL_ROW   cur;
  MYSQL_FIELD *fields;

  mysql_field_seek(result,0);

2518
  tee_fputs("<?xml version=\"1.0\"?>\n\n<resultset statement=\"", PAGER);
2519
  xmlencode_print(glob_buffer.ptr(), (int)strlen(glob_buffer.ptr()));
2520
  tee_fputs("\">", PAGER);
2521 2522 2523 2524

  fields = mysql_fetch_fields(result);
  while ((cur = mysql_fetch_row(result)))
  {
2525 2526
    if (interrupted_query)
      break;
2527
    ulong *lengths=mysql_fetch_lengths(result);
2528 2529 2530
    (void) tee_fputs("\n  <row>\n", PAGER);
    for (uint i=0; i < mysql_num_fields(result); i++)
    {
2531
      tee_fprintf(PAGER, "\t<field name=\"");
2532
      xmlencode_print(fields[i].name, (uint) strlen(fields[i].name));
2533 2534 2535 2536 2537 2538 2539 2540
      if (cur[i])
      {
        tee_fprintf(PAGER, "\">");
        xmlencode_print(cur[i], lengths[i]);
        tee_fprintf(PAGER, "</field>\n");
      }
      else
        tee_fprintf(PAGER, "\" xsi:nil=\"true\" />\n");
2541 2542 2543 2544 2545 2546
    }
    (void) tee_fputs("  </row>\n", PAGER);
  }
  (void) tee_fputs("</resultset>\n", PAGER);
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2547 2548 2549 2550 2551 2552 2553 2554 2555 2556

static void
print_table_data_vertically(MYSQL_RES *result)
{
  MYSQL_ROW	cur;
  uint		max_length=0;
  MYSQL_FIELD	*field;

  while ((field = mysql_fetch_field(result)))
  {
2557
    uint length= field->name_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2558 2559 2560 2561 2562 2563 2564 2565
    if (length > max_length)
      max_length= length;
    field->max_length=length;
  }

  mysql_field_seek(result,0);
  for (uint row_count=1; (cur= mysql_fetch_row(result)); row_count++)
  {
2566 2567
    if (interrupted_query)
      break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2568
    mysql_field_seek(result,0);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2569 2570
    tee_fprintf(PAGER, 
		"*************************** %d. row ***************************\n", row_count);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2571 2572 2573
    for (uint off=0; off < mysql_num_fields(result); off++)
    {
      field= mysql_fetch_field(result);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2574 2575
      tee_fprintf(PAGER, "%*s: ",(int) max_length,field->name);
      tee_fprintf(PAGER, "%s\n",cur[off] ? (char*) cur[off] : "NULL");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2576 2577 2578 2579
    }
  }
}

2580

2581
/* print_warnings should be called right after executing a statement */
2582 2583

static void print_warnings()
2584
{
2585
  const char   *query;
2586 2587
  MYSQL_RES    *result;
  MYSQL_ROW    cur;
2588
  my_ulonglong num_rows;
2589 2590

  /* Get the warnings */
2591 2592
  query= "show warnings";
  mysql_real_query_for_lazy(query, strlen(query));
2593 2594 2595
  mysql_store_result_for_lazy(&result);

  /* Bail out when no warnings */
2596
  if (!(num_rows= mysql_num_rows(result)))
2597 2598 2599 2600 2601 2602 2603 2604
  {
    mysql_free_result(result);
    return;
  }

  /* Print the warnings */
  while ((cur= mysql_fetch_row(result)))
  {
2605
    tee_fprintf(PAGER, "%s (Code %s): %s\n", cur[0], cur[1], cur[2]);
2606 2607 2608
  }
  mysql_free_result(result);
}
2609

2610 2611

static const char *array_value(const char **array, char key)
2612
{
2613 2614 2615
  for (; *array; array+= 2)
    if (**array == key)
      return array[1];
2616 2617 2618
  return 0;
}

2619

2620
static void
2621
xmlencode_print(const char *src, uint length)
2622
{
2623 2624 2625
  if (!src)
    tee_fputs("NULL", PAGER);
  else
2626
  {
2627 2628 2629 2630 2631 2632 2633 2634 2635
    for (const char *p = src; *p && length; *p++, length--)
    {
      const char *t;
      if ((t = array_value(xmlmeta, *p)))
	tee_fputs(t, PAGER);
      else
	tee_putc(*p, PAGER);
    }
  }
2636
}
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2637

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2638 2639 2640 2641 2642

static void
safe_put_field(const char *pos,ulong length)
{
  if (!pos)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2643
    tee_fputs("NULL", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2644 2645 2646
  else
  {
    if (opt_raw_data)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2647
      tee_fputs(pos, PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2648 2649 2650 2651
    else for (const char *end=pos+length ; pos != end ; pos++)
    {
#ifdef USE_MB
      int l;
2652 2653
      if (use_mb(charset_info) &&
          (l = my_ismbchar(charset_info, pos, end)))
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
2654
      {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2655
	  while (l--)
2656
	    tee_putc(*pos++, PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2657 2658 2659 2660 2661
	  pos--;
	  continue;
      }
#endif
      if (!*pos)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2662
	tee_fputs("\\0", PAGER); // This makes everything hard
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2663
      else if (*pos == '\t')
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2664
	tee_fputs("\\t", PAGER); // This would destroy tab format
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2665
      else if (*pos == '\n')
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2666
	tee_fputs("\\n", PAGER); // This too
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2667
      else if (*pos == '\\')
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2668
	tee_fputs("\\\\", PAGER);
2669
	else
2670
	tee_putc(*pos, PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682
    }
  }
}


static void
print_tab_data(MYSQL_RES *result)
{
  MYSQL_ROW	cur;
  MYSQL_FIELD	*field;
  ulong		*lengths;

2683
  if (opt_silent < 2 && column_names)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2684 2685 2686 2687 2688
  {
    int first=0;
    while ((field = mysql_fetch_field(result)))
    {
      if (first++)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2689 2690
	(void) tee_fputs("\t", PAGER);
      (void) tee_fputs(field->name, PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2691
    }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2692
    (void) tee_fputs("\n", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2693 2694 2695 2696 2697 2698 2699
  }
  while ((cur = mysql_fetch_row(result)))
  {
    lengths=mysql_fetch_lengths(result);
    safe_put_field(cur[0],lengths[0]);
    for (uint off=1 ; off < mysql_num_fields(result); off++)
    {
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2700
      (void) tee_fputs("\t", PAGER);
2701
      safe_put_field(cur[off], lengths[off]);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2702
    }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2703
    (void) tee_fputs("\n", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2704 2705 2706
  }
}

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2707 2708 2709 2710 2711 2712 2713
static int
com_tee(String *buffer, char *line __attribute__((unused)))
{
  char file_name[FN_REFLEN], *end, *param;

  if (status.batch)
    return 0;
2714
  while (my_isspace(charset_info,*line))
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2715 2716 2717 2718 2719
    line++;
  if (!(param = strchr(line, ' '))) // if outfile wasn't given, use the default
  {
    if (!strlen(outfile))
    {
2720
      printf("No previous outfile available, you must give a filename!\n");
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2721 2722
      return 0;
    }
2723 2724 2725 2726 2727 2728 2729
    else if (opt_outfile)
    {
      tee_fprintf(stdout, "Currently logging to file '%s'\n", outfile);
      return 0;
    }
    else
      param = outfile;			//resume using the old outfile
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2730
  }
2731 2732

  /* eliminate the spaces before the parameters */
2733
  while (my_isspace(charset_info,*param))
2734 2735 2736
    param++;
  end= strmake(file_name, param, sizeof(file_name) - 1);
  /* remove end space from command line */
2737 2738
  while (end > file_name && (my_isspace(charset_info,end[-1]) || 
			     my_iscntrl(charset_info,end[-1])))
2739 2740
    end--;
  end[0]= 0;
2741
  if (end == file_name)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2742 2743 2744 2745
  {
    printf("No outfile specified!\n");
    return 0;
  }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2746
  init_tee(file_name);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2747 2748 2749
  return 0;
}

2750

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2751 2752 2753 2754 2755 2756 2757 2758 2759 2760
static int
com_notee(String *buffer __attribute__((unused)),
	  char *line __attribute__((unused)))
{
  if (opt_outfile)
    end_tee();
  tee_fprintf(stdout, "Outfile disabled.\n");
  return 0;
}

2761
/*
2762
  Sorry, this command is not available in Windows.
2763 2764
*/

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2765
#ifdef USE_POPEN
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2766 2767 2768 2769 2770 2771 2772
static int
com_pager(String *buffer, char *line __attribute__((unused)))
{
  char pager_name[FN_REFLEN], *end, *param;

  if (status.batch)
    return 0;
2773 2774
  /* Skip spaces in front of the pager command */
  while (my_isspace(charset_info, *line))
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2775
    line++;
2776 2777 2778 2779 2780 2781
  /* Skip the pager command */
  param= strchr(line, ' ');
  /* Skip the spaces between the command and the argument */
  while (param && my_isspace(charset_info, *param))
    param++;
  if (!param || !strlen(param)) // if pager was not given, use the default
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2782
  {
2783
    if (!default_pager_set)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2784
    {
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2785
      tee_fprintf(stdout, "Default pager wasn't set, using stdout.\n");
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2786 2787 2788 2789 2790 2791 2792 2793 2794
      opt_nopager=1;
      strmov(pager, "stdout");
      PAGER= stdout;
      return 0;
    }
    strmov(pager, default_pager);
  }
  else
  {
2795
    end= strmake(pager_name, param, sizeof(pager_name)-1);
2796 2797
    while (end > pager_name && (my_isspace(charset_info,end[-1]) || 
                                my_iscntrl(charset_info,end[-1])))
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2798 2799 2800
      end--;
    end[0]=0;
    strmov(pager, pager_name);
2801
    strmov(default_pager, pager_name);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2802 2803
  }
  opt_nopager=0;
2804
  tee_fprintf(stdout, "PAGER set to '%s'\n", pager);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2805 2806 2807
  return 0;
}

2808

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2809 2810 2811 2812 2813 2814
static int
com_nopager(String *buffer __attribute__((unused)),
	    char *line __attribute__((unused)))
{
  strmov(pager, "stdout");
  opt_nopager=1;
2815
  PAGER= stdout;
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2816 2817 2818
  tee_fprintf(stdout, "PAGER set to stdout\n");
  return 0;
}
2819
#endif
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2820

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2821

2822
/*
2823
  Sorry, you can't send the result to an editor in Win32
2824 2825
*/

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2826
#ifdef USE_POPEN
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2827 2828 2829
static int
com_edit(String *buffer,char *line __attribute__((unused)))
{
2830
  char	filename[FN_REFLEN],buff[160];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2831 2832 2833
  int	fd,tmp;
  const char *editor;

2834 2835
  if ((fd=create_temp_file(filename,NullS,"sql", O_CREAT | O_WRONLY,
			   MYF(MY_WME))) < 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864
    goto err;
  if (buffer->is_empty() && !old_buffer.is_empty())
    (void) my_write(fd,(byte*) old_buffer.ptr(),old_buffer.length(),
		    MYF(MY_WME));
  else
    (void) my_write(fd,(byte*) buffer->ptr(),buffer->length(),MYF(MY_WME));
  (void) my_close(fd,MYF(0));

  if (!(editor = (char *)getenv("EDITOR")) &&
      !(editor = (char *)getenv("VISUAL")))
    editor = "vi";
  strxmov(buff,editor," ",filename,NullS);
  (void) system(buff);

  MY_STAT stat_arg;
  if (!my_stat(filename,&stat_arg,MYF(MY_WME)))
    goto err;
  if ((fd = my_open(filename,O_RDONLY, MYF(MY_WME))) < 0)
    goto err;
  (void) buffer->alloc((uint) stat_arg.st_size);
  if ((tmp=read(fd,(char*) buffer->ptr(),buffer->alloced_length())) >= 0L)
    buffer->length((uint) tmp);
  else
    buffer->length(0);
  (void) my_close(fd,MYF(0));
  (void) my_delete(filename,MYF(MY_WME));
err:
  return 0;
}
2865 2866
#endif

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2867 2868 2869 2870 2871 2872 2873

/* If arg is given, exit without errors. This happens on command 'quit' */

static int
com_quit(String *buffer __attribute__((unused)),
	 char *line __attribute__((unused)))
{
monty@mysql.com's avatar
monty@mysql.com committed
2874 2875
  /* let the screen auto close on a normal shutdown */
  NETWARE_SET_SCREEN_MODE(SCR_AUTOCLOSE_ON_EXIT);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2876 2877 2878 2879 2880 2881 2882 2883 2884
  status.exit_status=0;
  return 1;
}

static int
com_rehash(String *buffer __attribute__((unused)),
	 char *line __attribute__((unused)))
{
#ifdef HAVE_READLINE
2885
  build_completion_hash(1, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2886 2887 2888 2889
#endif
  return 0;
}

2890

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2891
#ifdef USE_POPEN
2892 2893 2894 2895
static int
com_shell(String *buffer, char *line __attribute__((unused)))
{
  char *shell_cmd;
2896 2897

  /* Skip space from line begin */
serg@serg.mylan's avatar
serg@serg.mylan committed
2898
  while (my_isspace(charset_info, *line))
2899
    line++;
2900 2901 2902 2903 2904
  if (!(shell_cmd = strchr(line, ' ')))
  {
    put_info("Usage: \\! shell-command", INFO_ERROR);
    return -1;
  }
2905 2906 2907 2908 2909
  /*
    The output of the shell command does not
    get directed to the pager or the outfile
  */
  if (system(shell_cmd) == -1)
2910 2911 2912 2913 2914 2915 2916 2917 2918
  {
    put_info(strerror(errno), INFO_ERROR, errno);
    return -1;
  }
  return 0;
}
#endif


bk@work.mysql.com's avatar
bk@work.mysql.com committed
2919 2920 2921
static int
com_print(String *buffer,char *line __attribute__((unused)))
{
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2922 2923
  tee_puts("--------------", stdout);
  (void) tee_fputs(buffer->c_ptr(), stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2924
  if (!buffer->length() || (*buffer)[buffer->length()-1] != '\n')
2925
    tee_putc('\n', stdout);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2926
  tee_puts("--------------\n", stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2927 2928 2929 2930 2931 2932 2933
  return 0;					/* If empty buffer */
}

	/* ARGSUSED */
static int
com_connect(String *buffer, char *line)
{
2934
  char *tmp, buff[256];
2935
  bool save_rehash= opt_rehash;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2936 2937
  int error;

2938
  bzero(buff, sizeof(buff));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2939 2940
  if (buffer)
  {
2941 2942 2943 2944 2945
    /*
      Two null bytes are needed in the end of buff to allow
      get_arg to find end of string the second time it's called.
    */
    strmake(buff, line, sizeof(buff)-2);
2946 2947
    tmp= get_arg(buff, 0);
    if (tmp && *tmp)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2948
    {
2949 2950 2951 2952
      my_free(current_db, MYF(MY_ALLOW_ZERO_PTR));
      current_db= my_strdup(tmp, MYF(MY_WME));
      tmp= get_arg(buff, 1);
      if (tmp)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2953 2954 2955 2956 2957 2958
      {
	my_free(current_host,MYF(MY_ALLOW_ZERO_PTR));
	current_host=my_strdup(tmp,MYF(MY_WME));
      }
    }
    else
2959 2960 2961 2962
    {
      /* Quick re-connect */
      opt_rehash= 0;                            /* purecov: tested */
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2963 2964 2965
    buffer->length(0);				// command used
  }
  else
2966
    opt_rehash= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2967
  error=sql_connect(current_host,current_db,current_user,opt_password,0);
2968
  opt_rehash= save_rehash;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2969 2970 2971

  if (connected)
  {
2972
    sprintf(buff,"Connection id:    %lu",mysql_thread_id(&mysql));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2973
    put_info(buff,INFO_INFO);
2974
    sprintf(buff,"Current database: %.128s\n",
2975
	    current_db ? current_db : "*** NONE ***");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990
    put_info(buff,INFO_INFO);
  }
  return error;
}


static int com_source(String *buffer, char *line)
{
  char source_name[FN_REFLEN], *end, *param;
  LINE_BUFFER *line_buff;
  int error;
  STATUS old_status;
  FILE *sql_file;

  /* Skip space from file name */
2991
  while (my_isspace(charset_info,*line))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2992
    line++;
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2993 2994 2995
  if (!(param = strchr(line, ' ')))		// Skip command name
    return put_info("Usage: \\. <filename> | source <filename>", 
		    INFO_ERROR, 0);
2996
  while (my_isspace(charset_info,*param))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2997 2998
    param++;
  end=strmake(source_name,param,sizeof(source_name)-1);
2999 3000
  while (end > source_name && (my_isspace(charset_info,end[-1]) || 
                               my_iscntrl(charset_info,end[-1])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3001 3002
    end--;
  end[0]=0;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3003
  unpack_filename(source_name,source_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3004
  /* open file name */
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3005
  if (!(sql_file = my_fopen(source_name, O_RDONLY | O_BINARY,MYF(0))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3006 3007 3008 3009 3010 3011
  {
    char buff[FN_REFLEN+60];
    sprintf(buff,"Failed to open file '%s', error: %d", source_name,errno);
    return put_info(buff, INFO_ERROR, 0);
  }

3012
  if (!(line_buff=batch_readline_init(opt_max_allowed_packet+512,sql_file)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025
  {
    my_fclose(sql_file,MYF(0));
    return put_info("Can't initialize batch_readline", INFO_ERROR, 0);
  }

  /* Save old status */
  old_status=status;
  bfill((char*) &status,sizeof(status),(char) 0);

  status.batch=old_status.batch;		// Run in batch mode
  status.line_buff=line_buff;
  status.file_name=source_name;
  glob_buffer.length(0);			// Empty command buffer
3026
  error= read_and_execute(false);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3027 3028 3029 3030 3031 3032 3033
  status=old_status;				// Continue as before
  my_fclose(sql_file,MYF(0));
  batch_readline_end(line_buff);
  return error;
}


3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048
	/* ARGSUSED */
static int
com_delimiter(String *buffer __attribute__((unused)), char *line)
{
  char buff[256], *tmp;

  strmake(buff, line, sizeof(buff) - 1);
  tmp= get_arg(buff, 0);

  if (!tmp || !*tmp)
  {
    put_info("DELIMITER must be followed by a 'delimiter' character or string",
	     INFO_ERROR);
    return 0;
  }
3049 3050 3051 3052 3053 3054 3055 3056
  else
  {
    if (strstr(tmp, "\\")) 
    {
      put_info("DELIMITER cannot contain a backslash character", INFO_ERROR);
      return 0;
    }
  }
3057
  strmake(delimiter, tmp, sizeof(delimiter) - 1);
3058
  delimiter_length= (int)strlen(delimiter);
3059
  delimiter_str= delimiter;
3060 3061 3062
  return 0;
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3063 3064 3065 3066
	/* ARGSUSED */
static int
com_use(String *buffer __attribute__((unused)), char *line)
{
3067
  char *tmp, buff[FN_REFLEN + 1];
3068
  int select_db;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3069

3070
  bzero(buff, sizeof(buff));
3071
  strmake(buff, line, sizeof(buff) - 1);
3072
  tmp= get_arg(buff, 0);
3073
  if (!tmp || !*tmp)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3074
  {
3075
    put_info("USE must be followed by a database name", INFO_ERROR);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3076 3077
    return 0;
  }
3078 3079 3080 3081
  /*
    We need to recheck the current database, because it may change
    under our feet, for example if DROP DATABASE or RENAME DATABASE
    (latter one not yet available by the time the comment was written)
3082
  */
3083
  get_current_db();
3084

serg@serg.mylan's avatar
serg@serg.mylan committed
3085
  if (!current_db || cmp_database(charset_info, current_db,tmp))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3086 3087
  {
    if (one_database)
3088
    {
3089
      skip_updates= 1;
3090 3091
      select_db= 0;    // don't do mysql_select_db()
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3092
    else
3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114
      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())
3115
      return opt_reconnect ? -1 : 1;                        // Fatal error
3116 3117 3118 3119
    if (mysql_select_db(&mysql,tmp))
    {
      if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR)
        return put_error(&mysql);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3120

3121
      if (reconnect())
3122
        return opt_reconnect ? -1 : 1;                      // Fatal error
3123 3124 3125 3126 3127
      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));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3128
#ifdef HAVE_READLINE
3129
    if (select_db > 1)
3130
      build_completion_hash(opt_rehash, 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3131 3132
#endif
  }
3133

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3134 3135 3136 3137
  put_info("Database changed",INFO_INFO);
  return 0;
}

3138 3139 3140 3141 3142
static int
com_warnings(String *buffer __attribute__((unused)),
   char *line __attribute__((unused)))
{
  show_warnings = 1;
3143
  put_info("Show warnings enabled.",INFO_INFO);
3144 3145 3146 3147 3148 3149 3150 3151
  return 0;
}

static int
com_nowarnings(String *buffer __attribute__((unused)),
   char *line __attribute__((unused)))
{
  show_warnings = 0;
3152
  put_info("Show warnings disabled.",INFO_INFO);
3153 3154
  return 0;
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3155

3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166
/*
  Gets argument from a command on the command line. If get_next_arg is
  not defined, skips the command and returns the first argument. The
  line is modified by adding zero to the end of the argument. If
  get_next_arg is defined, then the function searches for end of string
  first, after found, returns the next argument and adds zero to the
  end. If you ever wish to use this feature, remember to initialize all
  items in the array to zero first.
*/

char *get_arg(char *line, my_bool get_next_arg)
3167
{
3168
  char *ptr, *start;
3169
  my_bool quoted= 0, valid_arg= 0;
3170
  char qtype= 0;
3171 3172

  ptr= line;
3173 3174
  if (get_next_arg)
  {
3175 3176
    for (; *ptr; ptr++) ;
    if (*(ptr + 1))
3177 3178 3179 3180 3181
      ptr++;
  }
  else
  {
    /* skip leading white spaces */
3182
    while (my_isspace(charset_info, *ptr))
3183 3184 3185
      ptr++;
    if (*ptr == '\\') // short command was used
      ptr+= 2;
3186 3187 3188
    else
      while (*ptr &&!my_isspace(charset_info, *ptr)) // skip command
        ptr++;
3189
  }
3190 3191
  if (!*ptr)
    return NullS;
3192
  while (my_isspace(charset_info, *ptr))
3193
    ptr++;
3194
  if (*ptr == '\'' || *ptr == '\"' || *ptr == '`')
3195
  {
3196
    qtype= *ptr;
3197 3198 3199
    quoted= 1;
    ptr++;
  }
3200
  for (start=ptr ; *ptr; ptr++)
3201
  {
3202
    if (*ptr == '\\' && ptr[1]) // escaped character
3203
    {
3204 3205
      // Remove the backslash
      strmov(ptr, ptr+1);
3206
    }
3207
    else if ((!quoted && *ptr == ' ') || (quoted && *ptr == qtype))
3208 3209 3210 3211 3212
    {
      *ptr= 0;
      break;
    }
  }
3213 3214
  valid_arg= ptr != start;
  return valid_arg ? start : NullS;
3215 3216 3217
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
3218 3219 3220 3221
static int
sql_real_connect(char *host,char *database,char *user,char *password,
		 uint silent)
{
monty@mysql.com's avatar
monty@mysql.com committed
3222 3223 3224 3225 3226
  if (connected)
  {
    connected= 0;
    mysql_close(&mysql);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3227
  mysql_init(&mysql);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3228
  if (opt_connect_timeout)
3229 3230
  {
    uint timeout=opt_connect_timeout;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3231
    mysql_options(&mysql,MYSQL_OPT_CONNECT_TIMEOUT,
3232 3233
		  (char*) &timeout);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3234 3235
  if (opt_compress)
    mysql_options(&mysql,MYSQL_OPT_COMPRESS,NullS);
3236 3237
  if (opt_secure_auth)
    mysql_options(&mysql, MYSQL_SECURE_AUTH, (char *) &opt_secure_auth);
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3238 3239
  if (using_opt_local_infile)
    mysql_options(&mysql,MYSQL_OPT_LOCAL_INFILE, (char*) &opt_local_infile);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3240 3241 3242
#ifdef HAVE_OPENSSL
  if (opt_use_ssl)
    mysql_ssl_set(&mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
3243
		  opt_ssl_capath, opt_ssl_cipher);
3244 3245
  mysql_options(&mysql,MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
                (char*)&opt_ssl_verify_server_cert);
3246 3247 3248 3249 3250 3251
#endif
  if (opt_protocol)
    mysql_options(&mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
#ifdef HAVE_SMEM
  if (shared_memory_base_name)
    mysql_options(&mysql,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3252 3253 3254 3255 3256 3257 3258 3259 3260
#endif
  if (safe_updates)
  {
    char init_command[100];
    sprintf(init_command,
	    "SET SQL_SAFE_UPDATES=1,SQL_SELECT_LIMIT=%lu,SQL_MAX_JOIN_SIZE=%lu",
	    select_limit,max_join_size);
    mysql_options(&mysql, MYSQL_INIT_COMMAND, init_command);
  }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3261 3262
  if (default_charset_used)
    mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, default_charset);
3263 3264
  if (!mysql_real_connect(&mysql, host, user, password,
			  database, opt_mysql_port, opt_mysql_unix_port,
3265
			  connect_flag | CLIENT_MULTI_STATEMENTS))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3266 3267 3268 3269 3270
  {
    if (!silent ||
	(mysql_errno(&mysql) != CR_CONN_HOST_ERROR &&
	 mysql_errno(&mysql) != CR_CONNECTION_ERROR))
    {
3271
      (void) put_error(&mysql);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3272 3273 3274 3275 3276 3277
      (void) fflush(stdout);
      return ignore_errors ? -1 : 1;		// Abort
    }
    return -1;					// Retryable
  }
  connected=1;
3278
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3279
  mysql.reconnect=info_flag ? 1 : 0; // We want to know if this happens
3280 3281
#else
  mysql.reconnect= 1;
3282
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3283
#ifdef HAVE_READLINE
3284
  build_completion_hash(opt_rehash, 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301
#endif
  return 0;
}


static int
sql_connect(char *host,char *database,char *user,char *password,uint silent)
{
  bool message=0;
  uint count=0;
  int error;
  for (;;)
  {
    if ((error=sql_real_connect(host,database,user,password,wait_flag)) >= 0)
    {
      if (count)
      {
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3302
	tee_fputs("\n", stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3303 3304 3305 3306 3307 3308 3309 3310 3311
	(void) fflush(stderr);
      }
      return error;
    }
    if (!wait_flag)
      return ignore_errors ? -1 : 1;
    if (!message && !silent)
    {
      message=1;
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3312
      tee_fputs("Waiting",stderr); (void) fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3313
    }
3314
    (void) sleep(wait_time);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328
    if (!silent)
    {
      putc('.',stderr); (void) fflush(stderr);
      count++;
    }
  }
}



static int
com_status(String *buffer __attribute__((unused)),
	   char *line __attribute__((unused)))
{
3329 3330
  const char *status_str;
  char buff[40];
3331
  ulonglong id;
bar@mysql.com's avatar
bar@mysql.com committed
3332 3333
  MYSQL_RES *result;
  LINT_INIT(result);
3334

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3335
  tee_puts("--------------", stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3336 3337 3338
  usage(1);					/* Print version */
  if (connected)
  {
3339
    tee_fprintf(stdout, "\nConnection id:\t\t%lu\n",mysql_thread_id(&mysql));
bar@mysql.com's avatar
bar@mysql.com committed
3340 3341 3342 3343
    /* 
      Don't remove "limit 1", 
      it is protection againts SQL_SELECT_LIMIT=0
    */
3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354
    if (!mysql_query(&mysql,"select DATABASE(), USER() limit 1") &&
	(result=mysql_use_result(&mysql)))
    {
      MYSQL_ROW cur=mysql_fetch_row(result);
      if (cur)
      {
        tee_fprintf(stdout, "Current database:\t%s\n", cur[0] ? cur[0] : "");
        tee_fprintf(stdout, "Current user:\t\t%s\n", cur[1]);
      }
      mysql_free_result(result);
    } 
3355
#ifdef HAVE_OPENSSL
3356
    if ((status_str= mysql_get_ssl_cipher(&mysql)))
3357
      tee_fprintf(stdout, "SSL:\t\t\tCipher in use is %s\n",
3358
		  status_str);
3359 3360
    else
#endif /* HAVE_OPENSSL */
3361
      tee_puts("SSL:\t\t\tNot in use", stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3362 3363 3364 3365
  }
  else
  {
    vidattr(A_BOLD);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3366
    tee_fprintf(stdout, "\nNo connection\n");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3367 3368 3369 3370 3371 3372
    vidattr(A_NORMAL);
    return 0;
  }
  if (skip_updates)
  {
    vidattr(A_BOLD);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3373
    tee_fprintf(stdout, "\nAll updates ignored to this database\n");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3374 3375
    vidattr(A_NORMAL);
  }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3376
#ifdef USE_POPEN
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3377
  tee_fprintf(stdout, "Current pager:\t\t%s\n", pager);
3378
  tee_fprintf(stdout, "Using outfile:\t\t'%s'\n", opt_outfile ? outfile : "");
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3379
#endif
3380
  tee_fprintf(stdout, "Using delimiter:\t%s\n", delimiter);
3381
  tee_fprintf(stdout, "Server version:\t\t%s\n", server_version_string(&mysql));
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3382 3383
  tee_fprintf(stdout, "Protocol version:\t%d\n", mysql_get_proto_info(&mysql));
  tee_fprintf(stdout, "Connection:\t\t%s\n", mysql_get_host_info(&mysql));
3384 3385 3386
  if ((id= mysql_insert_id(&mysql)))
    tee_fprintf(stdout, "Insert id:\t\t%s\n", llstr(id, buff));

3387
  /* "limit 1" is protection against SQL_SELECT_LIMIT=0 */
bar@mysql.com's avatar
bar@mysql.com committed
3388
  if (!mysql_query(&mysql,"select @@character_set_client, @@character_set_connection, @@character_set_server, @@character_set_database limit 1") &&
bar@mysql.com's avatar
bar@mysql.com committed
3389 3390 3391 3392 3393
      (result=mysql_use_result(&mysql)))
  {
    MYSQL_ROW cur=mysql_fetch_row(result);
    if (cur)
    {
3394
      tee_fprintf(stdout, "Server characterset:\t%s\n", cur[2] ? cur[2] : "");
bar@mysql.com's avatar
bar@mysql.com committed
3395
      tee_fprintf(stdout, "Db     characterset:\t%s\n", cur[3] ? cur[3] : "");
3396
      tee_fprintf(stdout, "Client characterset:\t%s\n", cur[0] ? cur[0] : "");
bar@mysql.com's avatar
bar@mysql.com committed
3397 3398 3399 3400
      tee_fprintf(stdout, "Conn.  characterset:\t%s\n", cur[1] ? cur[1] : "");
    }
    mysql_free_result(result);
  }
3401 3402 3403 3404 3405 3406
  else
  {
    /* Probably pre-4.1 server */
    tee_fprintf(stdout, "Client characterset:\t%s\n", charset_info->csname);
    tee_fprintf(stdout, "Server characterset:\t%s\n", mysql.charset->csname);
  }
bar@mysql.com's avatar
bar@mysql.com committed
3407

3408
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3409
  if (strstr(mysql_get_host_info(&mysql),"TCP/IP") || ! mysql.unix_socket)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3410
    tee_fprintf(stdout, "TCP port:\t\t%d\n", mysql.port);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3411
  else
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3412
    tee_fprintf(stdout, "UNIX socket:\t\t%s\n", mysql.unix_socket);
3413 3414
  if (mysql.net.compress)
    tee_fprintf(stdout, "Protocol:\t\tCompressed\n");
3415
#endif
3416

3417
  if ((status_str= mysql_stat(&mysql)) && !mysql_error(&mysql)[0])
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3418 3419
  {
    ulong sec;
3420
    const char *pos= strchr(status_str,' ');
3421
    /* print label */
3422 3423
    tee_fprintf(stdout, "%.*s\t\t\t", (int) (pos-status_str), status_str);
    if ((status_str= str2int(pos,10,0,LONG_MAX,(long*) &sec)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3424 3425
    {
      nice_time((double) sec,buff,0);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3426
      tee_puts(buff, stdout);			/* print nice time */
3427 3428
      while (*status_str == ' ')
        status_str++;  /* to next info */
3429
      tee_putc('\n', stdout);
3430
      tee_puts(status_str, stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3431 3432 3433 3434 3435
    }
  }
  if (safe_updates)
  {
    vidattr(A_BOLD);
jcole@tetra.spaceapes.com's avatar
jcole@tetra.spaceapes.com committed
3436
    tee_fprintf(stdout, "\nNote that you are running in safe_update_mode:\n");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3437
    vidattr(A_NORMAL);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3438
    tee_fprintf(stdout, "\
jcole@tetra.spaceapes.com's avatar
jcole@tetra.spaceapes.com committed
3439 3440 3441
UPDATEs and DELETEs that don't use a key in the WHERE clause are not allowed.\n\
(One can force an UPDATE/DELETE by adding LIMIT # at the end of the command.)\n\
SELECT has an automatic 'LIMIT %lu' if LIMIT is not used.\n\
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3442
Max number of examined row combination in a join is set to: %lu\n\n",
jcole@tetra.spaceapes.com's avatar
jcole@tetra.spaceapes.com committed
3443
select_limit, max_join_size);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3444
  }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3445
  tee_puts("--------------\n", stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3446 3447 3448
  return 0;
}

3449
static const char *
3450
server_version_string(MYSQL *con)
3451 3452 3453 3454 3455 3456 3457 3458 3459
{
  static char buf[MAX_SERVER_VERSION_LENGTH] = "";

  /* Only one thread calls this, so no synchronization is needed */
  if (buf[0] == '\0')
  {
    char *bufp = buf;
    MYSQL_RES *result;

3460
    bufp= strnmov(buf, mysql_get_server_info(con), sizeof buf);
3461 3462

    /* "limit 1" is protection against SQL_SELECT_LIMIT=0 */
3463 3464
    if (!mysql_query(con, "select @@version_comment limit 1") &&
        (result = mysql_use_result(con)))
3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480
    {
      MYSQL_ROW cur = mysql_fetch_row(result);
      if (cur && cur[0])
      {
        bufp = strxnmov(bufp, sizeof buf - (bufp - buf), " ", cur[0], NullS);
      }
      mysql_free_result(result);
    }

    /* str*nmov doesn't guarantee NUL-termination */
    if (bufp == buf + sizeof buf)
      buf[sizeof buf - 1] = '\0';
  }

  return buf;
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3481 3482

static int
3483
put_info(const char *str,INFO_TYPE info_type, uint error, const char *sqlstate)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3484
{
3485
  FILE *file= (info_type == INFO_ERROR ? stderr : stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3486
  static int inited=0;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3487

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3488 3489 3490 3491
  if (status.batch)
  {
    if (info_type == INFO_ERROR)
    {
3492 3493
      (void) fflush(file);
      fprintf(file,"ERROR");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3494
      if (error)
paul@kite-hub.kitebird.com's avatar
paul@kite-hub.kitebird.com committed
3495 3496 3497 3498 3499 3500
      {
	if (sqlstate)
	  (void) fprintf(file," %d (%s)",error, sqlstate);
        else
	  (void) fprintf(file," %d",error);
      }
3501
      if (status.query_start_line && line_numbers)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3502
      {
3503
	(void) fprintf(file," at line %lu",status.query_start_line);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3504
	if (status.file_name)
3505
	  (void) fprintf(file," in file: '%s'", status.file_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3506
      }
3507 3508
      (void) fprintf(file,": %s\n",str);
      (void) fflush(file);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3509 3510 3511 3512
      if (!ignore_errors)
	return 1;
    }
    else if (info_type == INFO_RESULT && verbose > 1)
3513
      tee_puts(str, file);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3514
    if (unbuffered)
3515
      fflush(file);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528
    return info_type == INFO_ERROR ? -1 : 0;
  }
  if (!opt_silent || info_type == INFO_ERROR)
  {
    if (!inited)
    {
      inited=1;
#ifdef HAVE_SETUPTERM
      (void) setupterm((char *)0, 1, (int *) 0);
#endif
    }
    if (info_type == INFO_ERROR)
    {
3529
      if (!opt_nobeep)
3530
        putchar('\a');		      	/* This should make a bell */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3531 3532
      vidattr(A_STANDOUT);
      if (error)
3533 3534 3535 3536 3537 3538
      {
	if (sqlstate)
          (void) tee_fprintf(file, "ERROR %d (%s): ", error, sqlstate);
        else
          (void) tee_fprintf(file, "ERROR %d: ", error);
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3539
      else
3540
        tee_puts("ERROR: ", file);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3541 3542 3543
    }
    else
      vidattr(A_BOLD);
3544
    (void) tee_puts(str, file);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3545 3546 3547
    vidattr(A_NORMAL);
  }
  if (unbuffered)
3548
    fflush(file);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3549 3550 3551
  return info_type == INFO_ERROR ? -1 : 0;
}

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3552

3553
static int
3554
put_error(MYSQL *con)
3555
{
3556 3557
  return put_info(mysql_error(con), INFO_ERROR, mysql_errno(con),
		  mysql_sqlstate(con));
3558 3559 3560
}  


bk@work.mysql.com's avatar
bk@work.mysql.com committed
3561 3562 3563 3564
static void remove_cntrl(String &buffer)
{
  char *start,*end;
  end=(start=(char*) buffer.ptr())+buffer.length();
3565
  while (start < end && !my_isgraph(charset_info,end[-1]))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3566 3567 3568 3569 3570
    end--;
  buffer.length((uint) (end-start));
}


jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3571 3572 3573 3574
void tee_fprintf(FILE *file, const char *fmt, ...)
{
  va_list args;

monty@mysql.com's avatar
monty@mysql.com committed
3575
  NETWARE_YIELD;
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3576
  va_start(args, fmt);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3577
  (void) vfprintf(file, fmt, args);
3578 3579
  va_end(args);

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3580
  if (opt_outfile)
3581 3582
  {
    va_start(args, fmt);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3583
    (void) vfprintf(OUTFILE, fmt, args);
3584 3585
    va_end(args);
  }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3586 3587 3588 3589 3590
}


void tee_fputs(const char *s, FILE *file)
{
monty@mysql.com's avatar
monty@mysql.com committed
3591
  NETWARE_YIELD;
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3592 3593 3594 3595 3596 3597 3598 3599
  fputs(s, file);
  if (opt_outfile)
    fputs(s, OUTFILE);
}


void tee_puts(const char *s, FILE *file)
{
monty@mysql.com's avatar
monty@mysql.com committed
3600
  NETWARE_YIELD;
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3601
  fputs(s, file);
3602
  fputc('\n', file);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3603 3604 3605
  if (opt_outfile)
  {
    fputs(s, OUTFILE);
3606
    fputc('\n', OUTFILE);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3607 3608 3609
  }
}

3610 3611 3612 3613 3614 3615 3616
void tee_putc(int c, FILE *file)
{
  putc(c, file);
  if (opt_outfile)
    putc(c, OUTFILE);
}

3617
#if defined( __WIN__) || defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3618 3619 3620
#include <time.h>
#else
#include <sys/times.h>
3621
#ifdef _SC_CLK_TCK				// For mit-pthreads
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3622 3623 3624
#undef CLOCKS_PER_SEC
#define CLOCKS_PER_SEC (sysconf(_SC_CLK_TCK))
#endif
3625
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3626 3627 3628

static ulong start_timer(void)
{
3629
#if defined( __WIN__) || defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644
 return clock();
#else
  struct tms tms_tmp;
  return times(&tms_tmp);
#endif
}


static void nice_time(double sec,char *buff,bool part_second)
{
  ulong tmp;
  if (sec >= 3600.0*24)
  {
    tmp=(ulong) floor(sec/(3600.0*24));
    sec-=3600.0*24*tmp;
3645
    buff=int10_to_str((long) tmp, buff, 10);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3646 3647 3648 3649 3650 3651
    buff=strmov(buff,tmp > 1 ? " days " : " day ");
  }
  if (sec >= 3600.0)
  {
    tmp=(ulong) floor(sec/3600.0);
    sec-=3600.0*tmp;
3652
    buff=int10_to_str((long) tmp, buff, 10);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3653 3654 3655 3656 3657 3658
    buff=strmov(buff,tmp > 1 ? " hours " : " hour ");
  }
  if (sec >= 60.0)
  {
    tmp=(ulong) floor(sec/60.0);
    sec-=60.0*tmp;
3659
    buff=int10_to_str((long) tmp, buff, 10);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683
    buff=strmov(buff," min ");
  }
  if (part_second)
    sprintf(buff,"%.2f sec",sec);
  else
    sprintf(buff,"%d sec",(int) sec);
}


static void end_timer(ulong start_time,char *buff)
{
  nice_time((double) (start_timer() - start_time) /
	    CLOCKS_PER_SEC,buff,1);
}


static void mysql_end_timer(ulong start_time,char *buff)
{
  buff[0]=' ';
  buff[1]='(';
  end_timer(start_time,buff+2);
  strmov(strend(buff),")");
}

3684 3685
static const char* construct_prompt()
{
3686 3687
  processed_prompt.free();			// Erase the old prompt
  time_t  lclock = time(NULL);			// Get the date struct
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3688
  struct tm *t = localtime(&lclock);
3689 3690

  /* parse thru the settings for the prompt */
3691 3692 3693
  for (char *c = current_prompt; *c ; *c++)
  {
    if (*c != PROMPT_CHAR)
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3694
	processed_prompt.append(*c);
3695 3696
    else
    {
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3697 3698
      switch (*++c) {
      case '\0':
3699
	c--;			// stop it from going beyond if ends with %
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3700 3701 3702 3703 3704
	break;
      case 'c':
	add_int_to_prompt(++prompt_counter);
	break;
      case 'v':
3705 3706 3707 3708
	if (connected)
	  processed_prompt.append(mysql_get_server_info(&mysql));
	else
	  processed_prompt.append("not_connected");
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3709 3710 3711 3712 3713 3714
	break;
      case 'd':
	processed_prompt.append(current_db ? current_db : "(none)");
	break;
      case 'h':
      {
3715 3716
	const char *prompt;
	prompt= connected ? mysql_get_host_info(&mysql) : "not_connected";
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3717 3718 3719 3720 3721 3722 3723 3724 3725 3726
	if (strstr(prompt, "Localhost"))
	  processed_prompt.append("localhost");
	else
	{
	  const char *end=strcend(prompt,' ');
	  processed_prompt.append(prompt, (uint) (end-prompt));
	}
	break;
      }
      case 'p':
3727
      {
3728
#ifndef EMBEDDED_LIBRARY
3729 3730 3731 3732 3733
	if (!connected)
	{
	  processed_prompt.append("not_connected");
	  break;
	}
3734 3735 3736 3737 3738 3739 3740

	const char *host_info = mysql_get_host_info(&mysql);
	if (strstr(host_info, "memory")) 
	{
		processed_prompt.append( mysql.host );
	}
	else if (strstr(host_info,"TCP/IP") ||
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3741
	    !mysql.unix_socket)
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3742 3743
	  add_int_to_prompt(mysql.port);
	else
3744 3745
	{
	  char *pos=strrchr(mysql.unix_socket,'/');
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3746
 	  processed_prompt.append(pos ? pos+1 : mysql.unix_socket);
3747
	}
3748
#endif
3749
      }
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3750 3751 3752 3753
	break;
      case 'U':
	if (!full_username)
	  init_username();
3754 3755
        processed_prompt.append(full_username ? full_username :
                                (current_user ?  current_user : "(unknown)"));
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3756 3757 3758 3759
	break;
      case 'u':
	if (!full_username)
	  init_username();
3760 3761
        processed_prompt.append(part_username ? part_username :
                                (current_user ?  current_user : "(unknown)"));
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773
	break;
      case PROMPT_CHAR:
	processed_prompt.append(PROMPT_CHAR);
	break;
      case 'n':
	processed_prompt.append('\n');
	break;
      case ' ':
      case '_':
	processed_prompt.append(' ');
	break;
      case 'R':
3774 3775
	if (t->tm_hour < 10)
	  processed_prompt.append('0');
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3776 3777 3778 3779 3780 3781 3782
	add_int_to_prompt(t->tm_hour);
	break;
      case 'r':
	int getHour;
	getHour = t->tm_hour % 12;
	if (getHour == 0)
	  getHour=12;
3783 3784
	if (getHour < 10)
	  processed_prompt.append('0');
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807
	add_int_to_prompt(getHour);
	break;
      case 'm':
	if (t->tm_min < 10)
	  processed_prompt.append('0');
	add_int_to_prompt(t->tm_min);
	break;
      case 'y':
	int getYear;
	getYear = t->tm_year % 100;
	if (getYear < 10)
	  processed_prompt.append('0');
	add_int_to_prompt(getYear);
	break;
      case 'Y':
	add_int_to_prompt(t->tm_year+1900);
	break;
      case 'D':
	char* dateTime;
	dateTime = ctime(&lclock);
	processed_prompt.append(strtok(dateTime,"\n"));
	break;
      case 's':
3808 3809
	if (t->tm_sec < 10)
	  processed_prompt.append('0');
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835
	add_int_to_prompt(t->tm_sec);
	break;
      case 'w':
	processed_prompt.append(day_names[t->tm_wday]);
	break;
      case 'P':
	processed_prompt.append(t->tm_hour < 12 ? "am" : "pm");
	break;
      case 'o':
	add_int_to_prompt(t->tm_mon+1);
	break;
      case 'O':
	processed_prompt.append(month_names[t->tm_mon]);
	break;
      case '\'':
	processed_prompt.append("'");
	break;
      case '"':
	processed_prompt.append('"');
	break;
      case 'S':
	processed_prompt.append(';');
	break;
      case 't':
	processed_prompt.append('\t');
	break;
3836 3837 3838
      case 'l':
	processed_prompt.append(delimiter_str);
	break;
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3839 3840 3841 3842 3843 3844 3845 3846 3847
      default:
	processed_prompt.append(c);
      }
    }
  }
  processed_prompt.append('\0');
  return processed_prompt.ptr();
}

3848 3849 3850

static void add_int_to_prompt(int toadd)
{
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3851 3852 3853 3854 3855
  char buffer[16];
  int10_to_str(toadd,buffer,10);
  processed_prompt.append(buffer);
}

3856 3857
static void init_username()
{
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3858 3859 3860 3861 3862 3863 3864
  my_free(full_username,MYF(MY_ALLOW_ZERO_PTR));
  my_free(part_username,MYF(MY_ALLOW_ZERO_PTR));

  MYSQL_RES *result;
  LINT_INIT(result);
  if (!mysql_query(&mysql,"select USER()") &&
      (result=mysql_use_result(&mysql)))
3865 3866 3867 3868 3869 3870
  {
    MYSQL_ROW cur=mysql_fetch_row(result);
    full_username=my_strdup(cur[0],MYF(MY_WME));
    part_username=my_strdup(strtok(cur[0],"@"),MYF(MY_WME));
    (void) mysql_fetch_row(result);		// Read eof
  }
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3871 3872
}

3873 3874 3875
static int com_prompt(String *buffer, char *line)
{
  char *ptr=strchr(line, ' ');
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3876 3877
  prompt_counter = 0;
  my_free(current_prompt,MYF(MY_ALLOW_ZERO_PTR));
3878 3879
  current_prompt=my_strdup(ptr ? ptr+1 : default_prompt,MYF(MY_WME));
  if (!ptr)
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3880 3881 3882 3883 3884 3885
    tee_fprintf(stdout, "Returning to default PROMPT of %s\n", default_prompt);
  else
    tee_fprintf(stdout, "PROMPT set to '%s'\n", current_prompt);
  return 0;
}

3886
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897
/* Keep sql_string library happy */

gptr sql_alloc(unsigned int Size)
{
  return my_malloc(Size,MYF(MY_WME));
}

void sql_element_free(void *ptr)
{
  my_free((gptr) ptr,MYF(0));
}
3898
#endif /* EMBEDDED_LIBRARY */