~drizzle-trunk/drizzle/development

1 by brian
clean slate
1
/* Copyright (C) 2000-2008 MySQL AB
2
3
   This program is free software; you can redistribute it and/or modify
4
   it under the terms of the GNU General Public License as published by
5
   the Free Software Foundation; version 2 of the License.
6
7
   This program is distributed in the hope that it will be useful,
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
   GNU General Public License for more details.
11
12
   You should have received a copy of the GNU General Public License
13
   along with this program; if not, write to the Free Software
14
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
15
16
/* mysql command tool
17
 * Commands compatible with mSQL by David J. Hughes
18
 *
19
 * Written by:
20
 *   Michael 'Monty' Widenius
21
 *   Andi Gutmans  <andi@zend.com>
22
 *   Zeev Suraski  <zeev@zend.com>
23
 *   Jani Tolonen  <jani@mysql.com>
24
 *   Matt Wagner   <matt@mysql.com>
25
 *   Jeremy Cole   <jcole@mysql.com>
26
 *   Tonu Samuel   <tonu@mysql.com>
27
 *   Harrison Fisk <harrison@mysql.com>
28
 *
29
 **/
30
31
#include "client_priv.h"
32
#include <m_ctype.h>
33
#include <stdarg.h>
34
#include <my_dir.h>
35
#ifndef __GNU_LIBRARY__
36
#define __GNU_LIBRARY__		      // Skip warnings in getopt.h
37
#endif
38
#include "my_readline.h"
39
#include <signal.h>
40
#include <violite.h>
41
42
#if defined(USE_LIBEDIT_INTERFACE) && defined(HAVE_LOCALE_H)
43
#include <locale.h>
44
#endif
45
46
const char *VER= "14.14";
47
48
/* Don't try to make a nice table if the data is too big */
49
#define MAX_COLUMN_LENGTH	     1024
50
51
/* Buffer to hold 'version' and 'version_comment' */
52
#define MAX_SERVER_VERSION_LENGTH     128
53
54
/* Array of options to pass to libemysqld */
55
#define MAX_SERVER_ARGS               64
56
57
void* sql_alloc(unsigned size);	     // Don't use mysqld alloc for these
58
void sql_element_free(void *ptr);
59
#include "sql_string.h"
60
61
extern "C" {
62
#if defined(HAVE_CURSES_H) && defined(HAVE_TERM_H)
63
#include <curses.h>
64
#include <term.h>
65
#else
66
#if defined(HAVE_TERMIOS_H)
67
#include <termios.h>
68
#include <unistd.h>
69
#elif defined(HAVE_TERMBITS_H)
70
#include <termbits.h>
71
#elif defined(HAVE_ASM_TERMBITS_H) && (!defined __GLIBC__ || !(__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ > 0))
72
#include <asm/termbits.h>		// Standard linux
73
#endif
74
#undef VOID
75
#if defined(HAVE_TERMCAP_H)
76
#include <termcap.h>
77
#else
78
#ifdef HAVE_CURSES_H
79
#include <curses.h>
80
#endif
81
#undef SYSV				// hack to avoid syntax error
82
#ifdef HAVE_TERM_H
83
#include <term.h>
84
#endif
85
#endif
86
#endif
87
88
#undef bcmp				// Fix problem with new readline
89
90
#include <readline/readline.h>
91
#define HAVE_READLINE
92
93
  //int vidattr(long unsigned int attrs);	// Was missing in sun curses
94
}
95
96
#if !defined(HAVE_VIDATTR)
97
#undef vidattr
98
#define vidattr(A) {}			// Can't get this to work
99
#endif
100
101
#ifdef FN_NO_CASE_SENCE
102
#define cmp_database(cs,A,B) my_strcasecmp((cs), (A), (B))
103
#else
104
#define cmp_database(cs,A,B) strcmp((A),(B))
105
#endif
106
15 by brian
Fix for stat, NETWARE removal
107
#if !defined(THREAD)
1 by brian
clean slate
108
#define USE_POPEN
109
#endif
110
111
#include "completion_hash.h"
112
113
#define PROMPT_CHAR '\\'
114
#define DEFAULT_DELIMITER ";"
115
116
typedef struct st_status
117
{
118
  int exit_status;
119
  ulong query_start_line;
120
  char *file_name;
121
  LINE_BUFFER *line_buff;
122
  bool batch,add_to_history;
123
} STATUS;
124
125
126
static HashTable ht;
127
static char **defaults_argv;
128
129
enum enum_info_type { INFO_INFO,INFO_ERROR,INFO_RESULT};
130
typedef enum enum_info_type INFO_TYPE;
131
132
static MYSQL mysql;			/* The connection */
133
static my_bool ignore_errors=0,wait_flag=0,quick=0,
134
               connected=0,opt_raw_data=0,unbuffered=0,output_tables=0,
135
	       opt_rehash=1,skip_updates=0,safe_updates=0,one_database=0,
136
	       opt_compress=0, using_opt_local_infile=0,
137
	       vertical=0, line_numbers=1, column_names=1,opt_html=0,
138
               opt_xml=0,opt_nopager=1, opt_outfile=0, named_cmds= 0,
139
	       tty_password= 0, opt_nobeep=0, opt_reconnect=1,
140
	       default_charset_used= 0, opt_secure_auth= 0,
141
               default_pager_set= 0, opt_sigint_ignore= 0,
142
               auto_vertical_output= 0,
143
               show_warnings= 0, executing_query= 0, interrupted_query= 0;
144
static my_bool debug_info_flag, debug_check_flag;
145
static my_bool column_types_flag;
146
static my_bool preserve_comments= 0;
147
static ulong opt_max_allowed_packet, opt_net_buffer_length;
148
static uint verbose=0,opt_silent=0,opt_mysql_port=0, opt_local_infile=0;
149
static uint my_end_arg;
150
static char * opt_mysql_unix_port=0;
151
static int connect_flag=CLIENT_INTERACTIVE;
152
static char *current_host,*current_db,*current_user=0,*opt_password=0,
153
            *current_prompt=0, *delimiter_str= 0,
154
            *default_charset= (char*) MYSQL_DEFAULT_CHARSET_NAME;
155
static char *histfile;
156
static char *histfile_tmp;
157
static String glob_buffer,old_buffer;
158
static String processed_prompt;
159
static char *full_username=0,*part_username=0,*default_prompt=0;
160
static int wait_time = 5;
161
static STATUS status;
162
static ulong select_limit,max_join_size,opt_connect_timeout=0;
163
static char mysql_charsets_dir[FN_REFLEN+1];
164
static const char *xmlmeta[] = {
165
  "&", "&amp;",
166
  "<", "&lt;",
167
  ">", "&gt;",
168
  "\"", "&quot;",
169
  0, 0
170
};
171
static const char *day_names[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
172
static const char *month_names[]={"Jan","Feb","Mar","Apr","May","Jun","Jul",
173
			    "Aug","Sep","Oct","Nov","Dec"};
174
static char default_pager[FN_REFLEN];
175
static char pager[FN_REFLEN], outfile[FN_REFLEN];
176
static FILE *PAGER, *OUTFILE;
177
static MEM_ROOT hash_mem_root;
178
static uint prompt_counter;
179
static char delimiter[16]= DEFAULT_DELIMITER;
180
static uint delimiter_length= 1;
181
unsigned short terminal_width= 80;
182
183
#ifdef HAVE_SMEM
184
static char *shared_memory_base_name=0;
185
#endif
186
static uint opt_protocol= MYSQL_PROTOCOL_TCP;
187
static CHARSET_INFO *charset_info= &my_charset_latin1;
188
189
const char *default_dbug_option="d:t:o,/tmp/mysql.trace";
190
191
void tee_fprintf(FILE *file, const char *fmt, ...);
192
void tee_fputs(const char *s, FILE *file);
193
void tee_puts(const char *s, FILE *file);
194
void tee_putc(int c, FILE *file);
195
static void tee_print_sized_data(const char *, unsigned int, unsigned int, bool);
196
/* The names of functions that actually do the manipulation. */
197
static int get_options(int argc,char **argv);
198
extern "C" my_bool get_one_option(int optid, const struct my_option *opt,
199
                                  char *argument);
200
static int com_quit(String *str,char*),
201
	   com_go(String *str,char*), com_ego(String *str,char*),
202
	   com_print(String *str,char*),
203
	   com_help(String *str,char*), com_clear(String *str,char*),
204
	   com_connect(String *str,char*), com_status(String *str,char*),
205
	   com_use(String *str,char*), com_source(String *str, char*),
206
	   com_rehash(String *str, char*), com_tee(String *str, char*),
207
           com_notee(String *str, char*), com_charset(String *str,char*),
208
           com_prompt(String *str, char*), com_delimiter(String *str, char*),
209
     com_warnings(String *str, char*), com_nowarnings(String *str, char*);
210
211
#ifdef USE_POPEN
212
static int com_nopager(String *str, char*), com_pager(String *str, char*),
213
           com_edit(String *str,char*), com_shell(String *str, char *);
214
#endif
215
216
static int read_and_execute(bool interactive);
217
static int sql_connect(char *host,char *database,char *user,char *password,
218
		       uint silent);
219
static const char *server_version_string(MYSQL *mysql);
220
static int put_info(const char *str,INFO_TYPE info,uint error=0,
221
		    const char *sql_state=0);
222
static int put_error(MYSQL *mysql);
223
static void safe_put_field(const char *pos,ulong length);
224
static void xmlencode_print(const char *src, uint length);
225
static void init_pager();
226
static void end_pager();
227
static void init_tee(const char *);
228
static void end_tee();
229
static const char* construct_prompt();
230
static char *get_arg(char *line, my_bool get_next_arg);
231
static void init_username();
232
static void add_int_to_prompt(int toadd);
233
static int get_result_width(MYSQL_RES *res);
234
static int get_field_disp_length(MYSQL_FIELD * field);
235
236
/* A structure which contains information on the commands this program
237
   can understand. */
238
239
typedef struct {
240
  const char *name;		/* User printable name of the function. */
241
  char cmd_char;		/* msql command character */
242
  int (*func)(String *str,char *); /* Function to call to do the job. */
243
  bool takes_params;		/* Max parameters for command */
244
  const char *doc;		/* Documentation for this function.  */
245
} COMMANDS;
246
247
static COMMANDS commands[] = {
248
  { "?",      '?', com_help,   1, "Synonym for `help'." },
249
  { "clear",  'c', com_clear,  0, "Clear command."},
250
  { "connect",'r', com_connect,1,
251
    "Reconnect to the server. Optional arguments are db and host." },
252
  { "delimiter", 'd', com_delimiter,    1,
253
    "Set statement delimiter. NOTE: Takes the rest of the line as new delimiter." },
254
#ifdef USE_POPEN
255
  { "edit",   'e', com_edit,   0, "Edit command with $EDITOR."},
256
#endif
257
  { "ego",    'G', com_ego,    0,
258
    "Send command to mysql server, display result vertically."},
259
  { "exit",   'q', com_quit,   0, "Exit mysql. Same as quit."},
260
  { "go",     'g', com_go,     0, "Send command to mysql server." },
261
  { "help",   'h', com_help,   1, "Display this help." },
262
#ifdef USE_POPEN
263
  { "nopager",'n', com_nopager,0, "Disable pager, print to stdout." },
264
#endif
265
  { "notee",  't', com_notee,  0, "Don't write into outfile." },
266
#ifdef USE_POPEN
267
  { "pager",  'P', com_pager,  1, 
268
    "Set PAGER [to_pager]. Print the query results via PAGER." },
269
#endif
270
  { "print",  'p', com_print,  0, "Print current command." },
271
  { "prompt", 'R', com_prompt, 1, "Change your mysql prompt."},
272
  { "quit",   'q', com_quit,   0, "Quit mysql." },
273
  { "rehash", '#', com_rehash, 0, "Rebuild completion hash." },
274
  { "source", '.', com_source, 1,
275
    "Execute an SQL script file. Takes a file name as an argument."},
276
  { "status", 's', com_status, 0, "Get status information from the server."},
277
#ifdef USE_POPEN
278
  { "system", '!', com_shell,  1, "Execute a system shell command."},
279
#endif
280
  { "tee",    'T', com_tee,    1, 
281
    "Set outfile [to_outfile]. Append everything into given outfile." },
282
  { "use",    'u', com_use,    1,
283
    "Use another database. Takes database name as argument." },
284
  { "charset",    'C', com_charset,    1,
285
    "Switch to another charset. Might be needed for processing binlog with multi-byte charsets." },
286
  { "warnings", 'W', com_warnings,  0,
287
    "Show warnings after every statement." },
288
  { "nowarning", 'w', com_nowarnings, 0,
289
    "Don't show warnings after every statement." },
290
  /* Get bash-like expansion for some commands */
291
  { "create table",     0, 0, 0, ""},
292
  { "create database",  0, 0, 0, ""},
293
  { "show databases",   0, 0, 0, ""},
294
  { "show fields from", 0, 0, 0, ""},
295
  { "show keys from",   0, 0, 0, ""},
296
  { "show tables",      0, 0, 0, ""},
297
  { "load data from",   0, 0, 0, ""},
298
  { "alter table",      0, 0, 0, ""},
299
  { "set option",       0, 0, 0, ""},
300
  { "lock tables",      0, 0, 0, ""},
301
  { "unlock tables",    0, 0, 0, ""},
302
  /* generated 2006-12-28.  Refresh occasionally from lexer. */
303
  { "ACTION", 0, 0, 0, ""},
304
  { "ADD", 0, 0, 0, ""},
305
  { "AFTER", 0, 0, 0, ""},
306
  { "AGAINST", 0, 0, 0, ""},
307
  { "AGGREGATE", 0, 0, 0, ""},
308
  { "ALL", 0, 0, 0, ""},
309
  { "ALGORITHM", 0, 0, 0, ""},
310
  { "ALTER", 0, 0, 0, ""},
311
  { "ANALYZE", 0, 0, 0, ""},
312
  { "AND", 0, 0, 0, ""},
313
  { "ANY", 0, 0, 0, ""},
314
  { "AS", 0, 0, 0, ""},
315
  { "ASC", 0, 0, 0, ""},
316
  { "ASCII", 0, 0, 0, ""},
317
  { "ASENSITIVE", 0, 0, 0, ""},
318
  { "AUTO_INCREMENT", 0, 0, 0, ""},
319
  { "AVG", 0, 0, 0, ""},
320
  { "AVG_ROW_LENGTH", 0, 0, 0, ""},
321
  { "BACKUP", 0, 0, 0, ""},
322
  { "BDB", 0, 0, 0, ""},
323
  { "BEFORE", 0, 0, 0, ""},
324
  { "BEGIN", 0, 0, 0, ""},
325
  { "BERKELEYDB", 0, 0, 0, ""},
326
  { "BETWEEN", 0, 0, 0, ""},
327
  { "BIGINT", 0, 0, 0, ""},
328
  { "BINARY", 0, 0, 0, ""},
329
  { "BINLOG", 0, 0, 0, ""},
330
  { "BIT", 0, 0, 0, ""},
331
  { "BLOB", 0, 0, 0, ""},
332
  { "BOOL", 0, 0, 0, ""},
333
  { "BOOLEAN", 0, 0, 0, ""},
334
  { "BOTH", 0, 0, 0, ""},
335
  { "BTREE", 0, 0, 0, ""},
336
  { "BY", 0, 0, 0, ""},
337
  { "BYTE", 0, 0, 0, ""},
338
  { "CACHE", 0, 0, 0, ""},
339
  { "CALL", 0, 0, 0, ""},
340
  { "CASCADE", 0, 0, 0, ""},
341
  { "CASCADED", 0, 0, 0, ""},
342
  { "CASE", 0, 0, 0, ""},
343
  { "CHAIN", 0, 0, 0, ""},
344
  { "CHANGE", 0, 0, 0, ""},
345
  { "CHANGED", 0, 0, 0, ""},
346
  { "CHAR", 0, 0, 0, ""},
347
  { "CHARACTER", 0, 0, 0, ""},
348
  { "CHARSET", 0, 0, 0, ""},
349
  { "CHECK", 0, 0, 0, ""},
350
  { "CHECKSUM", 0, 0, 0, ""},
351
  { "CIPHER", 0, 0, 0, ""},
352
  { "CLIENT", 0, 0, 0, ""},
353
  { "CLOSE", 0, 0, 0, ""},
354
  { "CODE", 0, 0, 0, ""},
355
  { "COLLATE", 0, 0, 0, ""},
356
  { "COLLATION", 0, 0, 0, ""},
357
  { "COLUMN", 0, 0, 0, ""},
358
  { "COLUMNS", 0, 0, 0, ""},
359
  { "COMMENT", 0, 0, 0, ""},
360
  { "COMMIT", 0, 0, 0, ""},
361
  { "COMMITTED", 0, 0, 0, ""},
362
  { "COMPACT", 0, 0, 0, ""},
363
  { "COMPRESSED", 0, 0, 0, ""},
364
  { "CONCURRENT", 0, 0, 0, ""},
365
  { "CONDITION", 0, 0, 0, ""},
366
  { "CONNECTION", 0, 0, 0, ""},
367
  { "CONSISTENT", 0, 0, 0, ""},
368
  { "CONSTRAINT", 0, 0, 0, ""},
369
  { "CONTAINS", 0, 0, 0, ""},
370
  { "CONTINUE", 0, 0, 0, ""},
371
  { "CONVERT", 0, 0, 0, ""},
372
  { "CREATE", 0, 0, 0, ""},
373
  { "CROSS", 0, 0, 0, ""},
374
  { "CUBE", 0, 0, 0, ""},
375
  { "CURRENT_DATE", 0, 0, 0, ""},
376
  { "CURRENT_TIME", 0, 0, 0, ""},
377
  { "CURRENT_TIMESTAMP", 0, 0, 0, ""},
378
  { "CURRENT_USER", 0, 0, 0, ""},
379
  { "CURSOR", 0, 0, 0, ""},
380
  { "DATA", 0, 0, 0, ""},
381
  { "DATABASE", 0, 0, 0, ""},
382
  { "DATABASES", 0, 0, 0, ""},
383
  { "DATE", 0, 0, 0, ""},
384
  { "DATETIME", 0, 0, 0, ""},
385
  { "DAY", 0, 0, 0, ""},
386
  { "DAY_HOUR", 0, 0, 0, ""},
387
  { "DAY_MICROSECOND", 0, 0, 0, ""},
388
  { "DAY_MINUTE", 0, 0, 0, ""},
389
  { "DAY_SECOND", 0, 0, 0, ""},
390
  { "DEALLOCATE", 0, 0, 0, ""},     
391
  { "DEC", 0, 0, 0, ""},
392
  { "DECIMAL", 0, 0, 0, ""},
393
  { "DECLARE", 0, 0, 0, ""},
394
  { "DEFAULT", 0, 0, 0, ""},
395
  { "DEFINER", 0, 0, 0, ""},
396
  { "DELAYED", 0, 0, 0, ""},
397
  { "DELAY_KEY_WRITE", 0, 0, 0, ""},
398
  { "DELETE", 0, 0, 0, ""},
399
  { "DESC", 0, 0, 0, ""},
400
  { "DESCRIBE", 0, 0, 0, ""},
401
  { "DES_KEY_FILE", 0, 0, 0, ""},
402
  { "DETERMINISTIC", 0, 0, 0, ""},
403
  { "DIRECTORY", 0, 0, 0, ""},
404
  { "DISABLE", 0, 0, 0, ""},
405
  { "DISCARD", 0, 0, 0, ""},
406
  { "DISTINCT", 0, 0, 0, ""},
407
  { "DISTINCTROW", 0, 0, 0, ""},
408
  { "DIV", 0, 0, 0, ""},
409
  { "DO", 0, 0, 0, ""},
410
  { "DOUBLE", 0, 0, 0, ""},
411
  { "DROP", 0, 0, 0, ""},
412
  { "DUAL", 0, 0, 0, ""},
413
  { "DUMPFILE", 0, 0, 0, ""},
414
  { "DUPLICATE", 0, 0, 0, ""},
415
  { "DYNAMIC", 0, 0, 0, ""},
416
  { "EACH", 0, 0, 0, ""},
417
  { "ELSE", 0, 0, 0, ""},
418
  { "ELSEIF", 0, 0, 0, ""},
419
  { "ENABLE", 0, 0, 0, ""},
420
  { "ENCLOSED", 0, 0, 0, ""},
421
  { "END", 0, 0, 0, ""},
422
  { "ENGINE", 0, 0, 0, ""},
423
  { "ENGINES", 0, 0, 0, ""},
424
  { "ENUM", 0, 0, 0, ""},
425
  { "ERRORS", 0, 0, 0, ""},
426
  { "ESCAPE", 0, 0, 0, ""},
427
  { "ESCAPED", 0, 0, 0, ""},
428
  { "EVENTS", 0, 0, 0, ""},
429
  { "EXECUTE", 0, 0, 0, ""},
430
  { "EXISTS", 0, 0, 0, ""},
431
  { "EXIT", 0, 0, 0, ""},
432
  { "EXPANSION", 0, 0, 0, ""},
433
  { "EXPLAIN", 0, 0, 0, ""},
434
  { "EXTENDED", 0, 0, 0, ""},
435
  { "FALSE", 0, 0, 0, ""},
436
  { "FAST", 0, 0, 0, ""},
437
  { "FETCH", 0, 0, 0, ""},
438
  { "FIELDS", 0, 0, 0, ""},
439
  { "FILE", 0, 0, 0, ""},
440
  { "FIRST", 0, 0, 0, ""},
441
  { "FIXED", 0, 0, 0, ""},
442
  { "FLOAT", 0, 0, 0, ""},
443
  { "FLOAT4", 0, 0, 0, ""},
444
  { "FLOAT8", 0, 0, 0, ""},
445
  { "FLUSH", 0, 0, 0, ""},
446
  { "FOR", 0, 0, 0, ""},
447
  { "FORCE", 0, 0, 0, ""},
448
  { "FOREIGN", 0, 0, 0, ""},
449
  { "FOUND", 0, 0, 0, ""},
450
  { "FRAC_SECOND", 0, 0, 0, ""},
451
  { "FROM", 0, 0, 0, ""},
452
  { "FULL", 0, 0, 0, ""},
453
  { "FULLTEXT", 0, 0, 0, ""},
454
  { "FUNCTION", 0, 0, 0, ""},
455
  { "GEOMETRY", 0, 0, 0, ""},
456
  { "GEOMETRYCOLLECTION", 0, 0, 0, ""},
457
  { "GET_FORMAT", 0, 0, 0, ""},
458
  { "GLOBAL", 0, 0, 0, ""},
459
  { "GRANT", 0, 0, 0, ""},
460
  { "GRANTS", 0, 0, 0, ""},
461
  { "GROUP", 0, 0, 0, ""},
462
  { "HANDLER", 0, 0, 0, ""},
463
  { "HASH", 0, 0, 0, ""},
464
  { "HAVING", 0, 0, 0, ""},
465
  { "HELP", 0, 0, 0, ""},
466
  { "HIGH_PRIORITY", 0, 0, 0, ""},
467
  { "HOSTS", 0, 0, 0, ""},
468
  { "HOUR", 0, 0, 0, ""},
469
  { "HOUR_MICROSECOND", 0, 0, 0, ""},
470
  { "HOUR_MINUTE", 0, 0, 0, ""},
471
  { "HOUR_SECOND", 0, 0, 0, ""},
472
  { "IDENTIFIED", 0, 0, 0, ""},
473
  { "IF", 0, 0, 0, ""},
474
  { "IGNORE", 0, 0, 0, ""},
475
  { "IMPORT", 0, 0, 0, ""},
476
  { "IN", 0, 0, 0, ""},
477
  { "INDEX", 0, 0, 0, ""},
478
  { "INDEXES", 0, 0, 0, ""},
479
  { "INFILE", 0, 0, 0, ""},
480
  { "INNER", 0, 0, 0, ""},
481
  { "INNOBASE", 0, 0, 0, ""},
482
  { "INNODB", 0, 0, 0, ""},
483
  { "INOUT", 0, 0, 0, ""},
484
  { "INSENSITIVE", 0, 0, 0, ""},
485
  { "INSERT", 0, 0, 0, ""},
486
  { "INSERT_METHOD", 0, 0, 0, ""},
487
  { "INT", 0, 0, 0, ""},
488
  { "INT1", 0, 0, 0, ""},
489
  { "INT2", 0, 0, 0, ""},
490
  { "INT3", 0, 0, 0, ""},
491
  { "INT4", 0, 0, 0, ""},
492
  { "INT8", 0, 0, 0, ""},
493
  { "INTEGER", 0, 0, 0, ""},
494
  { "INTERVAL", 0, 0, 0, ""},
495
  { "INTO", 0, 0, 0, ""},
496
  { "IO_THREAD", 0, 0, 0, ""},
497
  { "IS", 0, 0, 0, ""},
498
  { "ISOLATION", 0, 0, 0, ""},
499
  { "ISSUER", 0, 0, 0, ""},
500
  { "ITERATE", 0, 0, 0, ""},
501
  { "INVOKER", 0, 0, 0, ""},
502
  { "JOIN", 0, 0, 0, ""},
503
  { "KEY", 0, 0, 0, ""},
504
  { "KEYS", 0, 0, 0, ""},
505
  { "KILL", 0, 0, 0, ""},
506
  { "LANGUAGE", 0, 0, 0, ""},
507
  { "LAST", 0, 0, 0, ""},
508
  { "LEADING", 0, 0, 0, ""},
509
  { "LEAVE", 0, 0, 0, ""},
510
  { "LEAVES", 0, 0, 0, ""},
511
  { "LEFT", 0, 0, 0, ""},
512
  { "LEVEL", 0, 0, 0, ""},
513
  { "LIKE", 0, 0, 0, ""},
514
  { "LIMIT", 0, 0, 0, ""},
515
  { "LINES", 0, 0, 0, ""},
516
  { "LINESTRING", 0, 0, 0, ""},
517
  { "LOAD", 0, 0, 0, ""},
518
  { "LOCAL", 0, 0, 0, ""},
519
  { "LOCALTIME", 0, 0, 0, ""},
520
  { "LOCALTIMESTAMP", 0, 0, 0, ""},
521
  { "LOCK", 0, 0, 0, ""},
522
  { "LOCKS", 0, 0, 0, ""},
523
  { "LOGS", 0, 0, 0, ""},
524
  { "LONG", 0, 0, 0, ""},
525
  { "LONGBLOB", 0, 0, 0, ""},
526
  { "LONGTEXT", 0, 0, 0, ""},
527
  { "LOOP", 0, 0, 0, ""},
528
  { "LOW_PRIORITY", 0, 0, 0, ""},
529
  { "MASTER", 0, 0, 0, ""},
530
  { "MASTER_CONNECT_RETRY", 0, 0, 0, ""},
531
  { "MASTER_HOST", 0, 0, 0, ""},
532
  { "MASTER_LOG_FILE", 0, 0, 0, ""},
533
  { "MASTER_LOG_POS", 0, 0, 0, ""},
534
  { "MASTER_PASSWORD", 0, 0, 0, ""},
535
  { "MASTER_PORT", 0, 0, 0, ""},
536
  { "MASTER_SERVER_ID", 0, 0, 0, ""},
537
  { "MASTER_SSL", 0, 0, 0, ""},
538
  { "MASTER_SSL_CA", 0, 0, 0, ""},
539
  { "MASTER_SSL_CAPATH", 0, 0, 0, ""},
540
  { "MASTER_SSL_CERT", 0, 0, 0, ""},
541
  { "MASTER_SSL_CIPHER", 0, 0, 0, ""},
542
  { "MASTER_SSL_KEY", 0, 0, 0, ""},
543
  { "MASTER_USER", 0, 0, 0, ""},
544
  { "MATCH", 0, 0, 0, ""},
545
  { "MAX_CONNECTIONS_PER_HOUR", 0, 0, 0, ""},
546
  { "MAX_QUERIES_PER_HOUR", 0, 0, 0, ""},
547
  { "MAX_ROWS", 0, 0, 0, ""},
548
  { "MAX_UPDATES_PER_HOUR", 0, 0, 0, ""},
549
  { "MAX_USER_CONNECTIONS", 0, 0, 0, ""},
550
  { "MEDIUM", 0, 0, 0, ""},
551
  { "MEDIUMBLOB", 0, 0, 0, ""},
552
  { "MEDIUMINT", 0, 0, 0, ""},
553
  { "MEDIUMTEXT", 0, 0, 0, ""},
554
  { "MERGE", 0, 0, 0, ""},
555
  { "MICROSECOND", 0, 0, 0, ""},
556
  { "MIDDLEINT", 0, 0, 0, ""},
557
  { "MIGRATE", 0, 0, 0, ""},
558
  { "MINUTE", 0, 0, 0, ""},
559
  { "MINUTE_MICROSECOND", 0, 0, 0, ""},
560
  { "MINUTE_SECOND", 0, 0, 0, ""},
561
  { "MIN_ROWS", 0, 0, 0, ""},
562
  { "MOD", 0, 0, 0, ""},
563
  { "MODE", 0, 0, 0, ""},
564
  { "MODIFIES", 0, 0, 0, ""},
565
  { "MODIFY", 0, 0, 0, ""},
566
  { "MONTH", 0, 0, 0, ""},
567
  { "MULTILINESTRING", 0, 0, 0, ""},
568
  { "MULTIPOINT", 0, 0, 0, ""},
569
  { "MULTIPOLYGON", 0, 0, 0, ""},
570
  { "MUTEX", 0, 0, 0, ""},
571
  { "NAME", 0, 0, 0, ""},
572
  { "NAMES", 0, 0, 0, ""},
573
  { "NATIONAL", 0, 0, 0, ""},
574
  { "NATURAL", 0, 0, 0, ""},
575
  { "NDB", 0, 0, 0, ""},
576
  { "NDBCLUSTER", 0, 0, 0, ""},
577
  { "NCHAR", 0, 0, 0, ""},
578
  { "NEW", 0, 0, 0, ""},
579
  { "NEXT", 0, 0, 0, ""},
580
  { "NO", 0, 0, 0, ""},
581
  { "NONE", 0, 0, 0, ""},
582
  { "NOT", 0, 0, 0, ""},
583
  { "NO_WRITE_TO_BINLOG", 0, 0, 0, ""},
584
  { "NULL", 0, 0, 0, ""},
585
  { "NUMERIC", 0, 0, 0, ""},
586
  { "NVARCHAR", 0, 0, 0, ""},
587
  { "OFFSET", 0, 0, 0, ""},
588
  { "OLD_PASSWORD", 0, 0, 0, ""},
589
  { "ON", 0, 0, 0, ""},
590
  { "ONE", 0, 0, 0, ""},
591
  { "ONE_SHOT", 0, 0, 0, ""},
592
  { "OPEN", 0, 0, 0, ""},
593
  { "OPTIMIZE", 0, 0, 0, ""},
594
  { "OPTION", 0, 0, 0, ""},
595
  { "OPTIONALLY", 0, 0, 0, ""},
596
  { "OR", 0, 0, 0, ""},
597
  { "ORDER", 0, 0, 0, ""},
598
  { "OUT", 0, 0, 0, ""},
599
  { "OUTER", 0, 0, 0, ""},
600
  { "OUTFILE", 0, 0, 0, ""},
601
  { "PACK_KEYS", 0, 0, 0, ""},
602
  { "PARTIAL", 0, 0, 0, ""},
603
  { "PASSWORD", 0, 0, 0, ""},
604
  { "PHASE", 0, 0, 0, ""},
605
  { "POINT", 0, 0, 0, ""},
606
  { "POLYGON", 0, 0, 0, ""},
607
  { "PRECISION", 0, 0, 0, ""},
608
  { "PREPARE", 0, 0, 0, ""},
609
  { "PREV", 0, 0, 0, ""},
610
  { "PRIMARY", 0, 0, 0, ""},
611
  { "PRIVILEGES", 0, 0, 0, ""},
612
  { "PROCEDURE", 0, 0, 0, ""},
613
  { "PROCESS", 0, 0, 0, ""},
614
  { "PROCESSLIST", 0, 0, 0, ""},
615
  { "PURGE", 0, 0, 0, ""},
616
  { "QUARTER", 0, 0, 0, ""},
617
  { "QUERY", 0, 0, 0, ""},
618
  { "QUICK", 0, 0, 0, ""},
619
  { "RAID0", 0, 0, 0, ""},
620
  { "RAID_CHUNKS", 0, 0, 0, ""},
621
  { "RAID_CHUNKSIZE", 0, 0, 0, ""},
622
  { "RAID_TYPE", 0, 0, 0, ""},
623
  { "READ", 0, 0, 0, ""},
624
  { "READS", 0, 0, 0, ""},
625
  { "REAL", 0, 0, 0, ""},
626
  { "RECOVER", 0, 0, 0, ""},
627
  { "REDUNDANT", 0, 0, 0, ""},
628
  { "REFERENCES", 0, 0, 0, ""},
629
  { "REGEXP", 0, 0, 0, ""},
630
  { "RELAY_LOG_FILE", 0, 0, 0, ""},
631
  { "RELAY_LOG_POS", 0, 0, 0, ""},
632
  { "RELAY_THREAD", 0, 0, 0, ""},
633
  { "RELEASE", 0, 0, 0, ""},
634
  { "RELOAD", 0, 0, 0, ""},
635
  { "RENAME", 0, 0, 0, ""},
636
  { "REPAIR", 0, 0, 0, ""},
637
  { "REPEATABLE", 0, 0, 0, ""},
638
  { "REPLACE", 0, 0, 0, ""},
639
  { "REPLICATION", 0, 0, 0, ""},
640
  { "REPEAT", 0, 0, 0, ""},
641
  { "REQUIRE", 0, 0, 0, ""},
642
  { "RESET", 0, 0, 0, ""},
643
  { "RESTORE", 0, 0, 0, ""},
644
  { "RESTRICT", 0, 0, 0, ""},
645
  { "RESUME", 0, 0, 0, ""},
646
  { "RETURN", 0, 0, 0, ""},
647
  { "RETURNS", 0, 0, 0, ""},
648
  { "REVOKE", 0, 0, 0, ""},
649
  { "RIGHT", 0, 0, 0, ""},
650
  { "RLIKE", 0, 0, 0, ""},
651
  { "ROLLBACK", 0, 0, 0, ""},
652
  { "ROLLUP", 0, 0, 0, ""},
653
  { "ROUTINE", 0, 0, 0, ""},
654
  { "ROW", 0, 0, 0, ""},
655
  { "ROWS", 0, 0, 0, ""},
656
  { "ROW_FORMAT", 0, 0, 0, ""},
657
  { "RTREE", 0, 0, 0, ""},
658
  { "SAVEPOINT", 0, 0, 0, ""},
659
  { "SCHEMA", 0, 0, 0, ""},
660
  { "SCHEMAS", 0, 0, 0, ""},
661
  { "SECOND", 0, 0, 0, ""},
662
  { "SECOND_MICROSECOND", 0, 0, 0, ""},
663
  { "SECURITY", 0, 0, 0, ""},
664
  { "SELECT", 0, 0, 0, ""},
665
  { "SENSITIVE", 0, 0, 0, ""},
666
  { "SEPARATOR", 0, 0, 0, ""},
667
  { "SERIAL", 0, 0, 0, ""},
668
  { "SERIALIZABLE", 0, 0, 0, ""},
669
  { "SESSION", 0, 0, 0, ""},
670
  { "SET", 0, 0, 0, ""},
671
  { "SHARE", 0, 0, 0, ""},
672
  { "SHOW", 0, 0, 0, ""},
673
  { "SHUTDOWN", 0, 0, 0, ""},
674
  { "SIGNED", 0, 0, 0, ""},
675
  { "SIMPLE", 0, 0, 0, ""},
676
  { "SLAVE", 0, 0, 0, ""},
677
  { "SNAPSHOT", 0, 0, 0, ""},
678
  { "SMALLINT", 0, 0, 0, ""},
679
  { "SOME", 0, 0, 0, ""},
680
  { "SONAME", 0, 0, 0, ""},
681
  { "SOUNDS", 0, 0, 0, ""},
682
  { "SPATIAL", 0, 0, 0, ""},
683
  { "SPECIFIC", 0, 0, 0, ""},
684
  { "SQL", 0, 0, 0, ""},
685
  { "SQLEXCEPTION", 0, 0, 0, ""},
686
  { "SQLSTATE", 0, 0, 0, ""},
687
  { "SQLWARNING", 0, 0, 0, ""},
688
  { "SQL_BIG_RESULT", 0, 0, 0, ""},
689
  { "SQL_BUFFER_RESULT", 0, 0, 0, ""},
690
  { "SQL_CACHE", 0, 0, 0, ""},
691
  { "SQL_CALC_FOUND_ROWS", 0, 0, 0, ""},
692
  { "SQL_NO_CACHE", 0, 0, 0, ""},
693
  { "SQL_SMALL_RESULT", 0, 0, 0, ""},
694
  { "SQL_THREAD", 0, 0, 0, ""},
695
  { "SQL_TSI_FRAC_SECOND", 0, 0, 0, ""},
696
  { "SQL_TSI_SECOND", 0, 0, 0, ""},
697
  { "SQL_TSI_MINUTE", 0, 0, 0, ""},
698
  { "SQL_TSI_HOUR", 0, 0, 0, ""},
699
  { "SQL_TSI_DAY", 0, 0, 0, ""},
700
  { "SQL_TSI_WEEK", 0, 0, 0, ""},
701
  { "SQL_TSI_MONTH", 0, 0, 0, ""},
702
  { "SQL_TSI_QUARTER", 0, 0, 0, ""},
703
  { "SQL_TSI_YEAR", 0, 0, 0, ""},
704
  { "SSL", 0, 0, 0, ""},
705
  { "START", 0, 0, 0, ""},
706
  { "STARTING", 0, 0, 0, ""},
707
  { "STATUS", 0, 0, 0, ""},
708
  { "STOP", 0, 0, 0, ""},
709
  { "STORAGE", 0, 0, 0, ""},
710
  { "STRAIGHT_JOIN", 0, 0, 0, ""},
711
  { "STRING", 0, 0, 0, ""},
712
  { "STRIPED", 0, 0, 0, ""},
713
  { "SUBJECT", 0, 0, 0, ""},
714
  { "SUPER", 0, 0, 0, ""},
715
  { "SUSPEND", 0, 0, 0, ""},
716
  { "TABLE", 0, 0, 0, ""},
717
  { "TABLES", 0, 0, 0, ""},
718
  { "TABLESPACE", 0, 0, 0, ""},
719
  { "TEMPORARY", 0, 0, 0, ""},
720
  { "TEMPTABLE", 0, 0, 0, ""},
721
  { "TERMINATED", 0, 0, 0, ""},
722
  { "TEXT", 0, 0, 0, ""},
723
  { "THEN", 0, 0, 0, ""},
724
  { "TIME", 0, 0, 0, ""},
725
  { "TIMESTAMP", 0, 0, 0, ""},
726
  { "TIMESTAMPADD", 0, 0, 0, ""},
727
  { "TIMESTAMPDIFF", 0, 0, 0, ""},
728
  { "TINYBLOB", 0, 0, 0, ""},
729
  { "TINYINT", 0, 0, 0, ""},
730
  { "TINYTEXT", 0, 0, 0, ""},
731
  { "TO", 0, 0, 0, ""},
732
  { "TRAILING", 0, 0, 0, ""},
733
  { "TRANSACTION", 0, 0, 0, ""},
734
  { "TRIGGER", 0, 0, 0, ""},
735
  { "TRIGGERS", 0, 0, 0, ""},
736
  { "TRUE", 0, 0, 0, ""},
737
  { "TRUNCATE", 0, 0, 0, ""},
738
  { "TYPE", 0, 0, 0, ""},
739
  { "TYPES", 0, 0, 0, ""},
740
  { "UNCOMMITTED", 0, 0, 0, ""},
741
  { "UNDEFINED", 0, 0, 0, ""},
742
  { "UNDO", 0, 0, 0, ""},
743
  { "UNICODE", 0, 0, 0, ""},
744
  { "UNION", 0, 0, 0, ""},
745
  { "UNIQUE", 0, 0, 0, ""},
746
  { "UNKNOWN", 0, 0, 0, ""},
747
  { "UNLOCK", 0, 0, 0, ""},
748
  { "UNSIGNED", 0, 0, 0, ""},
749
  { "UNTIL", 0, 0, 0, ""},
750
  { "UPDATE", 0, 0, 0, ""},
751
  { "UPGRADE", 0, 0, 0, ""},
752
  { "USAGE", 0, 0, 0, ""},
753
  { "USE", 0, 0, 0, ""},
754
  { "USER", 0, 0, 0, ""},
755
  { "USER_RESOURCES", 0, 0, 0, ""},
756
  { "USE_FRM", 0, 0, 0, ""},
757
  { "USING", 0, 0, 0, ""},
758
  { "UTC_DATE", 0, 0, 0, ""},
759
  { "UTC_TIME", 0, 0, 0, ""},
760
  { "UTC_TIMESTAMP", 0, 0, 0, ""},
761
  { "VALUE", 0, 0, 0, ""},
762
  { "VALUES", 0, 0, 0, ""},
763
  { "VARBINARY", 0, 0, 0, ""},
764
  { "VARCHAR", 0, 0, 0, ""},
765
  { "VARCHARACTER", 0, 0, 0, ""},
766
  { "VARIABLES", 0, 0, 0, ""},
767
  { "VARYING", 0, 0, 0, ""},
768
  { "WARNINGS", 0, 0, 0, ""},
769
  { "WEEK", 0, 0, 0, ""},
770
  { "WHEN", 0, 0, 0, ""},
771
  { "WHERE", 0, 0, 0, ""},
772
  { "WHILE", 0, 0, 0, ""},
773
  { "VIEW", 0, 0, 0, ""},
774
  { "WITH", 0, 0, 0, ""},
775
  { "WORK", 0, 0, 0, ""},
776
  { "WRITE", 0, 0, 0, ""},
777
  { "X509", 0, 0, 0, ""},
778
  { "XOR", 0, 0, 0, ""},
779
  { "XA", 0, 0, 0, ""},
780
  { "YEAR", 0, 0, 0, ""},
781
  { "YEAR_MONTH", 0, 0, 0, ""},
782
  { "ZEROFILL", 0, 0, 0, ""},
783
  { "ABS", 0, 0, 0, ""},
784
  { "ACOS", 0, 0, 0, ""},
785
  { "ADDDATE", 0, 0, 0, ""},
786
  { "ADDTIME", 0, 0, 0, ""},
787
  { "AES_ENCRYPT", 0, 0, 0, ""},
788
  { "AES_DECRYPT", 0, 0, 0, ""},
789
  { "AREA", 0, 0, 0, ""},
790
  { "ASIN", 0, 0, 0, ""},
791
  { "ASBINARY", 0, 0, 0, ""},
792
  { "ASTEXT", 0, 0, 0, ""},
793
  { "ASWKB", 0, 0, 0, ""},
794
  { "ASWKT", 0, 0, 0, ""},
795
  { "ATAN", 0, 0, 0, ""},
796
  { "ATAN2", 0, 0, 0, ""},
797
  { "BENCHMARK", 0, 0, 0, ""},
798
  { "BIN", 0, 0, 0, ""},
799
  { "BIT_COUNT", 0, 0, 0, ""},
800
  { "BIT_OR", 0, 0, 0, ""},
801
  { "BIT_AND", 0, 0, 0, ""},
802
  { "BIT_XOR", 0, 0, 0, ""},
803
  { "CAST", 0, 0, 0, ""},
804
  { "CEIL", 0, 0, 0, ""},
805
  { "CEILING", 0, 0, 0, ""},
806
  { "BIT_LENGTH", 0, 0, 0, ""},
807
  { "CENTROID", 0, 0, 0, ""},
808
  { "CHAR_LENGTH", 0, 0, 0, ""},
809
  { "CHARACTER_LENGTH", 0, 0, 0, ""},
810
  { "COALESCE", 0, 0, 0, ""},
811
  { "COERCIBILITY", 0, 0, 0, ""},
812
  { "COMPRESS", 0, 0, 0, ""},
813
  { "CONCAT", 0, 0, 0, ""},
814
  { "CONCAT_WS", 0, 0, 0, ""},
815
  { "CONNECTION_ID", 0, 0, 0, ""},
816
  { "CONV", 0, 0, 0, ""},
817
  { "CONVERT_TZ", 0, 0, 0, ""},
818
  { "COUNT", 0, 0, 0, ""},
819
  { "COS", 0, 0, 0, ""},
820
  { "COT", 0, 0, 0, ""},
821
  { "CRC32", 0, 0, 0, ""},
822
  { "CROSSES", 0, 0, 0, ""},
823
  { "CURDATE", 0, 0, 0, ""},
824
  { "CURTIME", 0, 0, 0, ""},
825
  { "DATE_ADD", 0, 0, 0, ""},
826
  { "DATEDIFF", 0, 0, 0, ""},
827
  { "DATE_FORMAT", 0, 0, 0, ""},
828
  { "DATE_SUB", 0, 0, 0, ""},
829
  { "DAYNAME", 0, 0, 0, ""},
830
  { "DAYOFMONTH", 0, 0, 0, ""},
831
  { "DAYOFWEEK", 0, 0, 0, ""},
832
  { "DAYOFYEAR", 0, 0, 0, ""},
833
  { "DECODE", 0, 0, 0, ""},
834
  { "DEGREES", 0, 0, 0, ""},
835
  { "DES_ENCRYPT", 0, 0, 0, ""},
836
  { "DES_DECRYPT", 0, 0, 0, ""},
837
  { "DIMENSION", 0, 0, 0, ""},
838
  { "DISJOINT", 0, 0, 0, ""},
839
  { "ELT", 0, 0, 0, ""},
840
  { "ENCODE", 0, 0, 0, ""},
841
  { "ENCRYPT", 0, 0, 0, ""},
842
  { "ENDPOINT", 0, 0, 0, ""},
843
  { "ENVELOPE", 0, 0, 0, ""},
844
  { "EQUALS", 0, 0, 0, ""},
845
  { "EXTERIORRING", 0, 0, 0, ""},
846
  { "EXTRACT", 0, 0, 0, ""},
847
  { "EXP", 0, 0, 0, ""},
848
  { "EXPORT_SET", 0, 0, 0, ""},
849
  { "FIELD", 0, 0, 0, ""},
850
  { "FIND_IN_SET", 0, 0, 0, ""},
851
  { "FLOOR", 0, 0, 0, ""},
852
  { "FORMAT", 0, 0, 0, ""},
853
  { "FOUND_ROWS", 0, 0, 0, ""},
854
  { "FROM_DAYS", 0, 0, 0, ""},
855
  { "FROM_UNIXTIME", 0, 0, 0, ""},
856
  { "GET_LOCK", 0, 0, 0, ""},
857
  { "GEOMETRYN", 0, 0, 0, ""},
858
  { "GEOMETRYTYPE", 0, 0, 0, ""},
859
  { "GEOMCOLLFROMTEXT", 0, 0, 0, ""},
860
  { "GEOMCOLLFROMWKB", 0, 0, 0, ""},
861
  { "GEOMETRYCOLLECTIONFROMTEXT", 0, 0, 0, ""},
862
  { "GEOMETRYCOLLECTIONFROMWKB", 0, 0, 0, ""},
863
  { "GEOMETRYFROMTEXT", 0, 0, 0, ""},
864
  { "GEOMETRYFROMWKB", 0, 0, 0, ""},
865
  { "GEOMFROMTEXT", 0, 0, 0, ""},
866
  { "GEOMFROMWKB", 0, 0, 0, ""},
867
  { "GLENGTH", 0, 0, 0, ""},
868
  { "GREATEST", 0, 0, 0, ""},
869
  { "GROUP_CONCAT", 0, 0, 0, ""},
870
  { "GROUP_UNIQUE_USERS", 0, 0, 0, ""},
871
  { "HEX", 0, 0, 0, ""},
872
  { "IFNULL", 0, 0, 0, ""},
873
  { "INET_ATON", 0, 0, 0, ""},
874
  { "INET_NTOA", 0, 0, 0, ""},
875
  { "INSTR", 0, 0, 0, ""},
876
  { "INTERIORRINGN", 0, 0, 0, ""},
877
  { "INTERSECTS", 0, 0, 0, ""},
878
  { "ISCLOSED", 0, 0, 0, ""},
879
  { "ISEMPTY", 0, 0, 0, ""},
880
  { "ISNULL", 0, 0, 0, ""},
881
  { "IS_FREE_LOCK", 0, 0, 0, ""},
882
  { "IS_USED_LOCK", 0, 0, 0, ""},
883
  { "LAST_INSERT_ID", 0, 0, 0, ""},
884
  { "ISSIMPLE", 0, 0, 0, ""},
885
  { "LAST_DAY", 0, 0, 0, ""},
886
  { "LCASE", 0, 0, 0, ""},
887
  { "LEAST", 0, 0, 0, ""},
888
  { "LENGTH", 0, 0, 0, ""},
889
  { "LN", 0, 0, 0, ""},
890
  { "LINEFROMTEXT", 0, 0, 0, ""},
891
  { "LINEFROMWKB", 0, 0, 0, ""},
892
  { "LINESTRINGFROMTEXT", 0, 0, 0, ""},
893
  { "LINESTRINGFROMWKB", 0, 0, 0, ""},
894
  { "LOAD_FILE", 0, 0, 0, ""},
895
  { "LOCATE", 0, 0, 0, ""},
896
  { "LOG", 0, 0, 0, ""},
897
  { "LOG2", 0, 0, 0, ""},
898
  { "LOG10", 0, 0, 0, ""},
899
  { "LOWER", 0, 0, 0, ""},
900
  { "LPAD", 0, 0, 0, ""},
901
  { "LTRIM", 0, 0, 0, ""},
902
  { "MAKE_SET", 0, 0, 0, ""},
903
  { "MAKEDATE", 0, 0, 0, ""},
904
  { "MAKETIME", 0, 0, 0, ""},
905
  { "MASTER_POS_WAIT", 0, 0, 0, ""},
906
  { "MAX", 0, 0, 0, ""},
907
  { "MBRCONTAINS", 0, 0, 0, ""},
908
  { "MBRDISJOINT", 0, 0, 0, ""},
909
  { "MBREQUAL", 0, 0, 0, ""},
910
  { "MBRINTERSECTS", 0, 0, 0, ""},
911
  { "MBROVERLAPS", 0, 0, 0, ""},
912
  { "MBRTOUCHES", 0, 0, 0, ""},
913
  { "MBRWITHIN", 0, 0, 0, ""},
914
  { "MD5", 0, 0, 0, ""},
915
  { "MID", 0, 0, 0, ""},
916
  { "MIN", 0, 0, 0, ""},
917
  { "MLINEFROMTEXT", 0, 0, 0, ""},
918
  { "MLINEFROMWKB", 0, 0, 0, ""},
919
  { "MPOINTFROMTEXT", 0, 0, 0, ""},
920
  { "MPOINTFROMWKB", 0, 0, 0, ""},
921
  { "MPOLYFROMTEXT", 0, 0, 0, ""},
922
  { "MPOLYFROMWKB", 0, 0, 0, ""},
923
  { "MONTHNAME", 0, 0, 0, ""},
924
  { "MULTILINESTRINGFROMTEXT", 0, 0, 0, ""},
925
  { "MULTILINESTRINGFROMWKB", 0, 0, 0, ""},
926
  { "MULTIPOINTFROMTEXT", 0, 0, 0, ""},
927
  { "MULTIPOINTFROMWKB", 0, 0, 0, ""},
928
  { "MULTIPOLYGONFROMTEXT", 0, 0, 0, ""},
929
  { "MULTIPOLYGONFROMWKB", 0, 0, 0, ""},
930
  { "NAME_CONST", 0, 0, 0, ""},
931
  { "NOW", 0, 0, 0, ""},
932
  { "NULLIF", 0, 0, 0, ""},
933
  { "NUMGEOMETRIES", 0, 0, 0, ""},
934
  { "NUMINTERIORRINGS", 0, 0, 0, ""},
935
  { "NUMPOINTS", 0, 0, 0, ""},
936
  { "OCTET_LENGTH", 0, 0, 0, ""},
937
  { "OCT", 0, 0, 0, ""},
938
  { "ORD", 0, 0, 0, ""},
939
  { "OVERLAPS", 0, 0, 0, ""},
940
  { "PERIOD_ADD", 0, 0, 0, ""},
941
  { "PERIOD_DIFF", 0, 0, 0, ""},
942
  { "PI", 0, 0, 0, ""},
943
  { "POINTFROMTEXT", 0, 0, 0, ""},
944
  { "POINTFROMWKB", 0, 0, 0, ""},
945
  { "POINTN", 0, 0, 0, ""},
946
  { "POLYFROMTEXT", 0, 0, 0, ""},
947
  { "POLYFROMWKB", 0, 0, 0, ""},
948
  { "POLYGONFROMTEXT", 0, 0, 0, ""},
949
  { "POLYGONFROMWKB", 0, 0, 0, ""},
950
  { "POSITION", 0, 0, 0, ""},
951
  { "POW", 0, 0, 0, ""},
952
  { "POWER", 0, 0, 0, ""},
953
  { "QUOTE", 0, 0, 0, ""},
954
  { "RADIANS", 0, 0, 0, ""},
955
  { "RAND", 0, 0, 0, ""},
956
  { "RELEASE_LOCK", 0, 0, 0, ""},
957
  { "REVERSE", 0, 0, 0, ""},
958
  { "ROUND", 0, 0, 0, ""},
959
  { "ROW_COUNT", 0, 0, 0, ""},
960
  { "RPAD", 0, 0, 0, ""},
961
  { "RTRIM", 0, 0, 0, ""},
962
  { "SEC_TO_TIME", 0, 0, 0, ""},
963
  { "SESSION_USER", 0, 0, 0, ""},
964
  { "SUBDATE", 0, 0, 0, ""},
965
  { "SIGN", 0, 0, 0, ""},
966
  { "SIN", 0, 0, 0, ""},
967
  { "SHA", 0, 0, 0, ""},
968
  { "SHA1", 0, 0, 0, ""},
969
  { "SLEEP", 0, 0, 0, ""},
970
  { "SOUNDEX", 0, 0, 0, ""},
971
  { "SPACE", 0, 0, 0, ""},
972
  { "SQRT", 0, 0, 0, ""},
973
  { "SRID", 0, 0, 0, ""},
974
  { "STARTPOINT", 0, 0, 0, ""},
975
  { "STD", 0, 0, 0, ""},
976
  { "STDDEV", 0, 0, 0, ""},
977
  { "STDDEV_POP", 0, 0, 0, ""},
978
  { "STDDEV_SAMP", 0, 0, 0, ""},
979
  { "STR_TO_DATE", 0, 0, 0, ""},
980
  { "STRCMP", 0, 0, 0, ""},
981
  { "SUBSTR", 0, 0, 0, ""},
982
  { "SUBSTRING", 0, 0, 0, ""},
983
  { "SUBSTRING_INDEX", 0, 0, 0, ""},
984
  { "SUBTIME", 0, 0, 0, ""},
985
  { "SUM", 0, 0, 0, ""},
986
  { "SYSDATE", 0, 0, 0, ""},
987
  { "SYSTEM_USER", 0, 0, 0, ""},
988
  { "TAN", 0, 0, 0, ""},
989
  { "TIME_FORMAT", 0, 0, 0, ""},
990
  { "TIME_TO_SEC", 0, 0, 0, ""},
991
  { "TIMEDIFF", 0, 0, 0, ""},
992
  { "TO_DAYS", 0, 0, 0, ""},
993
  { "TOUCHES", 0, 0, 0, ""},
994
  { "TRIM", 0, 0, 0, ""},
995
  { "UCASE", 0, 0, 0, ""},
996
  { "UNCOMPRESS", 0, 0, 0, ""},
997
  { "UNCOMPRESSED_LENGTH", 0, 0, 0, ""},
998
  { "UNHEX", 0, 0, 0, ""},
999
  { "UNIQUE_USERS", 0, 0, 0, ""},
1000
  { "UNIX_TIMESTAMP", 0, 0, 0, ""},
1001
  { "UPPER", 0, 0, 0, ""},
1002
  { "UUID", 0, 0, 0, ""},
1003
  { "VARIANCE", 0, 0, 0, ""},
1004
  { "VAR_POP", 0, 0, 0, ""},
1005
  { "VAR_SAMP", 0, 0, 0, ""},
1006
  { "VERSION", 0, 0, 0, ""},
1007
  { "WEEKDAY", 0, 0, 0, ""},
1008
  { "WEEKOFYEAR", 0, 0, 0, ""},
1009
  { "WITHIN", 0, 0, 0, ""},
1010
  { "X", 0, 0, 0, ""},
1011
  { "Y", 0, 0, 0, ""},
1012
  { "YEARWEEK", 0, 0, 0, ""},
1013
  /* end sentinel */
1014
  { (char *)NULL,       0, 0, 0, ""}
1015
};
1016
1017
static const char *load_default_groups[]= { "mysql","client",0 };
1018
1019
static int         embedded_server_arg_count= 0;
1020
static char       *embedded_server_args[MAX_SERVER_ARGS];
1021
static const char *embedded_server_groups[]=
1022
{ "server", "embedded", "mysql_SERVER", 0 };
1023
1024
#ifdef HAVE_READLINE
1025
/*
1026
 HIST_ENTRY is defined for libedit, but not for the real readline
1027
 Need to redefine it for real readline to find it
1028
*/
1029
#if !defined(HAVE_HIST_ENTRY)
1030
typedef struct _hist_entry {
1031
  const char      *line;
1032
  const char      *data;
1033
} HIST_ENTRY; 
1034
#endif
1035
1036
extern "C" int add_history(const char *command); /* From readline directory */
1037
extern "C" int read_history(const char *command);
1038
extern "C" int write_history(const char *command);
1039
extern "C" HIST_ENTRY *history_get(int num);
1040
extern "C" int history_length;
1041
static int not_in_history(const char *line);
1042
static void initialize_readline (char *name);
1043
static void fix_history(String *final_command);
1044
#endif
1045
1046
static COMMANDS *find_command(char *name,char cmd_name);
1047
static bool add_line(String &buffer,char *line,char *in_string,
1048
                     bool *ml_comment);
1049
static void remove_cntrl(String &buffer);
1050
static void print_table_data(MYSQL_RES *result);
1051
static void print_table_data_html(MYSQL_RES *result);
1052
static void print_table_data_xml(MYSQL_RES *result);
1053
static void print_tab_data(MYSQL_RES *result);
1054
static void print_table_data_vertically(MYSQL_RES *result);
1055
static void print_warnings(void);
1056
static ulong start_timer(void);
1057
static void end_timer(ulong start_time,char *buff);
1058
static void mysql_end_timer(ulong start_time,char *buff);
1059
static void nice_time(double sec,char *buff,bool part_second);
1060
extern "C" sig_handler mysql_end(int sig);
1061
extern "C" sig_handler handle_sigint(int sig);
1062
#if defined(HAVE_TERMIOS_H) && defined(GWINSZ_IN_SYS_IOCTL)
1063
static sig_handler window_resize(int sig);
1064
#endif
1065
1066
int main(int argc,char *argv[])
1067
{
1068
  char buff[80];
1069
1070
  MY_INIT(argv[0]);
1071
  DBUG_ENTER("main");
1072
  DBUG_PROCESS(argv[0]);
1073
  
1074
  delimiter_str= delimiter;
1075
  default_prompt = my_strdup(getenv("MYSQL_PS1") ? 
1076
			     getenv("MYSQL_PS1") : 
1077
			     "mysql> ",MYF(MY_WME));
1078
  current_prompt = my_strdup(default_prompt,MYF(MY_WME));
1079
  prompt_counter=0;
1080
1081
  outfile[0]=0;			// no (default) outfile
1082
  strmov(pager, "stdout");	// the default, if --pager wasn't given
1083
  {
1084
    char *tmp=getenv("PAGER");
1085
    if (tmp && strlen(tmp))
1086
    {
1087
      default_pager_set= 1;
1088
      strmov(default_pager, tmp);
1089
    }
1090
  }
1091
  if (!isatty(0) || !isatty(1))
1092
  {
1093
    status.batch=1; opt_silent=1;
1094
    ignore_errors=0;
1095
  }
1096
  else
1097
    status.add_to_history=1;
1098
  status.exit_status=1;
1099
1100
  {
1101
    /* 
1102
     The file descriptor-layer may be out-of-sync with the file-number layer,
1103
     so we make sure that "stdout" is really open.  If its file is closed then
1104
     explicitly close the FD layer. 
1105
    */
1106
    int stdout_fileno_copy;
1107
    stdout_fileno_copy= dup(fileno(stdout)); /* Okay if fileno fails. */
1108
    if (stdout_fileno_copy == -1)
1109
      fclose(stdout);
1110
    else
1111
      close(stdout_fileno_copy);             /* Clean up dup(). */
1112
  }
1113
1114
  load_defaults("my",load_default_groups,&argc,&argv);
1115
  defaults_argv=argv;
1116
  if (get_options(argc, (char **) argv))
1117
  {
1118
    free_defaults(defaults_argv);
1119
    my_end(0);
1120
    exit(1);
1121
  }
1122
  if (status.batch && !status.line_buff &&
1123
      !(status.line_buff=batch_readline_init(opt_max_allowed_packet+512,stdin)))
1124
  {
1125
    free_defaults(defaults_argv);
1126
    my_end(0);
1127
    exit(1);
1128
  }
1129
  if (mysql_server_init(embedded_server_arg_count, embedded_server_args, 
1130
                        (char**) embedded_server_groups))
1131
  {
1132
    put_error(NULL);
1133
    free_defaults(defaults_argv);
1134
    my_end(0);
1135
    exit(1);
1136
  }
1137
  glob_buffer.realloc(512);
1138
  completion_hash_init(&ht, 128);
1139
  init_alloc_root(&hash_mem_root, 16384, 0);
1140
  bzero((char*) &mysql, sizeof(mysql));
1141
  if (sql_connect(current_host,current_db,current_user,opt_password,
1142
		  opt_silent))
1143
  {
1144
    quick= 1;					// Avoid history
1145
    status.exit_status= 1;
1146
    mysql_end(-1);
1147
  }
1148
  if (!status.batch)
1149
    ignore_errors=1;				// Don't abort monitor
1150
1151
  if (opt_sigint_ignore)
1152
    signal(SIGINT, SIG_IGN);
1153
  else
1154
    signal(SIGINT, handle_sigint);              // Catch SIGINT to clean up
1155
  signal(SIGQUIT, mysql_end);			// Catch SIGQUIT to clean up
1156
1157
#if defined(HAVE_TERMIOS_H) && defined(GWINSZ_IN_SYS_IOCTL)
1158
  /* Readline will call this if it installs a handler */
1159
  signal(SIGWINCH, window_resize);
1160
  /* call the SIGWINCH handler to get the default term width */
1161
  window_resize(0);
1162
#endif
1163
1164
  put_info("Welcome to the MySQL monitor.  Commands end with ; or \\g.",
1165
	   INFO_INFO);
1166
  sprintf((char*) glob_buffer.ptr(),
1167
	  "Your MySQL connection id is %lu\nServer version: %s\n",
1168
	  mysql_thread_id(&mysql), server_version_string(&mysql));
1169
  put_info((char*) glob_buffer.ptr(),INFO_INFO);
1170
1171
#ifdef HAVE_READLINE
1172
  initialize_readline((char*) my_progname);
1173
  if (!status.batch && !quick && !opt_html && !opt_xml)
1174
  {
1175
    /* read-history from file, default ~/.mysql_history*/
1176
    if (getenv("MYSQL_HISTFILE"))
1177
      histfile=my_strdup(getenv("MYSQL_HISTFILE"),MYF(MY_WME));
1178
    else if (getenv("HOME"))
1179
    {
1180
      histfile=(char*) my_malloc((uint) strlen(getenv("HOME"))
1181
				 + (uint) strlen("/.mysql_history")+2,
1182
				 MYF(MY_WME));
1183
      if (histfile)
1184
	sprintf(histfile,"%s/.mysql_history",getenv("HOME"));
1185
      char link_name[FN_REFLEN];
1186
      if (my_readlink(link_name, histfile, 0) == 0 &&
1187
          strncmp(link_name, "/dev/null", 10) == 0)
1188
      {
1189
        /* The .mysql_history file is a symlink to /dev/null, don't use it */
1190
        my_free(histfile, MYF(MY_ALLOW_ZERO_PTR));
1191
        histfile= 0;
1192
      }
1193
    }
1194
    if (histfile)
1195
    {
1196
      if (verbose)
1197
	tee_fprintf(stdout, "Reading history-file %s\n",histfile);
1198
      read_history(histfile);
1199
      if (!(histfile_tmp= (char*) my_malloc((uint) strlen(histfile) + 5,
1200
					    MYF(MY_WME))))
1201
      {
1202
	fprintf(stderr, "Couldn't allocate memory for temp histfile!\n");
1203
	exit(1);
1204
      }
1205
      sprintf(histfile_tmp, "%s.TMP", histfile);
1206
    }
1207
  }
1208
#endif
1209
  sprintf(buff, "%s",
1210
#ifndef NOT_YET
1211
	  "Type 'help;' or '\\h' for help. Type '\\c' to clear the buffer.\n");
1212
#else
1213
	  "Type 'help [[%]function name[%]]' to get help on usage of function.\n");
1214
#endif
1215
  put_info(buff,INFO_INFO);
1216
  status.exit_status= read_and_execute(!status.batch);
1217
  if (opt_outfile)
1218
    end_tee();
1219
  mysql_end(0);
1220
#ifndef _lint
1221
  DBUG_RETURN(0);				// Keep compiler happy
1222
#endif
1223
}
1224
1225
sig_handler mysql_end(int sig)
1226
{
1227
  mysql_close(&mysql);
1228
#ifdef HAVE_READLINE
1229
  if (!status.batch && !quick && !opt_html && !opt_xml && histfile)
1230
  {
1231
    /* write-history */
1232
    if (verbose)
1233
      tee_fprintf(stdout, "Writing history-file %s\n",histfile);
1234
    if (!write_history(histfile_tmp))
1235
      my_rename(histfile_tmp, histfile, MYF(MY_WME));
1236
  }
1237
  batch_readline_end(status.line_buff);
1238
  completion_hash_free(&ht);
1239
  free_root(&hash_mem_root,MYF(0));
1240
1241
#endif
1242
  if (sig >= 0)
1243
    put_info(sig ? "Aborted" : "Bye", INFO_RESULT);
1244
  glob_buffer.free();
1245
  old_buffer.free();
1246
  processed_prompt.free();
1247
  my_free(opt_password,MYF(MY_ALLOW_ZERO_PTR));
1248
  my_free(opt_mysql_unix_port,MYF(MY_ALLOW_ZERO_PTR));
1249
  my_free(histfile,MYF(MY_ALLOW_ZERO_PTR));
1250
  my_free(histfile_tmp,MYF(MY_ALLOW_ZERO_PTR));
1251
  my_free(current_db,MYF(MY_ALLOW_ZERO_PTR));
1252
  my_free(current_host,MYF(MY_ALLOW_ZERO_PTR));
1253
  my_free(current_user,MYF(MY_ALLOW_ZERO_PTR));
1254
  my_free(full_username,MYF(MY_ALLOW_ZERO_PTR));
1255
  my_free(part_username,MYF(MY_ALLOW_ZERO_PTR));
1256
  my_free(default_prompt,MYF(MY_ALLOW_ZERO_PTR));
1257
#ifdef HAVE_SMEM
1258
  my_free(shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR));
1259
#endif
1260
  my_free(current_prompt,MYF(MY_ALLOW_ZERO_PTR));
1261
  while (embedded_server_arg_count > 1)
1262
    my_free(embedded_server_args[--embedded_server_arg_count],MYF(0));
1263
  mysql_server_end();
1264
  free_defaults(defaults_argv);
1265
  my_end(my_end_arg);
1266
  exit(status.exit_status);
1267
}
1268
1269
1270
/*
1271
  This function handles sigint calls
1272
  If query is in process, kill query
1273
  no query in process, terminate like previous behavior
1274
 */
1275
sig_handler handle_sigint(int sig)
1276
{
1277
  char kill_buffer[40];
1278
  MYSQL *kill_mysql= NULL;
1279
1280
  /* terminate if no query being executed, or we already tried interrupting */
1281
  if (!executing_query || interrupted_query)
1282
    goto err;
1283
1284
  kill_mysql= mysql_init(kill_mysql);
1285
  if (!mysql_real_connect(kill_mysql,current_host, current_user, opt_password,
1286
                          "", opt_mysql_port, opt_mysql_unix_port,0))
1287
    goto err;
1288
1289
  /* kill_buffer is always big enough because max length of %lu is 15 */
1290
  sprintf(kill_buffer, "KILL /*!50000 QUERY */ %lu", mysql_thread_id(&mysql));
1291
  mysql_real_query(kill_mysql, kill_buffer, strlen(kill_buffer));
1292
  mysql_close(kill_mysql);
1293
  tee_fprintf(stdout, "Query aborted by Ctrl+C\n");
1294
1295
  interrupted_query= 1;
1296
1297
  return;
1298
1299
err:
1300
  mysql_end(sig);
1301
}
1302
1303
1304
#if defined(HAVE_TERMIOS_H) && defined(GWINSZ_IN_SYS_IOCTL)
1305
sig_handler window_resize(int sig)
1306
{
1307
  struct winsize window_size;
1308
1309
  if (ioctl(fileno(stdin), TIOCGWINSZ, &window_size) == 0)
1310
    terminal_width= window_size.ws_col;
1311
}
1312
#endif
1313
1314
static struct my_option my_long_options[] =
1315
{
1316
  {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
1317
   0, 0, 0, 0, 0},
1318
  {"help", 'I', "Synonym for -?", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
1319
   0, 0, 0, 0, 0},
1320
  {"auto-rehash", OPT_AUTO_REHASH,
1321
   "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.",
1322
   (uchar**) &opt_rehash, (uchar**) &opt_rehash, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0,
1323
   0, 0},
1324
  {"no-auto-rehash", 'A',
1325
   "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.",
1326
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
1327
   {"auto-vertical-output", OPT_AUTO_VERTICAL_OUTPUT,
1328
    "Automatically switch to vertical output mode if the result is wider than the terminal width.",
1329
    (uchar**) &auto_vertical_output, (uchar**) &auto_vertical_output, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1330
  {"batch", 'B',
1331
   "Don't use history file. Disable interactive behavior. (Enables --silent)", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
1332
  {"character-sets-dir", OPT_CHARSETS_DIR,
1333
   "Directory where character sets are.", (uchar**) &charsets_dir,
1334
   (uchar**) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1335
  {"column-type-info", OPT_COLUMN_TYPES, "Display column type information.",
1336
   (uchar**) &column_types_flag, (uchar**) &column_types_flag,
1337
   0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1338
  {"comments", 'c', "Preserve comments. Send comments to the server."
1339
   " The default is --skip-comments (discard comments), enable with --comments",
1340
   (uchar**) &preserve_comments, (uchar**) &preserve_comments,
1341
   0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1342
  {"compress", 'C', "Use compression in server/client protocol.",
1343
   (uchar**) &opt_compress, (uchar**) &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
1344
   0, 0, 0},
1345
1346
#ifdef DBUG_OFF
1347
  {"debug", '#', "This is a non-debug version. Catch this and exit",
1348
   0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
1349
#else
1350
  {"debug", '#', "Output debug log", (uchar**) &default_dbug_option,
1351
   (uchar**) &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
1352
#endif
1353
  {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit .",
1354
   (uchar**) &debug_check_flag, (uchar**) &debug_check_flag, 0,
1355
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1356
  {"debug-info", 'T', "Print some debug info at exit.", (uchar**) &debug_info_flag,
1357
   (uchar**) &debug_info_flag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1358
  {"database", 'D', "Database to use.", (uchar**) &current_db,
1359
   (uchar**) &current_db, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1360
  {"default-character-set", OPT_DEFAULT_CHARSET,
1361
   "Set the default character set.", (uchar**) &default_charset,
1362
   (uchar**) &default_charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1363
  {"delimiter", OPT_DELIMITER, "Delimiter to be used.", (uchar**) &delimiter_str,
1364
   (uchar**) &delimiter_str, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1365
  {"execute", 'e', "Execute command and quit. (Disables --force and history file)", 0,
1366
   0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1367
  {"vertical", 'E', "Print the output of a query (rows) vertically.",
1368
   (uchar**) &vertical, (uchar**) &vertical, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
1369
   0},
1370
  {"force", 'f', "Continue even if we get an sql error.",
1371
   (uchar**) &ignore_errors, (uchar**) &ignore_errors, 0, GET_BOOL, NO_ARG, 0, 0,
1372
   0, 0, 0, 0},
1373
  {"named-commands", 'G',
1374
   "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.",
1375
   (uchar**) &named_cmds, (uchar**) &named_cmds, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
1376
   0, 0},
1377
  {"no-named-commands", 'g',
1378
   "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.",
1379
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
1380
  {"ignore-spaces", 'i', "Ignore space after function names.", 0, 0, 0,
1381
   GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
1382
  {"local-infile", OPT_LOCAL_INFILE, "Enable/disable LOAD DATA LOCAL INFILE.",
1383
   (uchar**) &opt_local_infile,
1384
   (uchar**) &opt_local_infile, 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
1385
  {"no-beep", 'b', "Turn off beep on error.", (uchar**) &opt_nobeep,
1386
   (uchar**) &opt_nobeep, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, 
1387
  {"host", 'h', "Connect to host.", (uchar**) &current_host,
1388
   (uchar**) &current_host, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1389
  {"html", 'H', "Produce HTML output.", (uchar**) &opt_html, (uchar**) &opt_html,
1390
   0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1391
  {"xml", 'X', "Produce XML output", (uchar**) &opt_xml, (uchar**) &opt_xml, 0,
1392
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1393
  {"line-numbers", OPT_LINE_NUMBERS, "Write line numbers for errors.",
1394
   (uchar**) &line_numbers, (uchar**) &line_numbers, 0, GET_BOOL,
1395
   NO_ARG, 1, 0, 0, 0, 0, 0},  
1396
  {"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,
1397
   NO_ARG, 0, 0, 0, 0, 0, 0},
1398
  {"unbuffered", 'n', "Flush buffer after each query.", (uchar**) &unbuffered,
1399
   (uchar**) &unbuffered, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1400
  {"column-names", OPT_COLUMN_NAMES, "Write column names in results.",
1401
   (uchar**) &column_names, (uchar**) &column_names, 0, GET_BOOL,
1402
   NO_ARG, 1, 0, 0, 0, 0, 0},
1403
  {"skip-column-names", 'N',
1404
   "Don't write column names in results. WARNING: -N is deprecated, use long version of this options instead.",
1405
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
1406
  {"set-variable", 'O',
1407
   "Change the value of a variable. Please note that this option is deprecated; you can set variables directly with --variable-name=value.",
1408
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1409
  {"sigint-ignore", OPT_SIGINT_IGNORE, "Ignore SIGINT (CTRL-C)",
1410
   (uchar**) &opt_sigint_ignore,  (uchar**) &opt_sigint_ignore, 0, GET_BOOL,
1411
   NO_ARG, 0, 0, 0, 0, 0, 0},
1412
  {"one-database", 'o',
1413
   "Only update the default database. This is useful for skipping updates to other database in the update log.",
1414
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
1415
#ifdef USE_POPEN
1416
  {"pager", OPT_PAGER,
1417
   "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.",
1418
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
1419
  {"no-pager", OPT_NOPAGER,
1420
   "Disable pager and print to stdout. See interactive help (\\h) also. WARNING: option deprecated; use --disable-pager instead.",
1421
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
1422
#endif
1423
  {"password", 'p',
1424
   "Password to use when connecting to server. If password is not given it's asked from the tty.",
1425
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
1426
  {"port", 'P', "Port number to use for connection or 0 for default to, in "
1427
   "order of preference, my.cnf, $MYSQL_TCP_PORT, "
1428
#if MYSQL_PORT_DEFAULT == 0
1429
   "/etc/services, "
1430
#endif
1431
   "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").",
1432
   (uchar**) &opt_mysql_port,
1433
   (uchar**) &opt_mysql_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0,  0},
1434
  {"prompt", OPT_PROMPT, "Set the mysql prompt to this value.",
1435
   (uchar**) &current_prompt, (uchar**) &current_prompt, 0, GET_STR_ALLOC,
1436
   REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1437
  {"protocol", OPT_MYSQL_PROTOCOL, "The protocol of connection (tcp,socket,pipe,memory).",
1438
   0, 0, 0, GET_STR,  REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1439
  {"quick", 'q',
1440
   "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.",
1441
   (uchar**) &quick, (uchar**) &quick, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1442
  {"raw", 'r', "Write fields without conversion. Used with --batch.",
1443
   (uchar**) &opt_raw_data, (uchar**) &opt_raw_data, 0, GET_BOOL, NO_ARG, 0, 0, 0,
1444
   0, 0, 0},
1445
  {"reconnect", OPT_RECONNECT, "Reconnect if the connection is lost. Disable with --disable-reconnect. This option is enabled by default.", 
1446
   (uchar**) &opt_reconnect, (uchar**) &opt_reconnect, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
1447
  {"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,
1448
   0, 0},
1449
#ifdef HAVE_SMEM
1450
  {"shared-memory-base-name", OPT_SHARED_MEMORY_BASE_NAME,
1451
   "Base name of shared memory.", (uchar**) &shared_memory_base_name, (uchar**) &shared_memory_base_name, 
1452
   0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1453
#endif
1454
  {"socket", 'S', "Socket file to use for connection.",
1455
   (uchar**) &opt_mysql_unix_port, (uchar**) &opt_mysql_unix_port, 0, GET_STR_ALLOC,
1456
   REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1457
  {"table", 't', "Output in table format.", (uchar**) &output_tables,
1458
   (uchar**) &output_tables, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1459
  {"tee", OPT_TEE,
1460
   "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.",
1461
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1462
  {"no-tee", OPT_NOTEE, "Disable outfile. See interactive help (\\h) also. WARNING: option deprecated; use --disable-tee instead", 0, 0, 0, GET_NO_ARG,
1463
   NO_ARG, 0, 0, 0, 0, 0, 0},
1464
#ifndef DONT_ALLOW_USER_CHANGE
1465
  {"user", 'u', "User for login if not current user.", (uchar**) &current_user,
1466
   (uchar**) &current_user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1467
#endif
1468
  {"safe-updates", 'U', "Only allow UPDATE and DELETE that uses keys.",
1469
   (uchar**) &safe_updates, (uchar**) &safe_updates, 0, GET_BOOL, NO_ARG, 0, 0,
1470
   0, 0, 0, 0},
1471
  {"i-am-a-dummy", 'U', "Synonym for option --safe-updates, -U.",
1472
   (uchar**) &safe_updates, (uchar**) &safe_updates, 0, GET_BOOL, NO_ARG, 0, 0,
1473
   0, 0, 0, 0},
1474
  {"verbose", 'v', "Write more. (-v -v -v gives the table output format).", 0,
1475
   0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
1476
  {"version", 'V', "Output version information and exit.", 0, 0, 0,
1477
   GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
1478
  {"wait", 'w', "Wait and retry if connection is down.", 0, 0, 0, GET_NO_ARG,
1479
   NO_ARG, 0, 0, 0, 0, 0, 0},
1480
  {"connect_timeout", OPT_CONNECT_TIMEOUT,
1481
   "Number of seconds before connection timeout.",
1482
   (uchar**) &opt_connect_timeout,
1483
   (uchar**) &opt_connect_timeout, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 3600*12, 0,
1484
   0, 0},
1485
  {"max_allowed_packet", OPT_MAX_ALLOWED_PACKET,
1486
   "Max packet length to send to, or receive from server",
1487
   (uchar**) &opt_max_allowed_packet, (uchar**) &opt_max_allowed_packet, 0,
1488
   GET_ULONG, REQUIRED_ARG, 16 *1024L*1024L, 4096,
1489
   (longlong) 2*1024L*1024L*1024L, MALLOC_OVERHEAD, 1024, 0},
1490
  {"net_buffer_length", OPT_NET_BUFFER_LENGTH,
1491
   "Buffer for TCP/IP and socket communication",
1492
   (uchar**) &opt_net_buffer_length, (uchar**) &opt_net_buffer_length, 0, GET_ULONG,
1493
   REQUIRED_ARG, 16384, 1024, 512*1024*1024L, MALLOC_OVERHEAD, 1024, 0},
1494
  {"select_limit", OPT_SELECT_LIMIT,
1495
   "Automatic limit for SELECT when using --safe-updates",
1496
   (uchar**) &select_limit,
1497
   (uchar**) &select_limit, 0, GET_ULONG, REQUIRED_ARG, 1000L, 1, ULONG_MAX,
1498
   0, 1, 0},
1499
  {"max_join_size", OPT_MAX_JOIN_SIZE,
1500
   "Automatic limit for rows in a join when using --safe-updates",
1501
   (uchar**) &max_join_size,
1502
   (uchar**) &max_join_size, 0, GET_ULONG, REQUIRED_ARG, 1000000L, 1, ULONG_MAX,
1503
   0, 1, 0},
1504
  {"secure-auth", OPT_SECURE_AUTH, "Refuse client connecting to server if it"
1505
    " uses old (pre-4.1.1) protocol", (uchar**) &opt_secure_auth,
1506
    (uchar**) &opt_secure_auth, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1507
  {"server-arg", OPT_SERVER_ARG, "Send embedded server this as a parameter.",
1508
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1509
  {"show-warnings", OPT_SHOW_WARNINGS, "Show warnings after every statement.",
1510
    (uchar**) &show_warnings, (uchar**) &show_warnings, 0, GET_BOOL, NO_ARG, 
1511
    0, 0, 0, 0, 0, 0},
1512
  { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
1513
};
1514
1515
1516
static void usage(int version)
1517
{
1518
#if defined(USE_LIBEDIT_INTERFACE)
1519
  const char* readline= "";
1520
#else
1521
  const char* readline= "readline";
1522
#endif
1523
1524
#ifdef HAVE_READLINE
1525
  printf("%s  Ver %s Distrib %s, for %s (%s) using %s %s\n",
1526
	 my_progname, VER, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE,
1527
         readline, rl_library_version);
1528
#else
1529
  printf("%s  Ver %s Distrib %s, for %s (%s)\n", my_progname, VER,
1530
	MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE);
1531
#endif
1532
1533
  if (version)
1534
    return;
1535
  printf("\
1536
Copyright (C) 2000-2008 MySQL AB\n\
1537
This software comes with ABSOLUTELY NO WARRANTY. This is free software,\n\
1538
and you are welcome to modify and redistribute it under the GPL license\n");
1539
  printf("Usage: %s [OPTIONS] [database]\n", my_progname);
1540
  my_print_help(my_long_options);
1541
  print_defaults("my", load_default_groups);
1542
  my_print_variables(my_long_options);
1543
}
1544
1545
1546
my_bool
1547
get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
1548
	       char *argument)
1549
{
1550
  switch(optid) {
1551
  case OPT_CHARSETS_DIR:
1552
    strmake(mysql_charsets_dir, argument, sizeof(mysql_charsets_dir) - 1);
1553
    charsets_dir = mysql_charsets_dir;
1554
    break;
1555
  case  OPT_DEFAULT_CHARSET:
1556
    default_charset_used= 1;
1557
    break;
1558
  case OPT_DELIMITER:
1559
    if (argument == disabled_my_option) 
1560
    {
1561
      strmov(delimiter, DEFAULT_DELIMITER);
1562
    }
1563
    else 
1564
    {
1565
      /* Check that delimiter does not contain a backslash */
1566
      if (!strstr(argument, "\\")) 
1567
      {
1568
        strmake(delimiter, argument, sizeof(delimiter) - 1);
1569
      }
1570
      else 
1571
      {
1572
        put_info("DELIMITER cannot contain a backslash character", INFO_ERROR);
1573
        return 0;
1574
      } 
1575
    }
1576
    delimiter_length= (uint)strlen(delimiter);
1577
    delimiter_str= delimiter;
1578
    break;
1579
  case OPT_LOCAL_INFILE:
1580
    using_opt_local_infile=1;
1581
    break;
1582
  case OPT_TEE:
1583
    if (argument == disabled_my_option)
1584
    {
1585
      if (opt_outfile)
1586
	end_tee();
1587
    }
1588
    else
1589
      init_tee(argument);
1590
    break;
1591
  case OPT_NOTEE:
1592
    printf("WARNING: option deprecated; use --disable-tee instead.\n");
1593
    if (opt_outfile)
1594
      end_tee();
1595
    break;
1596
  case OPT_PAGER:
1597
    if (argument == disabled_my_option)
1598
      opt_nopager= 1;
1599
    else
1600
    {
1601
      opt_nopager= 0;
1602
      if (argument && strlen(argument))
1603
      {
1604
	default_pager_set= 1;
1605
	strmake(pager, argument, sizeof(pager) - 1);
1606
	strmov(default_pager, pager);
1607
      }
1608
      else if (default_pager_set)
1609
	strmov(pager, default_pager);
1610
      else
1611
	opt_nopager= 1;
1612
    }
1613
    break;
1614
  case OPT_NOPAGER:
1615
    printf("WARNING: option deprecated; use --disable-pager instead.\n");
1616
    opt_nopager= 1;
1617
    break;
1618
  case OPT_MYSQL_PROTOCOL:
1619
    opt_protocol= find_type_or_exit(argument, &sql_protocol_typelib,
1620
                                    opt->name);
1621
    break;
1622
  case OPT_SERVER_ARG:
1623
#ifdef EMBEDDED_LIBRARY
1624
    /*
1625
      When the embedded server is being tested, the client needs to be
1626
      able to pass command-line arguments to the embedded server so it can
1627
      locate the language files and data directory.
1628
    */
1629
    if (!embedded_server_arg_count)
1630
    {
1631
      embedded_server_arg_count= 1;
1632
      embedded_server_args[0]= (char*) "";
1633
    }
1634
    if (embedded_server_arg_count == MAX_SERVER_ARGS-1 ||
1635
        !(embedded_server_args[embedded_server_arg_count++]=
1636
          my_strdup(argument, MYF(MY_FAE))))
1637
    {
1638
        put_info("Can't use server argument", INFO_ERROR);
1639
        return 0;
1640
    }
1641
#else /*EMBEDDED_LIBRARY */
1642
    printf("WARNING: --server-arg option not supported in this configuration.\n");
1643
#endif
1644
    break;
1645
  case 'A':
1646
    opt_rehash= 0;
1647
    break;
1648
  case 'N':
1649
    column_names= 0;
1650
    break;
1651
  case 'e':
1652
    status.batch= 1;
1653
    status.add_to_history= 0;
1654
    if (!status.line_buff)
1655
      ignore_errors= 0;                         // do it for the first -e only
1656
    if (!(status.line_buff= batch_readline_command(status.line_buff, argument)))
1657
      return 1;
1658
    break;
1659
  case 'o':
1660
    if (argument == disabled_my_option)
1661
      one_database= 0;
1662
    else
1663
      one_database= skip_updates= 1;
1664
    break;
1665
  case 'p':
1666
    if (argument == disabled_my_option)
1667
      argument= (char*) "";			// Don't require password
1668
    if (argument)
1669
    {
1670
      char *start= argument;
1671
      my_free(opt_password, MYF(MY_ALLOW_ZERO_PTR));
1672
      opt_password= my_strdup(argument, MYF(MY_FAE));
1673
      while (*argument) *argument++= 'x';		// Destroy argument
1674
      if (*start)
1675
	start[1]=0 ;
1676
      tty_password= 0;
1677
    }
1678
    else
1679
      tty_password= 1;
1680
    break;
1681
  case '#':
1682
    DBUG_PUSH(argument ? argument : default_dbug_option);
1683
    debug_info_flag= 1;
1684
    break;
1685
  case 's':
1686
    if (argument == disabled_my_option)
1687
      opt_silent= 0;
1688
    else
1689
      opt_silent++;
1690
    break;
1691
  case 'v':
1692
    if (argument == disabled_my_option)
1693
      verbose= 0;
1694
    else
1695
      verbose++;
1696
    break;
1697
  case 'B':
1698
    status.batch= 1;
1699
    status.add_to_history= 0;
1700
    set_if_bigger(opt_silent,1);                         // more silent
1701
    break;
1702
    break;
1703
  case 'V':
1704
    usage(1);
1705
    exit(0);
1706
  case 'I':
1707
  case '?':
1708
    usage(0);
1709
    exit(0);
1710
  }
1711
  return 0;
1712
}
1713
1714
1715
static int get_options(int argc, char **argv)
1716
{
1717
  char *tmp, *pagpoint;
1718
  int ho_error;
1719
  MYSQL_PARAMETERS *mysql_params= mysql_get_parameters();
1720
1721
  tmp= (char *) getenv("MYSQL_HOST");
1722
  if (tmp)
1723
    current_host= my_strdup(tmp, MYF(MY_WME));
1724
1725
  pagpoint= getenv("PAGER");
1726
  if (!((char*) (pagpoint)))
1727
  {
1728
    strmov(pager, "stdout");
1729
    opt_nopager= 1;
1730
  }
1731
  else
1732
    strmov(pager, pagpoint);
1733
  strmov(default_pager, pager);
1734
1735
  opt_max_allowed_packet= *mysql_params->p_max_allowed_packet;
1736
  opt_net_buffer_length= *mysql_params->p_net_buffer_length;
1737
1738
  if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option)))
1739
    exit(ho_error);
1740
1741
  *mysql_params->p_max_allowed_packet= opt_max_allowed_packet;
1742
  *mysql_params->p_net_buffer_length= opt_net_buffer_length;
1743
1744
  if (status.batch) /* disable pager and outfile in this case */
1745
  {
1746
    strmov(default_pager, "stdout");
1747
    strmov(pager, "stdout");
1748
    opt_nopager= 1;
1749
    default_pager_set= 0;
1750
    opt_outfile= 0;
1751
    opt_reconnect= 0;
1752
    connect_flag= 0; /* Not in interactive mode */
1753
  }
1754
  
1755
  if (strcmp(default_charset, charset_info->csname) &&
1756
      !(charset_info= get_charset_by_csname(default_charset, 
1757
					    MY_CS_PRIMARY, MYF(MY_WME))))
1758
    exit(1);
1759
  if (argc > 1)
1760
  {
1761
    usage(0);
1762
    exit(1);
1763
  }
1764
  if (argc == 1)
1765
  {
1766
    skip_updates= 0;
1767
    my_free(current_db, MYF(MY_ALLOW_ZERO_PTR));
1768
    current_db= my_strdup(*argv, MYF(MY_WME));
1769
  }
1770
  if (tty_password)
1771
    opt_password= get_tty_password(NullS);
1772
  if (debug_info_flag)
1773
    my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO;
1774
  if (debug_check_flag)
1775
    my_end_arg= MY_CHECK_ERROR;
1776
  return(0);
1777
}
1778
1779
static int read_and_execute(bool interactive)
1780
{
1781
  char	*line;
1782
  char	in_string=0;
1783
  ulong line_number=0;
1784
  bool ml_comment= 0;  
1785
  COMMANDS *com;
1786
  status.exit_status=1;
1787
  
1788
  for (;;)
1789
  {
1790
    if (!interactive)
1791
    {
1792
      line=batch_readline(status.line_buff);
1793
      /*
1794
        Skip UTF8 Byte Order Marker (BOM) 0xEFBBBF.
1795
        Editors like "notepad" put this marker in
1796
        the very beginning of a text file when
1797
        you save the file using "Unicode UTF-8" format.
1798
      */
1799
      if (!line_number &&
1800
           (uchar) line[0] == 0xEF &&
1801
           (uchar) line[1] == 0xBB &&
1802
           (uchar) line[2] == 0xBF)
1803
        line+= 3;
1804
      line_number++;
1805
      if (!glob_buffer.length())
1806
	status.query_start_line=line_number;
1807
    }
1808
    else
1809
    {
1810
      char *prompt= (char*) (ml_comment ? "   /*> " :
1811
                             glob_buffer.is_empty() ?  construct_prompt() :
1812
			     !in_string ? "    -> " :
1813
			     in_string == '\'' ?
1814
			     "    '> " : (in_string == '`' ?
1815
			     "    `> " :
1816
			     "    \"> "));
1817
      if (opt_outfile && glob_buffer.is_empty())
1818
	fflush(OUTFILE);
1819
1820
      if (opt_outfile)
1821
	fputs(prompt, OUTFILE);
1822
      line= readline(prompt);
1823
1824
      /*
1825
        When Ctrl+d or Ctrl+z is pressed, the line may be NULL on some OS
1826
        which may cause coredump.
1827
      */
1828
      if (opt_outfile && line)
1829
	fprintf(OUTFILE, "%s\n", line);
1830
    }
1831
    if (!line)					// End of file
1832
    {
1833
      status.exit_status=0;
1834
      break;
1835
    }
1836
1837
    /*
1838
      Check if line is a mysql command line
1839
      (We want to allow help, print and clear anywhere at line start
1840
    */
1841
    if ((named_cmds || glob_buffer.is_empty())
1842
	&& !ml_comment && !in_string && (com=find_command(line,0)))
1843
    {
1844
      if ((*com->func)(&glob_buffer,line) > 0)
1845
	break;
1846
      if (glob_buffer.is_empty())		// If buffer was emptied
1847
	in_string=0;
1848
#ifdef HAVE_READLINE
1849
      if (interactive && status.add_to_history && not_in_history(line))
1850
	add_history(line);
1851
#endif
1852
      continue;
1853
    }
1854
    if (add_line(glob_buffer,line,&in_string,&ml_comment))
1855
      break;
1856
  }
1857
  /* if in batch mode, send last query even if it doesn't end with \g or go */
1858
1859
  if (!interactive && !status.exit_status)
1860
  {
1861
    remove_cntrl(glob_buffer);
1862
    if (!glob_buffer.is_empty())
1863
    {
1864
      status.exit_status=1;
1865
      if (com_go(&glob_buffer,line) <= 0)
1866
	status.exit_status=0;
1867
    }
1868
  }
1869
1870
  return status.exit_status;
1871
}
1872
1873
1874
static COMMANDS *find_command(char *name,char cmd_char)
1875
{
1876
  uint len;
1877
  char *end;
1878
  DBUG_ENTER("find_command");
1879
  DBUG_PRINT("enter",("name: '%s'  char: %d", name ? name : "NULL", cmd_char));
1880
1881
  if (!name)
1882
  {
1883
    len=0;
1884
    end=0;
1885
  }
1886
  else
1887
  {
1888
    while (my_isspace(charset_info,*name))
1889
      name++;
1890
    /*
1891
      If there is an \\g in the row or if the row has a delimiter but
1892
      this is not a delimiter command, let add_line() take care of
1893
      parsing the row and calling find_command()
1894
    */
1895
    if (strstr(name, "\\g") || (strstr(name, delimiter) &&
1896
                                !(strlen(name) >= 9 &&
1897
                                  !my_strnncoll(charset_info,
1898
                                                (uchar*) name, 9,
1899
                                                (const uchar*) "delimiter",
1900
                                                9))))
1901
      DBUG_RETURN((COMMANDS *) 0);
1902
    if ((end=strcont(name," \t")))
1903
    {
1904
      len=(uint) (end - name);
1905
      while (my_isspace(charset_info,*end))
1906
	end++;
1907
      if (!*end)
1908
	end=0;					// no arguments to function
1909
    }
1910
    else
1911
      len=(uint) strlen(name);
1912
  }
1913
1914
  for (uint i= 0; commands[i].name; i++)
1915
  {
1916
    if (commands[i].func &&
1917
	((name &&
1918
	  !my_strnncoll(charset_info,(uchar*)name,len,
1919
				     (uchar*)commands[i].name,len) &&
1920
	  !commands[i].name[len] &&
1921
	  (!end || (end && commands[i].takes_params))) ||
1922
	 !name && commands[i].cmd_char == cmd_char))
1923
    {
1924
      DBUG_PRINT("exit",("found command: %s", commands[i].name));
1925
      DBUG_RETURN(&commands[i]);
1926
    }
1927
  }
1928
  DBUG_RETURN((COMMANDS *) 0);
1929
}
1930
1931
1932
static bool add_line(String &buffer,char *line,char *in_string,
1933
                     bool *ml_comment)
1934
{
1935
  uchar inchar;
1936
  char buff[80], *pos, *out;
1937
  COMMANDS *com;
1938
  bool need_space= 0;
1939
  bool ss_comment= 0;
1940
  DBUG_ENTER("add_line");
1941
1942
  if (!line[0] && buffer.is_empty())
1943
    DBUG_RETURN(0);
1944
#ifdef HAVE_READLINE
1945
  if (status.add_to_history && line[0] && not_in_history(line))
1946
    add_history(line);
1947
#endif
1948
  char *end_of_line=line+(uint) strlen(line);
1949
1950
  for (pos=out=line ; (inchar= (uchar) *pos) ; pos++)
1951
  {
1952
    if (!preserve_comments)
1953
    {
1954
      // Skip spaces at the beggining of a statement
1955
      if (my_isspace(charset_info,inchar) && (out == line) &&
1956
          buffer.is_empty())
1957
        continue;
1958
    }
1959
        
1960
#ifdef USE_MB
1961
    // Accept multi-byte characters as-is
1962
    int length;
1963
    if (use_mb(charset_info) &&
1964
        (length= my_ismbchar(charset_info, pos, end_of_line)))
1965
    {
1966
      if (!*ml_comment || preserve_comments)
1967
      {
1968
        while (length--)
1969
          *out++ = *pos++;
1970
        pos--;
1971
      }
1972
      else
1973
        pos+= length - 1;
1974
      continue;
1975
    }
1976
#endif
1977
    if (!*ml_comment && inchar == '\\' &&
1978
        !(mysql.server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES))
1979
    {
1980
      // Found possbile one character command like \c
1981
1982
      if (!(inchar = (uchar) *++pos))
1983
	break;				// readline adds one '\'
1984
      if (*in_string || inchar == 'N')	// \N is short for NULL
1985
      {					// Don't allow commands in string
1986
	*out++='\\';
1987
	*out++= (char) inchar;
1988
	continue;
1989
      }
1990
      if ((com=find_command(NullS,(char) inchar)))
1991
      {
1992
        // Flush previously accepted characters
1993
        if (out != line)
1994
        {
1995
          buffer.append(line, (uint) (out-line));
1996
          out= line;
1997
        }
1998
        
1999
        if ((*com->func)(&buffer,pos-1) > 0)
2000
          DBUG_RETURN(1);                       // Quit
2001
        if (com->takes_params)
2002
        {
2003
          if (ss_comment)
2004
          {
2005
            /*
2006
              If a client-side macro appears inside a server-side comment,
2007
              discard all characters in the comment after the macro (that is,
2008
              until the end of the comment rather than the next delimiter)
2009
            */
2010
            for (pos++; *pos && (*pos != '*' || *(pos + 1) != '/'); pos++)
2011
              ;
2012
            pos--;
2013
          }
2014
          else
2015
          {
2016
            for (pos++ ;
2017
                 *pos && (*pos != *delimiter ||
2018
                          !is_prefix(pos + 1, delimiter + 1)) ; pos++)
2019
              ;	// Remove parameters
2020
            if (!*pos)
2021
              pos--;
2022
            else 
2023
              pos+= delimiter_length - 1; // Point at last delim char
2024
          }
2025
        }
2026
      }
2027
      else
2028
      {
2029
	sprintf(buff,"Unknown command '\\%c'.",inchar);
2030
	if (put_info(buff,INFO_ERROR) > 0)
2031
	  DBUG_RETURN(1);
2032
	*out++='\\';
2033
	*out++=(char) inchar;
2034
	continue;
2035
      }
2036
    }
2037
    else if (!*ml_comment && !*in_string &&
2038
             (end_of_line - pos) >= 10 &&
2039
             !my_strnncoll(charset_info, (uchar*) pos, 10,
2040
                           (const uchar*) "delimiter ", 10))
2041
    {
2042
      // Flush previously accepted characters
2043
      if (out != line)
2044
      {
2045
        buffer.append(line, (uint32) (out - line));
2046
        out= line;
2047
      }
2048
2049
      // Flush possible comments in the buffer
2050
      if (!buffer.is_empty())
2051
      {
2052
        if (com_go(&buffer, 0) > 0) // < 0 is not fatal
2053
          DBUG_RETURN(1);
2054
        buffer.length(0);
2055
      }
2056
2057
      /*
2058
        Delimiter wants the get rest of the given line as argument to
2059
        allow one to change ';' to ';;' and back
2060
      */
2061
      buffer.append(pos);
2062
      if (com_delimiter(&buffer, pos) > 0)
2063
        DBUG_RETURN(1);
2064
2065
      buffer.length(0);
2066
      break;
2067
    }
2068
    else if (!*ml_comment && !*in_string && is_prefix(pos, delimiter))
2069
    {
2070
      // Found a statement. Continue parsing after the delimiter
2071
      pos+= delimiter_length;
2072
2073
      if (preserve_comments)
2074
      {
2075
        while (my_isspace(charset_info, *pos))
2076
          *out++= *pos++;
2077
      }
2078
      // Flush previously accepted characters
2079
      if (out != line)
2080
      {
2081
        buffer.append(line, (uint32) (out-line));
2082
        out= line;
2083
      }
2084
2085
      if (preserve_comments && ((*pos == '#') ||
2086
                                ((*pos == '-') &&
2087
                                 (pos[1] == '-') &&
2088
                                 my_isspace(charset_info, pos[2]))))
2089
      {
2090
        // Add trailing single line comments to this statement
2091
        buffer.append(pos);
2092
        pos+= strlen(pos);
2093
      }
2094
2095
      pos--;
2096
2097
      if ((com= find_command(buffer.c_ptr(), 0)))
2098
      {
2099
          
2100
        if ((*com->func)(&buffer, buffer.c_ptr()) > 0)
2101
          DBUG_RETURN(1);                       // Quit 
2102
      }
2103
      else
2104
      {
2105
        if (com_go(&buffer, 0) > 0)             // < 0 is not fatal
2106
          DBUG_RETURN(1);
2107
      }
2108
      buffer.length(0);
2109
    }
2110
    else if (!*ml_comment && (!*in_string && (inchar == '#' ||
2111
			      inchar == '-' && pos[1] == '-' &&
2112
			      my_isspace(charset_info,pos[2]))))
2113
    {
2114
      // Flush previously accepted characters
2115
      if (out != line)
2116
      {
2117
        buffer.append(line, (uint32) (out - line));
2118
        out= line;
2119
      }
2120
2121
      // comment to end of line
2122
      if (preserve_comments)
2123
        buffer.append(pos);
2124
2125
      break;
2126
    }
2127
    else if (!*in_string && inchar == '/' && *(pos+1) == '*' &&
2128
	     *(pos+2) != '!')
2129
    {
2130
      if (preserve_comments)
2131
      {
2132
        *out++= *pos++;                       // copy '/'
2133
        *out++= *pos;                         // copy '*'
2134
      }
2135
      else
2136
        pos++;
2137
      *ml_comment= 1;
2138
      if (out != line)
2139
      {
2140
        buffer.append(line,(uint) (out-line));
2141
        out=line;
2142
      }
2143
    }
2144
    else if (*ml_comment && !ss_comment && inchar == '*' && *(pos + 1) == '/')
2145
    {
2146
      if (preserve_comments)
2147
      {
2148
        *out++= *pos++;                       // copy '*'
2149
        *out++= *pos;                         // copy '/'
2150
      }
2151
      else
2152
        pos++;
2153
      *ml_comment= 0;
2154
      if (out != line)
2155
      {
2156
        buffer.append(line, (uint32) (out - line));
2157
        out= line;
2158
      }
2159
      // Consumed a 2 chars or more, and will add 1 at most,
2160
      // so using the 'line' buffer to edit data in place is ok.
2161
      need_space= 1;
2162
    }      
2163
    else
2164
    {						// Add found char to buffer
2165
      if (!*in_string && inchar == '/' && *(pos + 1) == '*' &&
2166
          *(pos + 2) == '!')
2167
        ss_comment= 1;
2168
      else if (!*in_string && ss_comment && inchar == '*' && *(pos + 1) == '/')
2169
        ss_comment= 0;
2170
      if (inchar == *in_string)
2171
	*in_string= 0;
2172
      else if (!*ml_comment && !*in_string &&
2173
	       (inchar == '\'' || inchar == '"' || inchar == '`'))
2174
	*in_string= (char) inchar;
2175
      if (!*ml_comment || preserve_comments)
2176
      {
2177
        if (need_space && !my_isspace(charset_info, (char)inchar))
2178
          *out++= ' ';
2179
        need_space= 0;
2180
        *out++= (char) inchar;
2181
      }
2182
    }
2183
  }
2184
  if (out != line || !buffer.is_empty())
2185
  {
2186
    *out++='\n';
2187
    uint length=(uint) (out-line);
2188
    if (buffer.length() + length >= buffer.alloced_length())
2189
      buffer.realloc(buffer.length()+length+IO_SIZE);
2190
    if ((!*ml_comment || preserve_comments) && buffer.append(line, length))
2191
      DBUG_RETURN(1);
2192
  }
2193
  DBUG_RETURN(0);
2194
}
2195
2196
/*****************************************************************
2197
	    Interface to Readline Completion
2198
******************************************************************/
2199
2200
#ifdef HAVE_READLINE
2201
2202
static char *new_command_generator(const char *text, int);
2203
extern "C" char **new_mysql_completion (const char *text, int start, int end);
2204
2205
/*
2206
  Tell the GNU Readline library how to complete.  We want to try to complete
2207
  on command names if this is the first word in the line, or on filenames
2208
  if not.
2209
*/
2210
2211
#if defined(USE_NEW_READLINE_INTERFACE) || defined(USE_LIBEDIT_INTERFACE)
2212
extern "C" char *no_completion(const char*,int)
2213
#else
2214
extern "C" char *no_completion()
2215
#endif
2216
{
2217
  return 0;					/* No filename completion */
2218
}
2219
2220
/*	glues pieces of history back together if in pieces   */
2221
static void fix_history(String *final_command) 
2222
{
2223
  int total_lines = 1;
2224
  char *ptr = final_command->c_ptr();
2225
  String fixed_buffer; 	/* Converted buffer */
2226
  char str_char = '\0';  /* Character if we are in a string or not */
2227
  
2228
  /* find out how many lines we have and remove newlines */
2229
  while (*ptr != '\0') 
2230
  {
2231
    switch (*ptr) {
2232
      /* string character */
2233
    case '"':
2234
    case '\'':
2235
    case '`':
2236
      if (str_char == '\0')	/* open string */
2237
	str_char = *ptr;
2238
      else if (str_char == *ptr)   /* close string */
2239
	str_char = '\0';
2240
      fixed_buffer.append(ptr,1);
2241
      break;
2242
    case '\n':
2243
      /* 
2244
	 not in string, change to space
2245
	 if in string, leave it alone 
2246
      */
2247
      fixed_buffer.append(str_char == '\0' ? " " : "\n");
2248
      total_lines++;
2249
      break;
2250
    case '\\':
2251
      fixed_buffer.append('\\');
2252
      /* need to see if the backslash is escaping anything */
2253
      if (str_char) 
2254
      {
2255
	ptr++;
2256
	/* special characters that need escaping */
2257
	if (*ptr == '\'' || *ptr == '"' || *ptr == '\\')
2258
	  fixed_buffer.append(ptr,1);
2259
	else
2260
	  ptr--;
2261
      }
2262
      break;
2263
      
2264
    default:
2265
      fixed_buffer.append(ptr,1);
2266
    }
2267
    ptr++;
2268
  }
2269
  if (total_lines > 1)			
2270
    add_history(fixed_buffer.ptr());
2271
}
2272
2273
/*	
2274
  returns 0 if line matches the previous history entry
2275
  returns 1 if the line doesn't match the previous history entry
2276
*/
2277
static int not_in_history(const char *line) 
2278
{
2279
  HIST_ENTRY *oldhist = history_get(history_length);
2280
  
2281
  if (oldhist == 0)
2282
    return 1;
2283
  if (strcmp(oldhist->line,line) == 0)
2284
    return 0;
2285
  return 1;
2286
}
2287
2288
static void initialize_readline (char *name)
2289
{
2290
  /* Allow conditional parsing of the ~/.inputrc file. */
2291
  rl_readline_name = name;
2292
2293
  /* Tell the completer that we want a crack first. */
2294
  rl_attempted_completion_function= (rl_completion_func_t*)&new_mysql_completion;
2295
  rl_completion_entry_function= (rl_compentry_func_t*)&no_completion;
2296
}
2297
2298
/*
2299
  Attempt to complete on the contents of TEXT.  START and END show the
2300
  region of TEXT that contains the word to complete.  We can use the
2301
  entire line in case we want to do some simple parsing.  Return the
2302
  array of matches, or NULL if there aren't any.
2303
*/
2304
2305
char **new_mysql_completion (const char *text,
2306
                             int start __attribute__((unused)),
2307
                             int end __attribute__((unused)))
2308
{
2309
  if (!status.batch && !quick)
2310
    return rl_completion_matches(text, new_command_generator);
2311
  else
2312
    return (char**) 0;
2313
}
2314
2315
static char *new_command_generator(const char *text,int state)
2316
{
2317
  static int textlen;
2318
  char *ptr;
2319
  static Bucket *b;
2320
  static entry *e;
2321
  static uint i;
2322
2323
  if (!state)
2324
    textlen=(uint) strlen(text);
2325
2326
  if (textlen>0)
2327
  {						/* lookup in the hash */
2328
    if (!state)
2329
    {
2330
      uint len;
2331
2332
      b = find_all_matches(&ht,text,(uint) strlen(text),&len);
2333
      if (!b)
2334
	return NullS;
2335
      e = b->pData;
2336
    }
2337
2338
    if (e)
2339
    {
2340
      ptr= strdup(e->str);
2341
      e = e->pNext;
2342
      return ptr;
2343
    }
2344
  }
2345
  else
2346
  { /* traverse the entire hash, ugly but works */
2347
2348
    if (!state)
2349
    {
2350
      /* find the first used bucket */
2351
      for (i=0 ; i < ht.nTableSize ; i++)
2352
      {
2353
	if (ht.arBuckets[i])
2354
	{
2355
	  b = ht.arBuckets[i];
2356
	  e = b->pData;
2357
	  break;
2358
	}
2359
      }
2360
    }
2361
    ptr= NullS;
2362
    while (e && !ptr)
2363
    {					/* find valid entry in bucket */
2364
      if ((uint) strlen(e->str) == b->nKeyLength)
2365
	ptr = strdup(e->str);
2366
      /* find the next used entry */
2367
      e = e->pNext;
2368
      if (!e)
2369
      { /* find the next used bucket */
2370
	b = b->pNext;
2371
	if (!b)
2372
	{
2373
	  for (i++ ; i<ht.nTableSize; i++)
2374
	  {
2375
	    if (ht.arBuckets[i])
2376
	    {
2377
	      b = ht.arBuckets[i];
2378
	      e = b->pData;
2379
	      break;
2380
	    }
2381
	  }
2382
	}
2383
	else
2384
	  e = b->pData;
2385
      }
2386
    }
2387
    if (ptr)
2388
      return ptr;
2389
  }
2390
  return NullS;
2391
}
2392
2393
2394
/* Build up the completion hash */
2395
2396
static void build_completion_hash(bool rehash, bool write_info)
2397
{
2398
  COMMANDS *cmd=commands;
2399
  MYSQL_RES *databases=0,*tables=0;
2400
  MYSQL_RES *fields;
2401
  static char ***field_names= 0;
2402
  MYSQL_ROW database_row,table_row;
2403
  MYSQL_FIELD *sql_field;
2404
  char buf[NAME_LEN*2+2];		 // table name plus field name plus 2
2405
  int i,j,num_fields;
2406
  DBUG_ENTER("build_completion_hash");
2407
2408
  if (status.batch || quick || !current_db)
2409
    DBUG_VOID_RETURN;			// We don't need completion in batches
2410
  if (!rehash)
2411
    DBUG_VOID_RETURN;
2412
2413
  /* Free old used memory */
2414
  if (field_names)
2415
    field_names=0;
2416
  completion_hash_clean(&ht);
2417
  free_root(&hash_mem_root,MYF(0));
2418
2419
  /* hash this file's known subset of SQL commands */
2420
  while (cmd->name) {
2421
    add_word(&ht,(char*) cmd->name);
2422
    cmd++;
2423
  }
2424
2425
  /* hash MySQL functions (to be implemented) */
2426
2427
  /* hash all database names */
2428
  if (mysql_query(&mysql,"show databases") == 0)
2429
  {
2430
    if (!(databases = mysql_store_result(&mysql)))
2431
      put_info(mysql_error(&mysql),INFO_INFO);
2432
    else
2433
    {
2434
      while ((database_row=mysql_fetch_row(databases)))
2435
      {
2436
	char *str=strdup_root(&hash_mem_root, (char*) database_row[0]);
2437
	if (str)
2438
	  add_word(&ht,(char*) str);
2439
      }
2440
      mysql_free_result(databases);
2441
    }
2442
  }
2443
  /* hash all table names */
2444
  if (mysql_query(&mysql,"show tables")==0)
2445
  {
2446
    if (!(tables = mysql_store_result(&mysql)))
2447
      put_info(mysql_error(&mysql),INFO_INFO);
2448
    else
2449
    {
2450
      if (mysql_num_rows(tables) > 0 && !opt_silent && write_info)
2451
      {
2452
	tee_fprintf(stdout, "\
2453
Reading table information for completion of table and column names\n\
2454
You can turn off this feature to get a quicker startup with -A\n\n");
2455
      }
2456
      while ((table_row=mysql_fetch_row(tables)))
2457
      {
2458
	char *str=strdup_root(&hash_mem_root, (char*) table_row[0]);
2459
	if (str &&
2460
	    !completion_hash_exists(&ht,(char*) str, (uint) strlen(str)))
2461
	  add_word(&ht,str);
2462
      }
2463
    }
2464
  }
2465
2466
  /* hash all field names, both with the table prefix and without it */
2467
  if (!tables)					/* no tables */
2468
  {
2469
    DBUG_VOID_RETURN;
2470
  }
2471
  mysql_data_seek(tables,0);
2472
  if (!(field_names= (char ***) alloc_root(&hash_mem_root,sizeof(char **) *
2473
					   (uint) (mysql_num_rows(tables)+1))))
2474
  {
2475
    mysql_free_result(tables);
2476
    DBUG_VOID_RETURN;
2477
  }
2478
  i=0;
2479
  while ((table_row=mysql_fetch_row(tables)))
2480
  {
2481
    if ((fields=mysql_list_fields(&mysql,(const char*) table_row[0],NullS)))
2482
    {
2483
      num_fields=mysql_num_fields(fields);
2484
      if (!(field_names[i] = (char **) alloc_root(&hash_mem_root,
2485
						  sizeof(char *) *
2486
						  (num_fields*2+1))))
2487
      {
2488
        mysql_free_result(fields);
2489
        break;
2490
      }
2491
      field_names[i][num_fields*2]= '\0';
2492
      j=0;
2493
      while ((sql_field=mysql_fetch_field(fields)))
2494
      {
2495
	sprintf(buf,"%.64s.%.64s",table_row[0],sql_field->name);
2496
	field_names[i][j] = strdup_root(&hash_mem_root,buf);
2497
	add_word(&ht,field_names[i][j]);
2498
	field_names[i][num_fields+j] = strdup_root(&hash_mem_root,
2499
						   sql_field->name);
2500
	if (!completion_hash_exists(&ht,field_names[i][num_fields+j],
2501
				    (uint) strlen(field_names[i][num_fields+j])))
2502
	  add_word(&ht,field_names[i][num_fields+j]);
2503
	j++;
2504
      }
2505
      mysql_free_result(fields);
2506
    }
2507
    else
2508
      field_names[i]= 0;
2509
2510
    i++;
2511
  }
2512
  mysql_free_result(tables);
2513
  field_names[i]=0;				// End pointer
2514
  DBUG_VOID_RETURN;
2515
}
2516
2517
	/* for gnu readline */
2518
2519
#ifndef HAVE_INDEX
2520
extern "C" {
2521
extern char *index(const char *,int c),*rindex(const char *,int);
2522
2523
char *index(const char *s,int c)
2524
{
2525
  for (;;)
2526
  {
2527
     if (*s == (char) c) return (char*) s;
2528
     if (!*s++) return NullS;
2529
  }
2530
}
2531
2532
char *rindex(const char *s,int c)
2533
{
2534
  register char *t;
2535
2536
  t = NullS;
2537
  do if (*s == (char) c) t = (char*) s; while (*s++);
2538
  return (char*) t;
2539
}
2540
}
2541
#endif
2542
#endif /* HAVE_READLINE */
2543
2544
2545
static int reconnect(void)
2546
{
2547
  /* purecov: begin tested */
2548
  if (opt_reconnect)
2549
  {
2550
    put_info("No connection. Trying to reconnect...",INFO_INFO);
2551
    (void) com_connect((String *) 0, 0);
2552
    if (opt_rehash)
2553
      com_rehash(NULL, NULL);
2554
  }
2555
  if (!connected)
2556
    return put_info("Can't connect to the server\n",INFO_ERROR);
2557
  /* purecov: end */
2558
  return 0;
2559
}
2560
2561
static void get_current_db()
2562
{
2563
  MYSQL_RES *res;
2564
2565
  my_free(current_db, MYF(MY_ALLOW_ZERO_PTR));
2566
  current_db= NULL;
2567
  /* In case of error below current_db will be NULL */
2568
  if (!mysql_query(&mysql, "SELECT DATABASE()") &&
2569
      (res= mysql_use_result(&mysql)))
2570
  {
2571
    MYSQL_ROW row= mysql_fetch_row(res);
2572
    if (row[0])
2573
      current_db= my_strdup(row[0], MYF(MY_WME));
2574
    mysql_free_result(res);
2575
  }
2576
}
2577
2578
/***************************************************************************
2579
 The different commands
2580
***************************************************************************/
2581
2582
int mysql_real_query_for_lazy(const char *buf, int length)
2583
{
2584
  for (uint retry=0;; retry++)
2585
  {
2586
    int error;
2587
    if (!mysql_real_query(&mysql,buf,length))
2588
      return 0;
2589
    error= put_error(&mysql);
2590
    if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR || retry > 1 ||
2591
        !opt_reconnect)
2592
      return error;
2593
    if (reconnect())
2594
      return error;
2595
  }
2596
}
2597
2598
int mysql_store_result_for_lazy(MYSQL_RES **result)
2599
{
2600
  if ((*result=mysql_store_result(&mysql)))
2601
    return 0;
2602
2603
  if (mysql_error(&mysql)[0])
2604
    return put_error(&mysql);
2605
  return 0;
2606
}
2607
2608
static void print_help_item(MYSQL_ROW *cur, int num_name, int num_cat, char *last_char)
2609
{
2610
  char ccat= (*cur)[num_cat][0];
2611
  if (*last_char != ccat)
2612
  {
2613
    put_info(ccat == 'Y' ? "categories:" : "topics:", INFO_INFO);
2614
    *last_char= ccat;
2615
  }
2616
  tee_fprintf(PAGER, "   %s\n", (*cur)[num_name]);
2617
}
2618
2619
2620
static int com_server_help(String *buffer __attribute__((unused)),
2621
			   char *line __attribute__((unused)), char *help_arg)
2622
{
2623
  MYSQL_ROW cur;
2624
  const char *server_cmd= buffer->ptr();
2625
  char cmd_buf[100];
2626
  MYSQL_RES *result;
2627
  int error;
2628
  
2629
  if (help_arg[0] != '\'')
2630
  {
2631
	char *end_arg= strend(help_arg);
2632
	if(--end_arg)
2633
	{
2634
		while (my_isspace(charset_info,*end_arg))
2635
          end_arg--;
2636
		*++end_arg= '\0';
2637
	}
2638
	(void) strxnmov(cmd_buf, sizeof(cmd_buf), "help '", help_arg, "'", NullS);
2639
    server_cmd= cmd_buf;
2640
  }
2641
  
2642
  if (!status.batch)
2643
  {
2644
    old_buffer= *buffer;
2645
    old_buffer.copy();
2646
  }
2647
2648
  if (!connected && reconnect())
2649
    return 1;
2650
2651
  if ((error= mysql_real_query_for_lazy(server_cmd,(int)strlen(server_cmd))) ||
2652
      (error= mysql_store_result_for_lazy(&result)))
2653
    return error;
2654
2655
  if (result)
2656
  {
2657
    unsigned int num_fields= mysql_num_fields(result);
2658
    my_ulonglong num_rows= mysql_num_rows(result);
2659
    mysql_fetch_fields(result);
2660
    if (num_fields==3 && num_rows==1)
2661
    {
2662
      if (!(cur= mysql_fetch_row(result)))
2663
      {
2664
	error= -1;
2665
	goto err;
2666
      }
2667
2668
      init_pager();
2669
      tee_fprintf(PAGER,   "Name: \'%s\'\n", cur[0]);
2670
      tee_fprintf(PAGER,   "Description:\n%s", cur[1]);
2671
      if (cur[2] && *((char*)cur[2]))
2672
	tee_fprintf(PAGER, "Examples:\n%s", cur[2]);
2673
      tee_fprintf(PAGER,   "\n");
2674
      end_pager();
2675
    }
2676
    else if (num_fields >= 2 && num_rows)
2677
    {
2678
      init_pager();
2679
      char last_char= 0;
2680
2681
      int num_name= 0, num_cat= 0;
2682
2683
      if (num_fields == 2)
2684
      {
2685
	put_info("Many help items for your request exist.", INFO_INFO);
2686
	put_info("To make a more specific request, please type 'help <item>',\nwhere <item> is one of the following", INFO_INFO);
2687
	num_name= 0;
2688
	num_cat= 1;
2689
      }
2690
      else if ((cur= mysql_fetch_row(result)))
2691
      {
2692
	tee_fprintf(PAGER, "You asked for help about help category: \"%s\"\n", cur[0]);
2693
	put_info("For more information, type 'help <item>', where <item> is one of the following", INFO_INFO);
2694
	num_name= 1;
2695
	num_cat= 2;
2696
	print_help_item(&cur,1,2,&last_char);
2697
      }
2698
2699
      while ((cur= mysql_fetch_row(result)))
2700
	print_help_item(&cur,num_name,num_cat,&last_char);
2701
      tee_fprintf(PAGER, "\n");
2702
      end_pager();
2703
    }
2704
    else
2705
    {
2706
      put_info("\nNothing found", INFO_INFO);
2707
      put_info("Please try to run 'help contents' for a list of all accessible topics\n", INFO_INFO);
2708
    }
2709
  }
2710
2711
err:
2712
  mysql_free_result(result);
2713
  return error;
2714
}
2715
2716
static int
2717
com_help(String *buffer __attribute__((unused)),
2718
	 char *line __attribute__((unused)))
2719
{
2720
  register int i, j;
2721
  char * help_arg= strchr(line,' '), buff[32], *end;
2722
  if (help_arg)
2723
  {
2724
    while (my_isspace(charset_info,*help_arg))
2725
      help_arg++;
2726
	if (*help_arg)	  
2727
	  return com_server_help(buffer,line,help_arg);
2728
  }
2729
2730
  put_info("\nFor information about MySQL products and services, visit:\n"
2731
           "   http://www.mysql.com/\n"
2732
           "For developer information, including the MySQL Reference Manual, "
2733
           "visit:\n"
2734
           "   http://dev.mysql.com/\n"
2735
           "To buy MySQL Network Support, training, or other products, visit:\n"
2736
           "   https://shop.mysql.com/\n", INFO_INFO);
2737
  put_info("List of all MySQL commands:", INFO_INFO);
2738
  if (!named_cmds)
2739
    put_info("Note that all text commands must be first on line and end with ';'",INFO_INFO);
2740
  for (i = 0; commands[i].name; i++)
2741
  {
2742
    end= strmov(buff, commands[i].name);
2743
    for (j= (int)strlen(commands[i].name); j < 10; j++)
2744
      end= strmov(end, " ");
2745
    if (commands[i].func)
2746
      tee_fprintf(stdout, "%s(\\%c) %s\n", buff,
2747
		  commands[i].cmd_char, commands[i].doc);
2748
  }
2749
  if (connected && mysql_get_server_version(&mysql) >= 40100)
2750
    put_info("\nFor server side help, type 'help contents'\n", INFO_INFO);
2751
  return 0;
2752
}
2753
2754
2755
	/* ARGSUSED */
2756
static int
2757
com_clear(String *buffer,char *line __attribute__((unused)))
2758
{
2759
#ifdef HAVE_READLINE
2760
  if (status.add_to_history)
2761
    fix_history(buffer);
2762
#endif
2763
  buffer->length(0);
2764
  return 0;
2765
}
2766
2767
	/* ARGSUSED */
2768
static int
2769
com_charset(String *buffer __attribute__((unused)), char *line)
2770
{
2771
  char buff[256], *param;
2772
  CHARSET_INFO * new_cs;
2773
  strmake(buff, line, sizeof(buff) - 1);
2774
  param= get_arg(buff, 0);
2775
  if (!param || !*param)
2776
  {
2777
    return put_info("Usage: \\C char_setname | charset charset_name", 
2778
		    INFO_ERROR, 0);
2779
  }
2780
  new_cs= get_charset_by_csname(param, MY_CS_PRIMARY, MYF(MY_WME));
2781
  if (new_cs)
2782
  {
2783
    charset_info= new_cs;
2784
    mysql_set_character_set(&mysql, charset_info->csname);
2785
    default_charset= (char *)charset_info->csname;
2786
    default_charset_used= 1;
2787
    put_info("Charset changed", INFO_INFO);
2788
  }
2789
  else put_info("Charset is not found", INFO_INFO);
2790
  return 0;
2791
}
2792
2793
/*
2794
  Execute command
2795
  Returns: 0  if ok
2796
          -1 if not fatal error
2797
	  1  if fatal error
2798
*/
2799
2800
2801
static int
2802
com_go(String *buffer,char *line __attribute__((unused)))
2803
{
2804
  char		buff[200]; /* about 110 chars used so far */
2805
  char		time_buff[52+3+1]; /* time max + space&parens + NUL */
2806
  MYSQL_RES	*result;
2807
  ulong		timer, warnings= 0;
2808
  uint		error= 0;
2809
  int           err= 0;
2810
2811
  interrupted_query= 0;
2812
  if (!status.batch)
2813
  {
2814
    old_buffer= *buffer;			// Save for edit command
2815
    old_buffer.copy();
2816
  }
2817
2818
  /* Remove garbage for nicer messages */
2819
  remove_cntrl(*buffer);
2820
2821
  if (buffer->is_empty())
2822
  {
2823
    if (status.batch)				// Ignore empty quries
2824
      return 0;
2825
    return put_info("No query specified\n",INFO_ERROR);
2826
2827
  }
2828
  if (!connected && reconnect())
2829
  {
2830
    buffer->length(0);				// Remove query on error
2831
    return opt_reconnect ? -1 : 1;          // Fatal error
2832
  }
2833
  if (verbose)
2834
    (void) com_print(buffer,0);
2835
2836
  if (skip_updates &&
2837
      (buffer->length() < 4 || my_strnncoll(charset_info,
2838
					    (const uchar*)buffer->ptr(),4,
2839
					    (const uchar*)"SET ",4)))
2840
  {
2841
    (void) put_info("Ignoring query to other database",INFO_INFO);
2842
    return 0;
2843
  }
2844
2845
  timer=start_timer();
2846
  executing_query= 1;
2847
  error= mysql_real_query_for_lazy(buffer->ptr(),buffer->length());
2848
2849
#ifdef HAVE_READLINE
2850
  if (status.add_to_history) 
2851
  {  
2852
    buffer->append(vertical ? "\\G" : delimiter);
2853
    /* Append final command onto history */
2854
    fix_history(buffer);
2855
  }
2856
#endif
2857
2858
  buffer->length(0);
2859
2860
  if (error)
2861
    goto end;
2862
2863
  do
2864
  {
2865
    char *pos;
2866
2867
    if (quick)
2868
    {
2869
      if (!(result=mysql_use_result(&mysql)) && mysql_field_count(&mysql))
2870
      {
2871
        error= put_error(&mysql);
2872
        goto end;
2873
      }
2874
    }
2875
    else
2876
    {
2877
      error= mysql_store_result_for_lazy(&result);
2878
      if (error)
2879
        goto end;
2880
    }
2881
2882
    if (verbose >= 3 || !opt_silent)
2883
      mysql_end_timer(timer,time_buff);
2884
    else
2885
      time_buff[0]= '\0';
2886
2887
    /* Every branch must truncate  buff . */
2888
    if (result)
2889
    {
2890
      if (!mysql_num_rows(result) && ! quick && !column_types_flag)
2891
      {
2892
	strmov(buff, "Empty set");
2893
        if (opt_xml)
2894
        { 
2895
          /*
2896
            We must print XML header and footer
2897
            to produce a well-formed XML even if
2898
            the result set is empty (Bug#27608).
2899
          */
2900
          init_pager();
2901
          print_table_data_xml(result);
2902
          end_pager();
2903
        }
2904
      }
2905
      else
2906
      {
2907
	init_pager();
2908
	if (opt_html)
2909
	  print_table_data_html(result);
2910
	else if (opt_xml)
2911
	  print_table_data_xml(result);
2912
  else if (vertical || (auto_vertical_output && (terminal_width < get_result_width(result))))
2913
	  print_table_data_vertically(result);
2914
	else if (opt_silent && verbose <= 2 && !output_tables)
2915
	  print_tab_data(result);
2916
	else
2917
	  print_table_data(result);
2918
	sprintf(buff,"%ld %s in set",
2919
		(long) mysql_num_rows(result),
2920
		(long) mysql_num_rows(result) == 1 ? "row" : "rows");
2921
	end_pager();
2922
        if (mysql_errno(&mysql))
2923
          error= put_error(&mysql);
2924
      }
2925
    }
2926
    else if (mysql_affected_rows(&mysql) == ~(ulonglong) 0)
2927
      strmov(buff,"Query OK");
2928
    else
2929
      sprintf(buff,"Query OK, %ld %s affected",
2930
	      (long) mysql_affected_rows(&mysql),
2931
	      (long) mysql_affected_rows(&mysql) == 1 ? "row" : "rows");
2932
2933
    pos=strend(buff);
2934
    if ((warnings= mysql_warning_count(&mysql)))
2935
    {
2936
      *pos++= ',';
2937
      *pos++= ' ';
2938
      pos=int10_to_str(warnings, pos, 10);
2939
      pos=strmov(pos, " warning");
2940
      if (warnings != 1)
2941
	*pos++= 's';
2942
    }
2943
    strmov(pos, time_buff);
2944
    put_info(buff,INFO_RESULT);
2945
    if (mysql_info(&mysql))
2946
      put_info(mysql_info(&mysql),INFO_RESULT);
2947
    put_info("",INFO_RESULT);			// Empty row
2948
2949
    if (result && !mysql_eof(result))	/* Something wrong when using quick */
2950
      error= put_error(&mysql);
2951
    else if (unbuffered)
2952
      fflush(stdout);
2953
    mysql_free_result(result);
2954
  } while (!(err= mysql_next_result(&mysql)));
2955
  if (err >= 1)
2956
    error= put_error(&mysql);
2957
2958
end:
2959
2960
 /* Show warnings if any or error occured */
2961
  if (show_warnings == 1 && (warnings >= 1 || error))
2962
    print_warnings();
2963
2964
  if (!error && !status.batch && 
2965
      (mysql.server_status & SERVER_STATUS_DB_DROPPED))
2966
    get_current_db();
2967
2968
  executing_query= 0;
2969
  return error;				/* New command follows */
2970
}
2971
2972
2973
static void init_pager()
2974
{
2975
#ifdef USE_POPEN
2976
  if (!opt_nopager)
2977
  {
2978
    if (!(PAGER= popen(pager, "w")))
2979
    {
2980
      tee_fprintf(stdout, "popen() failed! defaulting PAGER to stdout!\n");
2981
      PAGER= stdout;
2982
    }
2983
  }
2984
  else
2985
#endif
2986
    PAGER= stdout;
2987
}
2988
2989
static void end_pager()
2990
{
2991
#ifdef USE_POPEN
2992
  if (!opt_nopager)
2993
    pclose(PAGER);
2994
#endif
2995
}
2996
2997
2998
static void init_tee(const char *file_name)
2999
{
3000
  FILE* new_outfile;
3001
  if (opt_outfile)
3002
    end_tee();
3003
  if (!(new_outfile= my_fopen(file_name, O_APPEND | O_WRONLY, MYF(MY_WME))))
3004
  {
3005
    tee_fprintf(stdout, "Error logging to file '%s'\n", file_name);
3006
    return;
3007
  }
3008
  OUTFILE = new_outfile;
3009
  strmake(outfile, file_name, FN_REFLEN-1);
3010
  tee_fprintf(stdout, "Logging to file '%s'\n", file_name);
3011
  opt_outfile= 1;
3012
  return;
3013
}
3014
3015
3016
static void end_tee()
3017
{
3018
  my_fclose(OUTFILE, MYF(0));
3019
  OUTFILE= 0;
3020
  opt_outfile= 0;
3021
  return;
3022
}
3023
3024
3025
static int
3026
com_ego(String *buffer,char *line)
3027
{
3028
  int result;
3029
  bool oldvertical=vertical;
3030
  vertical=1;
3031
  result=com_go(buffer,line);
3032
  vertical=oldvertical;
3033
  return result;
3034
}
3035
3036
3037
static const char *fieldtype2str(enum enum_field_types type)
3038
{
3039
  switch (type) {
3040
    case MYSQL_TYPE_BIT:         return "BIT";
3041
    case MYSQL_TYPE_BLOB:        return "BLOB";
3042
    case MYSQL_TYPE_DATE:        return "DATE";
3043
    case MYSQL_TYPE_DATETIME:    return "DATETIME";
3044
    case MYSQL_TYPE_NEWDECIMAL:  return "NEWDECIMAL";
3045
    case MYSQL_TYPE_DECIMAL:     return "DECIMAL";
3046
    case MYSQL_TYPE_DOUBLE:      return "DOUBLE";
3047
    case MYSQL_TYPE_ENUM:        return "ENUM";
3048
    case MYSQL_TYPE_FLOAT:       return "FLOAT";
3049
    case MYSQL_TYPE_GEOMETRY:    return "GEOMETRY";
3050
    case MYSQL_TYPE_INT24:       return "INT24";
3051
    case MYSQL_TYPE_LONG:        return "LONG";
3052
    case MYSQL_TYPE_LONGLONG:    return "LONGLONG";
3053
    case MYSQL_TYPE_LONG_BLOB:   return "LONG_BLOB";
3054
    case MYSQL_TYPE_MEDIUM_BLOB: return "MEDIUM_BLOB";
3055
    case MYSQL_TYPE_NEWDATE:     return "NEWDATE";
3056
    case MYSQL_TYPE_NULL:        return "NULL";
3057
    case MYSQL_TYPE_SET:         return "SET";
3058
    case MYSQL_TYPE_SHORT:       return "SHORT";
3059
    case MYSQL_TYPE_STRING:      return "STRING";
3060
    case MYSQL_TYPE_TIME:        return "TIME";
3061
    case MYSQL_TYPE_TIMESTAMP:   return "TIMESTAMP";
3062
    case MYSQL_TYPE_TINY:        return "TINY";
3063
    case MYSQL_TYPE_TINY_BLOB:   return "TINY_BLOB";
3064
    case MYSQL_TYPE_VAR_STRING:  return "VAR_STRING";
3065
    case MYSQL_TYPE_YEAR:        return "YEAR";
3066
    default:                     return "?-unknown-?";
3067
  }
3068
}
3069
3070
static char *fieldflags2str(uint f) {
3071
  static char buf[1024];
3072
  char *s=buf;
3073
  *s=0;
3074
#define ff2s_check_flag(X) \
3075
                if (f & X ## _FLAG) { s=strmov(s, # X " "); f &= ~ X ## _FLAG; }
3076
  ff2s_check_flag(NOT_NULL);
3077
  ff2s_check_flag(PRI_KEY);
3078
  ff2s_check_flag(UNIQUE_KEY);
3079
  ff2s_check_flag(MULTIPLE_KEY);
3080
  ff2s_check_flag(BLOB);
3081
  ff2s_check_flag(UNSIGNED);
3082
  ff2s_check_flag(ZEROFILL);
3083
  ff2s_check_flag(BINARY);
3084
  ff2s_check_flag(ENUM);
3085
  ff2s_check_flag(AUTO_INCREMENT);
3086
  ff2s_check_flag(TIMESTAMP);
3087
  ff2s_check_flag(SET);
3088
  ff2s_check_flag(NO_DEFAULT_VALUE);
3089
  ff2s_check_flag(NUM);
3090
  ff2s_check_flag(PART_KEY);
3091
  ff2s_check_flag(GROUP);
3092
  ff2s_check_flag(UNIQUE);
3093
  ff2s_check_flag(BINCMP);
3094
  ff2s_check_flag(ON_UPDATE_NOW);
3095
#undef ff2s_check_flag
3096
  if (f)
3097
    sprintf(s, " unknows=0x%04x", f);
3098
  return buf;
3099
}
3100
3101
static void
3102
print_field_types(MYSQL_RES *result)
3103
{
3104
  MYSQL_FIELD   *field;
3105
  uint i=0;
3106
3107
  while ((field = mysql_fetch_field(result)))
3108
  {
3109
    tee_fprintf(PAGER, "Field %3u:  `%s`\n"
3110
                       "Catalog:    `%s`\n"
3111
                       "Database:   `%s`\n"
3112
                       "Table:      `%s`\n"
3113
                       "Org_table:  `%s`\n"
3114
                       "Type:       %s\n"
3115
                       "Collation:  %s (%u)\n"
3116
                       "Length:     %lu\n"
3117
                       "Max_length: %lu\n"
3118
                       "Decimals:   %u\n"
3119
                       "Flags:      %s\n\n",
3120
                ++i,
3121
                field->name, field->catalog, field->db, field->table,
3122
                field->org_table, fieldtype2str(field->type),
3123
                get_charset_name(field->charsetnr), field->charsetnr,
3124
                field->length, field->max_length, field->decimals,
3125
                fieldflags2str(field->flags));
3126
  }
3127
  tee_puts("", PAGER);
3128
}
3129
3130
3131
static void
3132
print_table_data(MYSQL_RES *result)
3133
{
3134
  String separator(256);
3135
  MYSQL_ROW	cur;
3136
  MYSQL_FIELD	*field;
3137
  bool		*num_flag;
3138
3139
  num_flag=(bool*) my_alloca(sizeof(bool)*mysql_num_fields(result));
3140
  if (column_types_flag)
3141
  {
3142
    print_field_types(result);
3143
    if (!mysql_num_rows(result))
3144
      return;
3145
    mysql_field_seek(result,0);
3146
  }
3147
  separator.copy("+",1,charset_info);
3148
  while ((field = mysql_fetch_field(result)))
3149
  {
3150
    uint length= column_names ? field->name_length : 0;
3151
    if (quick)
3152
      length=max(length,field->length);
3153
    else
3154
      length=max(length,field->max_length);
3155
    if (length < 4 && !IS_NOT_NULL(field->flags))
3156
      length=4;					// Room for "NULL"
3157
    field->max_length=length;
3158
    separator.fill(separator.length()+length+2,'-');
3159
    separator.append('+');
3160
  }
3161
  separator.append('\0');                       // End marker for \0
3162
  tee_puts((char*) separator.ptr(), PAGER);
3163
  if (column_names)
3164
  {
3165
    mysql_field_seek(result,0);
3166
    (void) tee_fputs("|", PAGER);
3167
    for (uint off=0; (field = mysql_fetch_field(result)) ; off++)
3168
    {
3169
      uint name_length= (uint) strlen(field->name);
3170
      uint numcells= charset_info->cset->numcells(charset_info,
3171
                                                  field->name,
3172
                                                  field->name + name_length);
3173
      uint display_length= field->max_length + name_length - numcells;
3174
      tee_fprintf(PAGER, " %-*s |",(int) min(display_length,
3175
                                            MAX_COLUMN_LENGTH),
3176
                  field->name);
3177
      num_flag[off]= IS_NUM(field->type);
3178
    }
3179
    (void) tee_fputs("\n", PAGER);
3180
    tee_puts((char*) separator.ptr(), PAGER);
3181
  }
3182
3183
  while ((cur= mysql_fetch_row(result)))
3184
  {
3185
    if (interrupted_query)
3186
      break;
3187
    ulong *lengths= mysql_fetch_lengths(result);
3188
    (void) tee_fputs("| ", PAGER);
3189
    mysql_field_seek(result, 0);
3190
    for (uint off= 0; off < mysql_num_fields(result); off++)
3191
    {
3192
      const char *buffer;
3193
      uint data_length;
3194
      uint field_max_length;
3195
      uint visible_length;
3196
      uint extra_padding;
3197
3198
      if (cur[off] == NULL)
3199
      {
3200
        buffer= "NULL";
3201
        data_length= 4;
3202
      } 
3203
      else 
3204
      {
3205
        buffer= cur[off];
3206
        data_length= (uint) lengths[off];
3207
      }
3208
3209
      field= mysql_fetch_field(result);
3210
      field_max_length= field->max_length;
3211
3212
      /* 
3213
       How many text cells on the screen will this string span?  If it contains
3214
       multibyte characters, then the number of characters we occupy on screen
3215
       will be fewer than the number of bytes we occupy in memory.
3216
3217
       We need to find how much screen real-estate we will occupy to know how 
3218
       many extra padding-characters we should send with the printing function.
3219
      */
3220
      visible_length= charset_info->cset->numcells(charset_info, buffer, buffer + data_length);
3221
      extra_padding= data_length - visible_length;
3222
3223
      if (field_max_length > MAX_COLUMN_LENGTH)
3224
        tee_print_sized_data(buffer, data_length, MAX_COLUMN_LENGTH+extra_padding, FALSE);
3225
      else
3226
      {
3227
        if (num_flag[off] != 0) /* if it is numeric, we right-justify it */
3228
          tee_print_sized_data(buffer, data_length, field_max_length+extra_padding, TRUE);
3229
        else 
3230
          tee_print_sized_data(buffer, data_length, field_max_length+extra_padding, FALSE);
3231
      }
3232
      tee_fputs(" | ", PAGER);
3233
    }
3234
    (void) tee_fputs("\n", PAGER);
3235
  }
3236
  tee_puts((char*) separator.ptr(), PAGER);
3237
  my_afree((uchar*) num_flag);
3238
}
3239
3240
/**
3241
  Return the length of a field after it would be rendered into text.
3242
3243
  This doesn't know or care about multibyte characters.  Assume we're
3244
  using such a charset.  We can't know that all of the upcoming rows 
3245
  for this column will have bytes that each render into some fraction
3246
  of a character.  It's at least possible that a row has bytes that 
3247
  all render into one character each, and so the maximum length is 
3248
  still the number of bytes.  (Assumption 1:  This can't be better 
3249
  because we can never know the number of characters that the DB is 
3250
  going to send -- only the number of bytes.  2: Chars <= Bytes.)
3251
3252
  @param  field  Pointer to a field to be inspected
3253
3254
  @returns  number of character positions to be used, at most
3255
*/
3256
static int get_field_disp_length(MYSQL_FIELD *field)
3257
{
3258
  uint length= column_names ? field->name_length : 0;
3259
3260
  if (quick)
3261
    length= max(length, field->length);
3262
  else
3263
    length= max(length, field->max_length);
3264
3265
  if (length < 4 && !IS_NOT_NULL(field->flags))
3266
    length= 4;				/* Room for "NULL" */
3267
3268
  return length;
3269
}
3270
3271
/**
3272
  For a new result, return the max number of characters that any
3273
  upcoming row may return.
3274
3275
  @param  result  Pointer to the result to judge
3276
3277
  @returns  The max number of characters in any row of this result
3278
*/
3279
static int get_result_width(MYSQL_RES *result)
3280
{
3281
  unsigned int len= 0;
3282
  MYSQL_FIELD *field;
3283
  MYSQL_FIELD_OFFSET offset;
3284
  
3285
#ifndef DBUG_OFF
3286
  offset= mysql_field_tell(result);
3287
  DBUG_ASSERT(offset == 0);
3288
#else
3289
  offset= 0;
3290
#endif
3291
3292
  while ((field= mysql_fetch_field(result)) != NULL)
3293
    len+= get_field_disp_length(field) + 3; /* plus bar, space, & final space */
3294
3295
  (void) mysql_field_seek(result, offset);	
3296
3297
  return len + 1; /* plus final bar. */
3298
}
3299
3300
static void
3301
tee_print_sized_data(const char *data, unsigned int data_length, unsigned int total_bytes_to_send, bool right_justified)
3302
{
3303
  /* 
3304
    For '\0's print ASCII spaces instead, as '\0' is eaten by (at
3305
    least my) console driver, and that messes up the pretty table
3306
    grid.  (The \0 is also the reason we can't use fprintf() .) 
3307
  */
3308
  unsigned int i;
3309
  const char *p;
3310
3311
  if (right_justified) 
3312
    for (i= data_length; i < total_bytes_to_send; i++)
3313
      tee_putc((int)' ', PAGER);
3314
3315
  for (i= 0, p= data; i < data_length; i+= 1, p+= 1)
3316
  {
3317
    if (*p == '\0')
3318
      tee_putc((int)' ', PAGER);
3319
    else
3320
      tee_putc((int)*p, PAGER);
3321
  }
3322
3323
  if (! right_justified) 
3324
    for (i= data_length; i < total_bytes_to_send; i++)
3325
      tee_putc((int)' ', PAGER);
3326
}
3327
3328
3329
3330
static void
3331
print_table_data_html(MYSQL_RES *result)
3332
{
3333
  MYSQL_ROW	cur;
3334
  MYSQL_FIELD	*field;
3335
3336
  mysql_field_seek(result,0);
3337
  (void) tee_fputs("<TABLE BORDER=1><TR>", PAGER);
3338
  if (column_names)
3339
  {
3340
    while((field = mysql_fetch_field(result)))
3341
    {
3342
      tee_fprintf(PAGER, "<TH>%s</TH>", (field->name ? 
3343
					 (field->name[0] ? field->name : 
3344
					  " &nbsp; ") : "NULL"));
3345
    }
3346
    (void) tee_fputs("</TR>", PAGER);
3347
  }
3348
  while ((cur = mysql_fetch_row(result)))
3349
  {
3350
    if (interrupted_query)
3351
      break;
3352
    ulong *lengths=mysql_fetch_lengths(result);
3353
    (void) tee_fputs("<TR>", PAGER);
3354
    for (uint i=0; i < mysql_num_fields(result); i++)
3355
    {
3356
      (void) tee_fputs("<TD>", PAGER);
3357
      safe_put_field(cur[i],lengths[i]);
3358
      (void) tee_fputs("</TD>", PAGER);
3359
    }
3360
    (void) tee_fputs("</TR>", PAGER);
3361
  }
3362
  (void) tee_fputs("</TABLE>", PAGER);
3363
}
3364
3365
3366
static void
3367
print_table_data_xml(MYSQL_RES *result)
3368
{
3369
  MYSQL_ROW   cur;
3370
  MYSQL_FIELD *fields;
3371
3372
  mysql_field_seek(result,0);
3373
3374
  tee_fputs("<?xml version=\"1.0\"?>\n\n<resultset statement=\"", PAGER);
3375
  xmlencode_print(glob_buffer.ptr(), (int)strlen(glob_buffer.ptr()));
3376
  tee_fputs("\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">",
3377
            PAGER);
3378
3379
  fields = mysql_fetch_fields(result);
3380
  while ((cur = mysql_fetch_row(result)))
3381
  {
3382
    if (interrupted_query)
3383
      break;
3384
    ulong *lengths=mysql_fetch_lengths(result);
3385
    (void) tee_fputs("\n  <row>\n", PAGER);
3386
    for (uint i=0; i < mysql_num_fields(result); i++)
3387
    {
3388
      tee_fprintf(PAGER, "\t<field name=\"");
3389
      xmlencode_print(fields[i].name, (uint) strlen(fields[i].name));
3390
      if (cur[i])
3391
      {
3392
        tee_fprintf(PAGER, "\">");
3393
        xmlencode_print(cur[i], lengths[i]);
3394
        tee_fprintf(PAGER, "</field>\n");
3395
      }
3396
      else
3397
        tee_fprintf(PAGER, "\" xsi:nil=\"true\" />\n");
3398
    }
3399
    (void) tee_fputs("  </row>\n", PAGER);
3400
  }
3401
  (void) tee_fputs("</resultset>\n", PAGER);
3402
}
3403
3404
3405
static void
3406
print_table_data_vertically(MYSQL_RES *result)
3407
{
3408
  MYSQL_ROW	cur;
3409
  uint		max_length=0;
3410
  MYSQL_FIELD	*field;
3411
3412
  while ((field = mysql_fetch_field(result)))
3413
  {
3414
    uint length= field->name_length;
3415
    if (length > max_length)
3416
      max_length= length;
3417
    field->max_length=length;
3418
  }
3419
3420
  mysql_field_seek(result,0);
3421
  for (uint row_count=1; (cur= mysql_fetch_row(result)); row_count++)
3422
  {
3423
    if (interrupted_query)
3424
      break;
3425
    mysql_field_seek(result,0);
3426
    tee_fprintf(PAGER, 
3427
		"*************************** %d. row ***************************\n", row_count);
3428
    for (uint off=0; off < mysql_num_fields(result); off++)
3429
    {
3430
      field= mysql_fetch_field(result);
3431
      tee_fprintf(PAGER, "%*s: ",(int) max_length,field->name);
3432
      tee_fprintf(PAGER, "%s\n",cur[off] ? (char*) cur[off] : "NULL");
3433
    }
3434
  }
3435
}
3436
3437
3438
/* print_warnings should be called right after executing a statement */
3439
3440
static void print_warnings()
3441
{
3442
  const char   *query;
3443
  MYSQL_RES    *result;
3444
  MYSQL_ROW    cur;
3445
  my_ulonglong num_rows;
3446
  
3447
  /* Save current error before calling "show warnings" */
3448
  uint error= mysql_errno(&mysql);
3449
3450
  /* Get the warnings */
3451
  query= "show warnings";
3452
  mysql_real_query_for_lazy(query, strlen(query));
3453
  mysql_store_result_for_lazy(&result);
3454
3455
  /* Bail out when no warnings */
3456
  if (!(num_rows= mysql_num_rows(result)))
3457
    goto end;
3458
3459
  cur= mysql_fetch_row(result);
3460
3461
  /*
3462
    Don't print a duplicate of the current error.  It is possible for SHOW
3463
    WARNINGS to return multiple errors with the same code, but different
3464
    messages.  To be safe, skip printing the duplicate only if it is the only
3465
    warning.
3466
  */
3467
  if (!cur || num_rows == 1 && error == (uint) strtoul(cur[1], NULL, 10))
3468
    goto end;
3469
3470
  /* Print the warnings */
3471
  init_pager();
3472
  do
3473
  {
3474
    tee_fprintf(PAGER, "%s (Code %s): %s\n", cur[0], cur[1], cur[2]);
3475
  } while ((cur= mysql_fetch_row(result)));
3476
  end_pager();
3477
3478
end:
3479
  mysql_free_result(result);
3480
}
3481
3482
3483
static const char *array_value(const char **array, char key)
3484
{
3485
  for (; *array; array+= 2)
3486
    if (**array == key)
3487
      return array[1];
3488
  return 0;
3489
}
3490
3491
3492
static void
3493
xmlencode_print(const char *src, uint length)
3494
{
3495
  if (!src)
3496
    tee_fputs("NULL", PAGER);
3497
  else
3498
  {
3499
    for (const char *p = src; *p && length; *p++, length--)
3500
    {
3501
      const char *t;
3502
      if ((t = array_value(xmlmeta, *p)))
3503
	tee_fputs(t, PAGER);
3504
      else
3505
	tee_putc(*p, PAGER);
3506
    }
3507
  }
3508
}
3509
3510
3511
static void
3512
safe_put_field(const char *pos,ulong length)
3513
{
3514
  if (!pos)
3515
    tee_fputs("NULL", PAGER);
3516
  else
3517
  {
3518
    if (opt_raw_data)
3519
      tee_fputs(pos, PAGER);
3520
    else for (const char *end=pos+length ; pos != end ; pos++)
3521
    {
3522
#ifdef USE_MB
3523
      int l;
3524
      if (use_mb(charset_info) &&
3525
          (l = my_ismbchar(charset_info, pos, end)))
3526
      {
3527
	  while (l--)
3528
	    tee_putc(*pos++, PAGER);
3529
	  pos--;
3530
	  continue;
3531
      }
3532
#endif
3533
      if (!*pos)
3534
	tee_fputs("\\0", PAGER); // This makes everything hard
3535
      else if (*pos == '\t')
3536
	tee_fputs("\\t", PAGER); // This would destroy tab format
3537
      else if (*pos == '\n')
3538
	tee_fputs("\\n", PAGER); // This too
3539
      else if (*pos == '\\')
3540
	tee_fputs("\\\\", PAGER);
3541
	else
3542
	tee_putc(*pos, PAGER);
3543
    }
3544
  }
3545
}
3546
3547
3548
static void
3549
print_tab_data(MYSQL_RES *result)
3550
{
3551
  MYSQL_ROW	cur;
3552
  MYSQL_FIELD	*field;
3553
  ulong		*lengths;
3554
3555
  if (opt_silent < 2 && column_names)
3556
  {
3557
    int first=0;
3558
    while ((field = mysql_fetch_field(result)))
3559
    {
3560
      if (first++)
3561
	(void) tee_fputs("\t", PAGER);
3562
      (void) tee_fputs(field->name, PAGER);
3563
    }
3564
    (void) tee_fputs("\n", PAGER);
3565
  }
3566
  while ((cur = mysql_fetch_row(result)))
3567
  {
3568
    lengths=mysql_fetch_lengths(result);
3569
    safe_put_field(cur[0],lengths[0]);
3570
    for (uint off=1 ; off < mysql_num_fields(result); off++)
3571
    {
3572
      (void) tee_fputs("\t", PAGER);
3573
      safe_put_field(cur[off], lengths[off]);
3574
    }
3575
    (void) tee_fputs("\n", PAGER);
3576
  }
3577
}
3578
3579
static int
3580
com_tee(String *buffer, char *line __attribute__((unused)))
3581
{
3582
  char file_name[FN_REFLEN], *end, *param;
3583
3584
  if (status.batch)
3585
    return 0;
3586
  while (my_isspace(charset_info,*line))
3587
    line++;
3588
  if (!(param = strchr(line, ' '))) // if outfile wasn't given, use the default
3589
  {
3590
    if (!strlen(outfile))
3591
    {
3592
      printf("No previous outfile available, you must give a filename!\n");
3593
      return 0;
3594
    }
3595
    else if (opt_outfile)
3596
    {
3597
      tee_fprintf(stdout, "Currently logging to file '%s'\n", outfile);
3598
      return 0;
3599
    }
3600
    else
3601
      param = outfile;			//resume using the old outfile
3602
  }
3603
3604
  /* eliminate the spaces before the parameters */
3605
  while (my_isspace(charset_info,*param))
3606
    param++;
3607
  end= strmake(file_name, param, sizeof(file_name) - 1);
3608
  /* remove end space from command line */
3609
  while (end > file_name && (my_isspace(charset_info,end[-1]) || 
3610
			     my_iscntrl(charset_info,end[-1])))
3611
    end--;
3612
  end[0]= 0;
3613
  if (end == file_name)
3614
  {
3615
    printf("No outfile specified!\n");
3616
    return 0;
3617
  }
3618
  init_tee(file_name);
3619
  return 0;
3620
}
3621
3622
3623
static int
3624
com_notee(String *buffer __attribute__((unused)),
3625
	  char *line __attribute__((unused)))
3626
{
3627
  if (opt_outfile)
3628
    end_tee();
3629
  tee_fprintf(stdout, "Outfile disabled.\n");
3630
  return 0;
3631
}
3632
3633
/*
3634
  Sorry, this command is not available in Windows.
3635
*/
3636
3637
#ifdef USE_POPEN
3638
static int
3639
com_pager(String *buffer, char *line __attribute__((unused)))
3640
{
3641
  char pager_name[FN_REFLEN], *end, *param;
3642
3643
  if (status.batch)
3644
    return 0;
3645
  /* Skip spaces in front of the pager command */
3646
  while (my_isspace(charset_info, *line))
3647
    line++;
3648
  /* Skip the pager command */
3649
  param= strchr(line, ' ');
3650
  /* Skip the spaces between the command and the argument */
3651
  while (param && my_isspace(charset_info, *param))
3652
    param++;
3653
  if (!param || !strlen(param)) // if pager was not given, use the default
3654
  {
3655
    if (!default_pager_set)
3656
    {
3657
      tee_fprintf(stdout, "Default pager wasn't set, using stdout.\n");
3658
      opt_nopager=1;
3659
      strmov(pager, "stdout");
3660
      PAGER= stdout;
3661
      return 0;
3662
    }
3663
    strmov(pager, default_pager);
3664
  }
3665
  else
3666
  {
3667
    end= strmake(pager_name, param, sizeof(pager_name)-1);
3668
    while (end > pager_name && (my_isspace(charset_info,end[-1]) || 
3669
                                my_iscntrl(charset_info,end[-1])))
3670
      end--;
3671
    end[0]=0;
3672
    strmov(pager, pager_name);
3673
    strmov(default_pager, pager_name);
3674
  }
3675
  opt_nopager=0;
3676
  tee_fprintf(stdout, "PAGER set to '%s'\n", pager);
3677
  return 0;
3678
}
3679
3680
3681
static int
3682
com_nopager(String *buffer __attribute__((unused)),
3683
	    char *line __attribute__((unused)))
3684
{
3685
  strmov(pager, "stdout");
3686
  opt_nopager=1;
3687
  PAGER= stdout;
3688
  tee_fprintf(stdout, "PAGER set to stdout\n");
3689
  return 0;
3690
}
3691
#endif
3692
3693
3694
/*
3695
  Sorry, you can't send the result to an editor in Win32
3696
*/
3697
3698
#ifdef USE_POPEN
3699
static int
3700
com_edit(String *buffer,char *line __attribute__((unused)))
3701
{
3702
  char	filename[FN_REFLEN],buff[160];
3703
  int	fd,tmp;
3704
  const char *editor;
3705
3706
  if ((fd=create_temp_file(filename,NullS,"sql", O_CREAT | O_WRONLY,
3707
			   MYF(MY_WME))) < 0)
3708
    goto err;
3709
  if (buffer->is_empty() && !old_buffer.is_empty())
3710
    (void) my_write(fd,(uchar*) old_buffer.ptr(),old_buffer.length(),
3711
		    MYF(MY_WME));
3712
  else
3713
    (void) my_write(fd,(uchar*) buffer->ptr(),buffer->length(),MYF(MY_WME));
3714
  (void) my_close(fd,MYF(0));
3715
3716
  if (!(editor = (char *)getenv("EDITOR")) &&
3717
      !(editor = (char *)getenv("VISUAL")))
3718
    editor = "vi";
3719
  strxmov(buff,editor," ",filename,NullS);
3720
  (void) system(buff);
3721
15 by brian
Fix for stat, NETWARE removal
3722
  struct stat stat_arg;
3723
  if (stat(filename,&stat_arg))
1 by brian
clean slate
3724
    goto err;
3725
  if ((fd = my_open(filename,O_RDONLY, MYF(MY_WME))) < 0)
3726
    goto err;
3727
  (void) buffer->alloc((uint) stat_arg.st_size);
3728
  if ((tmp=read(fd,(char*) buffer->ptr(),buffer->alloced_length())) >= 0L)
3729
    buffer->length((uint) tmp);
3730
  else
3731
    buffer->length(0);
3732
  (void) my_close(fd,MYF(0));
3733
  (void) my_delete(filename,MYF(MY_WME));
3734
err:
3735
  return 0;
3736
}
3737
#endif
3738
3739
3740
/* If arg is given, exit without errors. This happens on command 'quit' */
3741
3742
static int
3743
com_quit(String *buffer __attribute__((unused)),
3744
	 char *line __attribute__((unused)))
3745
{
3746
  /* let the screen auto close on a normal shutdown */
3747
  status.exit_status=0;
3748
  return 1;
3749
}
3750
3751
static int
3752
com_rehash(String *buffer __attribute__((unused)),
3753
	 char *line __attribute__((unused)))
3754
{
3755
#ifdef HAVE_READLINE
3756
  build_completion_hash(1, 0);
3757
#endif
3758
  return 0;
3759
}
3760
3761
3762
#ifdef USE_POPEN
3763
static int
3764
com_shell(String *buffer, char *line __attribute__((unused)))
3765
{
3766
  char *shell_cmd;
3767
3768
  /* Skip space from line begin */
3769
  while (my_isspace(charset_info, *line))
3770
    line++;
3771
  if (!(shell_cmd = strchr(line, ' ')))
3772
  {
3773
    put_info("Usage: \\! shell-command", INFO_ERROR);
3774
    return -1;
3775
  }
3776
  /*
3777
    The output of the shell command does not
3778
    get directed to the pager or the outfile
3779
  */
3780
  if (system(shell_cmd) == -1)
3781
  {
3782
    put_info(strerror(errno), INFO_ERROR, errno);
3783
    return -1;
3784
  }
3785
  return 0;
3786
}
3787
#endif
3788
3789
3790
static int
3791
com_print(String *buffer,char *line __attribute__((unused)))
3792
{
3793
  tee_puts("--------------", stdout);
3794
  (void) tee_fputs(buffer->c_ptr(), stdout);
3795
  if (!buffer->length() || (*buffer)[buffer->length()-1] != '\n')
3796
    tee_putc('\n', stdout);
3797
  tee_puts("--------------\n", stdout);
3798
  return 0;					/* If empty buffer */
3799
}
3800
3801
	/* ARGSUSED */
3802
static int
3803
com_connect(String *buffer, char *line)
3804
{
3805
  char *tmp, buff[256];
3806
  bool save_rehash= opt_rehash;
3807
  int error;
3808
3809
  bzero(buff, sizeof(buff));
3810
  if (buffer)
3811
  {
3812
    /*
3813
      Two null bytes are needed in the end of buff to allow
3814
      get_arg to find end of string the second time it's called.
3815
    */
3816
    tmp= strmake(buff, line, sizeof(buff)-2);
3817
#ifdef EXTRA_DEBUG
3818
    tmp[1]= 0;
3819
#endif
3820
    tmp= get_arg(buff, 0);
3821
    if (tmp && *tmp)
3822
    {
3823
      my_free(current_db, MYF(MY_ALLOW_ZERO_PTR));
3824
      current_db= my_strdup(tmp, MYF(MY_WME));
3825
      tmp= get_arg(buff, 1);
3826
      if (tmp)
3827
      {
3828
	my_free(current_host,MYF(MY_ALLOW_ZERO_PTR));
3829
	current_host=my_strdup(tmp,MYF(MY_WME));
3830
      }
3831
    }
3832
    else
3833
    {
3834
      /* Quick re-connect */
3835
      opt_rehash= 0;                            /* purecov: tested */
3836
    }
3837
    buffer->length(0);				// command used
3838
  }
3839
  else
3840
    opt_rehash= 0;
3841
  error=sql_connect(current_host,current_db,current_user,opt_password,0);
3842
  opt_rehash= save_rehash;
3843
3844
  if (connected)
3845
  {
3846
    sprintf(buff,"Connection id:    %lu",mysql_thread_id(&mysql));
3847
    put_info(buff,INFO_INFO);
3848
    sprintf(buff,"Current database: %.128s\n",
3849
	    current_db ? current_db : "*** NONE ***");
3850
    put_info(buff,INFO_INFO);
3851
  }
3852
  return error;
3853
}
3854
3855
3856
static int com_source(String *buffer, char *line)
3857
{
3858
  char source_name[FN_REFLEN], *end, *param;
3859
  LINE_BUFFER *line_buff;
3860
  int error;
3861
  STATUS old_status;
3862
  FILE *sql_file;
3863
3864
  /* Skip space from file name */
3865
  while (my_isspace(charset_info,*line))
3866
    line++;
3867
  if (!(param = strchr(line, ' ')))		// Skip command name
3868
    return put_info("Usage: \\. <filename> | source <filename>", 
3869
		    INFO_ERROR, 0);
3870
  while (my_isspace(charset_info,*param))
3871
    param++;
3872
  end=strmake(source_name,param,sizeof(source_name)-1);
3873
  while (end > source_name && (my_isspace(charset_info,end[-1]) || 
3874
                               my_iscntrl(charset_info,end[-1])))
3875
    end--;
3876
  end[0]=0;
3877
  unpack_filename(source_name,source_name);
3878
  /* open file name */
3879
  if (!(sql_file = my_fopen(source_name, O_RDONLY | O_BINARY,MYF(0))))
3880
  {
3881
    char buff[FN_REFLEN+60];
3882
    sprintf(buff,"Failed to open file '%s', error: %d", source_name,errno);
3883
    return put_info(buff, INFO_ERROR, 0);
3884
  }
3885
3886
  if (!(line_buff=batch_readline_init(opt_max_allowed_packet+512,sql_file)))
3887
  {
3888
    my_fclose(sql_file,MYF(0));
3889
    return put_info("Can't initialize batch_readline", INFO_ERROR, 0);
3890
  }
3891
3892
  /* Save old status */
3893
  old_status=status;
3894
  bfill((char*) &status,sizeof(status),(char) 0);
3895
3896
  status.batch=old_status.batch;		// Run in batch mode
3897
  status.line_buff=line_buff;
3898
  status.file_name=source_name;
3899
  glob_buffer.length(0);			// Empty command buffer
3900
  error= read_and_execute(false);
3901
  status=old_status;				// Continue as before
3902
  my_fclose(sql_file,MYF(0));
3903
  batch_readline_end(line_buff);
3904
  return error;
3905
}
3906
3907
3908
	/* ARGSUSED */
3909
static int
3910
com_delimiter(String *buffer __attribute__((unused)), char *line)
3911
{
3912
  char buff[256], *tmp;
3913
3914
  strmake(buff, line, sizeof(buff) - 1);
3915
  tmp= get_arg(buff, 0);
3916
3917
  if (!tmp || !*tmp)
3918
  {
3919
    put_info("DELIMITER must be followed by a 'delimiter' character or string",
3920
	     INFO_ERROR);
3921
    return 0;
3922
  }
3923
  else
3924
  {
3925
    if (strstr(tmp, "\\")) 
3926
    {
3927
      put_info("DELIMITER cannot contain a backslash character", INFO_ERROR);
3928
      return 0;
3929
    }
3930
  }
3931
  strmake(delimiter, tmp, sizeof(delimiter) - 1);
3932
  delimiter_length= (int)strlen(delimiter);
3933
  delimiter_str= delimiter;
3934
  return 0;
3935
}
3936
3937
	/* ARGSUSED */
3938
static int
3939
com_use(String *buffer __attribute__((unused)), char *line)
3940
{
3941
  char *tmp, buff[FN_REFLEN + 1];
3942
  int select_db;
3943
3944
  bzero(buff, sizeof(buff));
3945
  strmake(buff, line, sizeof(buff) - 1);
3946
  tmp= get_arg(buff, 0);
3947
  if (!tmp || !*tmp)
3948
  {
3949
    put_info("USE must be followed by a database name", INFO_ERROR);
3950
    return 0;
3951
  }
3952
  /*
3953
    We need to recheck the current database, because it may change
3954
    under our feet, for example if DROP DATABASE or RENAME DATABASE
3955
    (latter one not yet available by the time the comment was written)
3956
  */
3957
  get_current_db();
3958
3959
  if (!current_db || cmp_database(charset_info, current_db,tmp))
3960
  {
3961
    if (one_database)
3962
    {
3963
      skip_updates= 1;
3964
      select_db= 0;    // don't do mysql_select_db()
3965
    }
3966
    else
3967
      select_db= 2;    // do mysql_select_db() and build_completion_hash()
3968
  }
3969
  else
3970
  {
3971
    /*
3972
      USE to the current db specified.
3973
      We do need to send mysql_select_db() to make server
3974
      update database level privileges, which might
3975
      change since last USE (see bug#10979).
3976
      For performance purposes, we'll skip rebuilding of completion hash.
3977
    */
3978
    skip_updates= 0;
3979
    select_db= 1;      // do only mysql_select_db(), without completion
3980
  }
3981
3982
  if (select_db)
3983
  {
3984
    /*
3985
      reconnect once if connection is down or if connection was found to
3986
      be down during query
3987
    */
3988
    if (!connected && reconnect())
3989
      return opt_reconnect ? -1 : 1;                        // Fatal error
3990
    if (mysql_select_db(&mysql,tmp))
3991
    {
3992
      if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR)
3993
        return put_error(&mysql);
3994
3995
      if (reconnect())
3996
        return opt_reconnect ? -1 : 1;                      // Fatal error
3997
      if (mysql_select_db(&mysql,tmp))
3998
        return put_error(&mysql);
3999
    }
4000
    my_free(current_db,MYF(MY_ALLOW_ZERO_PTR));
4001
    current_db=my_strdup(tmp,MYF(MY_WME));
4002
#ifdef HAVE_READLINE
4003
    if (select_db > 1)
4004
      build_completion_hash(opt_rehash, 1);
4005
#endif
4006
  }
4007
4008
  put_info("Database changed",INFO_INFO);
4009
  return 0;
4010
}
4011
4012
static int
4013
com_warnings(String *buffer __attribute__((unused)),
4014
   char *line __attribute__((unused)))
4015
{
4016
  show_warnings = 1;
4017
  put_info("Show warnings enabled.",INFO_INFO);
4018
  return 0;
4019
}
4020
4021
static int
4022
com_nowarnings(String *buffer __attribute__((unused)),
4023
   char *line __attribute__((unused)))
4024
{
4025
  show_warnings = 0;
4026
  put_info("Show warnings disabled.",INFO_INFO);
4027
  return 0;
4028
}
4029
4030
/*
4031
  Gets argument from a command on the command line. If get_next_arg is
4032
  not defined, skips the command and returns the first argument. The
4033
  line is modified by adding zero to the end of the argument. If
4034
  get_next_arg is defined, then the function searches for end of string
4035
  first, after found, returns the next argument and adds zero to the
4036
  end. If you ever wish to use this feature, remember to initialize all
4037
  items in the array to zero first.
4038
*/
4039
4040
char *get_arg(char *line, my_bool get_next_arg)
4041
{
4042
  char *ptr, *start;
4043
  my_bool quoted= 0, valid_arg= 0;
4044
  char qtype= 0;
4045
4046
  ptr= line;
4047
  if (get_next_arg)
4048
  {
4049
    for (; *ptr; ptr++) ;
4050
    if (*(ptr + 1))
4051
      ptr++;
4052
  }
4053
  else
4054
  {
4055
    /* skip leading white spaces */
4056
    while (my_isspace(charset_info, *ptr))
4057
      ptr++;
4058
    if (*ptr == '\\') // short command was used
4059
      ptr+= 2;
4060
    else
4061
      while (*ptr &&!my_isspace(charset_info, *ptr)) // skip command
4062
        ptr++;
4063
  }
4064
  if (!*ptr)
4065
    return NullS;
4066
  while (my_isspace(charset_info, *ptr))
4067
    ptr++;
4068
  if (*ptr == '\'' || *ptr == '\"' || *ptr == '`')
4069
  {
4070
    qtype= *ptr;
4071
    quoted= 1;
4072
    ptr++;
4073
  }
4074
  for (start=ptr ; *ptr; ptr++)
4075
  {
4076
    if (*ptr == '\\' && ptr[1]) // escaped character
4077
    {
4078
      // Remove the backslash
4079
      strmov(ptr, ptr+1);
4080
    }
4081
    else if ((!quoted && *ptr == ' ') || (quoted && *ptr == qtype))
4082
    {
4083
      *ptr= 0;
4084
      break;
4085
    }
4086
  }
4087
  valid_arg= ptr != start;
4088
  return valid_arg ? start : NullS;
4089
}
4090
4091
4092
static int
4093
sql_real_connect(char *host,char *database,char *user,char *password,
4094
		 uint silent)
4095
{
4096
  if (connected)
4097
  {
4098
    connected= 0;
4099
    mysql_close(&mysql);
4100
  }
4101
  mysql_init(&mysql);
4102
  if (opt_connect_timeout)
4103
  {
4104
    uint timeout=opt_connect_timeout;
4105
    mysql_options(&mysql,MYSQL_OPT_CONNECT_TIMEOUT,
4106
		  (char*) &timeout);
4107
  }
4108
  if (opt_compress)
4109
    mysql_options(&mysql,MYSQL_OPT_COMPRESS,NullS);
4110
  if (opt_secure_auth)
4111
    mysql_options(&mysql, MYSQL_SECURE_AUTH, (char *) &opt_secure_auth);
4112
  if (using_opt_local_infile)
4113
    mysql_options(&mysql,MYSQL_OPT_LOCAL_INFILE, (char*) &opt_local_infile);
4114
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
4115
  if (opt_use_ssl)
4116
    mysql_ssl_set(&mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
4117
		  opt_ssl_capath, opt_ssl_cipher);
4118
  mysql_options(&mysql,MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
4119
                (char*)&opt_ssl_verify_server_cert);
4120
#endif
4121
  if (opt_protocol)
4122
    mysql_options(&mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
4123
#ifdef HAVE_SMEM
4124
  if (shared_memory_base_name)
4125
    mysql_options(&mysql,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name);
4126
#endif
4127
  if (safe_updates)
4128
  {
4129
    char init_command[100];
4130
    sprintf(init_command,
4131
	    "SET SQL_SAFE_UPDATES=1,SQL_SELECT_LIMIT=%lu,SQL_MAX_JOIN_SIZE=%lu",
4132
	    select_limit,max_join_size);
4133
    mysql_options(&mysql, MYSQL_INIT_COMMAND, init_command);
4134
  }
4135
  if (default_charset_used)
4136
    mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, default_charset);
4137
  if (!mysql_real_connect(&mysql, host, user, password,
4138
			  database, opt_mysql_port, opt_mysql_unix_port,
4139
			  connect_flag | CLIENT_MULTI_STATEMENTS))
4140
  {
4141
    if (!silent ||
4142
	(mysql_errno(&mysql) != CR_CONN_HOST_ERROR &&
4143
	 mysql_errno(&mysql) != CR_CONNECTION_ERROR))
4144
    {
4145
      (void) put_error(&mysql);
4146
      (void) fflush(stdout);
4147
      return ignore_errors ? -1 : 1;		// Abort
4148
    }
4149
    return -1;					// Retryable
4150
  }
4151
  connected=1;
4152
#ifndef EMBEDDED_LIBRARY
4153
  mysql.reconnect= debug_info_flag; // We want to know if this happens
4154
#else
4155
  mysql.reconnect= 1;
4156
#endif
4157
#ifdef HAVE_READLINE
4158
  build_completion_hash(opt_rehash, 1);
4159
#endif
4160
  return 0;
4161
}
4162
4163
4164
static int
4165
sql_connect(char *host,char *database,char *user,char *password,uint silent)
4166
{
4167
  bool message=0;
4168
  uint count=0;
4169
  int error;
4170
  for (;;)
4171
  {
4172
    if ((error=sql_real_connect(host,database,user,password,wait_flag)) >= 0)
4173
    {
4174
      if (count)
4175
      {
4176
	tee_fputs("\n", stderr);
4177
	(void) fflush(stderr);
4178
      }
4179
      return error;
4180
    }
4181
    if (!wait_flag)
4182
      return ignore_errors ? -1 : 1;
4183
    if (!message && !silent)
4184
    {
4185
      message=1;
4186
      tee_fputs("Waiting",stderr); (void) fflush(stderr);
4187
    }
4188
    (void) sleep(wait_time);
4189
    if (!silent)
4190
    {
4191
      putc('.',stderr); (void) fflush(stderr);
4192
      count++;
4193
    }
4194
  }
4195
}
4196
4197
4198
4199
static int
4200
com_status(String *buffer __attribute__((unused)),
4201
	   char *line __attribute__((unused)))
4202
{
4203
  const char *status_str;
4204
  char buff[40];
4205
  ulonglong id;
4206
  MYSQL_RES *result;
4207
4208
  tee_puts("--------------", stdout);
4209
  usage(1);					/* Print version */
4210
  if (connected)
4211
  {
4212
    tee_fprintf(stdout, "\nConnection id:\t\t%lu\n",mysql_thread_id(&mysql));
4213
    /* 
4214
      Don't remove "limit 1", 
4215
      it is protection againts SQL_SELECT_LIMIT=0
4216
    */
4217
    if (!mysql_query(&mysql,"select DATABASE(), USER() limit 1") &&
4218
	(result=mysql_use_result(&mysql)))
4219
    {
4220
      MYSQL_ROW cur=mysql_fetch_row(result);
4221
      if (cur)
4222
      {
4223
        tee_fprintf(stdout, "Current database:\t%s\n", cur[0] ? cur[0] : "");
4224
        tee_fprintf(stdout, "Current user:\t\t%s\n", cur[1]);
4225
      }
4226
      mysql_free_result(result);
4227
    } 
4228
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
4229
    if ((status_str= mysql_get_ssl_cipher(&mysql)))
4230
      tee_fprintf(stdout, "SSL:\t\t\tCipher in use is %s\n",
4231
		  status_str);
4232
    else
4233
#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
4234
      tee_puts("SSL:\t\t\tNot in use", stdout);
4235
  }
4236
  else
4237
  {
4238
    vidattr(A_BOLD);
4239
    tee_fprintf(stdout, "\nNo connection\n");
4240
    vidattr(A_NORMAL);
4241
    return 0;
4242
  }
4243
  if (skip_updates)
4244
  {
4245
    vidattr(A_BOLD);
4246
    tee_fprintf(stdout, "\nAll updates ignored to this database\n");
4247
    vidattr(A_NORMAL);
4248
  }
4249
#ifdef USE_POPEN
4250
  tee_fprintf(stdout, "Current pager:\t\t%s\n", pager);
4251
  tee_fprintf(stdout, "Using outfile:\t\t'%s'\n", opt_outfile ? outfile : "");
4252
#endif
4253
  tee_fprintf(stdout, "Using delimiter:\t%s\n", delimiter);
4254
  tee_fprintf(stdout, "Server version:\t\t%s\n", server_version_string(&mysql));
4255
  tee_fprintf(stdout, "Protocol version:\t%d\n", mysql_get_proto_info(&mysql));
4256
  tee_fprintf(stdout, "Connection:\t\t%s\n", mysql_get_host_info(&mysql));
4257
  if ((id= mysql_insert_id(&mysql)))
4258
    tee_fprintf(stdout, "Insert id:\t\t%s\n", llstr(id, buff));
4259
4260
  /* "limit 1" is protection against SQL_SELECT_LIMIT=0 */
4261
  if (!mysql_query(&mysql,"select @@character_set_client, @@character_set_connection, @@character_set_server, @@character_set_database limit 1") &&
4262
      (result=mysql_use_result(&mysql)))
4263
  {
4264
    MYSQL_ROW cur=mysql_fetch_row(result);
4265
    if (cur)
4266
    {
4267
      tee_fprintf(stdout, "Server characterset:\t%s\n", cur[2] ? cur[2] : "");
4268
      tee_fprintf(stdout, "Db     characterset:\t%s\n", cur[3] ? cur[3] : "");
4269
      tee_fprintf(stdout, "Client characterset:\t%s\n", cur[0] ? cur[0] : "");
4270
      tee_fprintf(stdout, "Conn.  characterset:\t%s\n", cur[1] ? cur[1] : "");
4271
    }
4272
    mysql_free_result(result);
4273
  }
4274
  else
4275
  {
4276
    /* Probably pre-4.1 server */
4277
    tee_fprintf(stdout, "Client characterset:\t%s\n", charset_info->csname);
4278
    tee_fprintf(stdout, "Server characterset:\t%s\n", mysql.charset->csname);
4279
  }
4280
4281
#ifndef EMBEDDED_LIBRARY
4282
  if (strstr(mysql_get_host_info(&mysql),"TCP/IP") || ! mysql.unix_socket)
4283
    tee_fprintf(stdout, "TCP port:\t\t%d\n", mysql.port);
4284
  else
4285
    tee_fprintf(stdout, "UNIX socket:\t\t%s\n", mysql.unix_socket);
4286
  if (mysql.net.compress)
4287
    tee_fprintf(stdout, "Protocol:\t\tCompressed\n");
4288
#endif
4289
4290
  if ((status_str= mysql_stat(&mysql)) && !mysql_error(&mysql)[0])
4291
  {
4292
    ulong sec;
4293
    const char *pos= strchr(status_str,' ');
4294
    /* print label */
4295
    tee_fprintf(stdout, "%.*s\t\t\t", (int) (pos-status_str), status_str);
4296
    if ((status_str= str2int(pos,10,0,LONG_MAX,(long*) &sec)))
4297
    {
4298
      nice_time((double) sec,buff,0);
4299
      tee_puts(buff, stdout);			/* print nice time */
4300
      while (*status_str == ' ')
4301
        status_str++;  /* to next info */
4302
      tee_putc('\n', stdout);
4303
      tee_puts(status_str, stdout);
4304
    }
4305
  }
4306
  if (safe_updates)
4307
  {
4308
    vidattr(A_BOLD);
4309
    tee_fprintf(stdout, "\nNote that you are running in safe_update_mode:\n");
4310
    vidattr(A_NORMAL);
4311
    tee_fprintf(stdout, "\
4312
UPDATEs and DELETEs that don't use a key in the WHERE clause are not allowed.\n\
4313
(One can force an UPDATE/DELETE by adding LIMIT # at the end of the command.)\n\
4314
SELECT has an automatic 'LIMIT %lu' if LIMIT is not used.\n\
4315
Max number of examined row combination in a join is set to: %lu\n\n",
4316
select_limit, max_join_size);
4317
  }
4318
  tee_puts("--------------\n", stdout);
4319
  return 0;
4320
}
4321
4322
static const char *
4323
server_version_string(MYSQL *con)
4324
{
4325
  static char buf[MAX_SERVER_VERSION_LENGTH] = "";
4326
4327
  /* Only one thread calls this, so no synchronization is needed */
4328
  if (buf[0] == '\0')
4329
  {
4330
    char *bufp = buf;
4331
    MYSQL_RES *result;
4332
4333
    bufp= strnmov(buf, mysql_get_server_info(con), sizeof buf);
4334
4335
    /* "limit 1" is protection against SQL_SELECT_LIMIT=0 */
4336
    if (!mysql_query(con, "select @@version_comment limit 1") &&
4337
        (result = mysql_use_result(con)))
4338
    {
4339
      MYSQL_ROW cur = mysql_fetch_row(result);
4340
      if (cur && cur[0])
4341
      {
4342
        bufp = strxnmov(bufp, sizeof buf - (bufp - buf), " ", cur[0], NullS);
4343
      }
4344
      mysql_free_result(result);
4345
    }
4346
4347
    /* str*nmov doesn't guarantee NUL-termination */
4348
    if (bufp == buf + sizeof buf)
4349
      buf[sizeof buf - 1] = '\0';
4350
  }
4351
4352
  return buf;
4353
}
4354
4355
static int
4356
put_info(const char *str,INFO_TYPE info_type, uint error, const char *sqlstate)
4357
{
4358
  FILE *file= (info_type == INFO_ERROR ? stderr : stdout);
4359
  static int inited=0;
4360
4361
  if (status.batch)
4362
  {
4363
    if (info_type == INFO_ERROR)
4364
    {
4365
      (void) fflush(file);
4366
      fprintf(file,"ERROR");
4367
      if (error)
4368
      {
4369
	if (sqlstate)
4370
	  (void) fprintf(file," %d (%s)",error, sqlstate);
4371
        else
4372
	  (void) fprintf(file," %d",error);
4373
      }
4374
      if (status.query_start_line && line_numbers)
4375
      {
4376
	(void) fprintf(file," at line %lu",status.query_start_line);
4377
	if (status.file_name)
4378
	  (void) fprintf(file," in file: '%s'", status.file_name);
4379
      }
4380
      (void) fprintf(file,": %s\n",str);
4381
      (void) fflush(file);
4382
      if (!ignore_errors)
4383
	return 1;
4384
    }
4385
    else if (info_type == INFO_RESULT && verbose > 1)
4386
      tee_puts(str, file);
4387
    if (unbuffered)
4388
      fflush(file);
4389
    return info_type == INFO_ERROR ? -1 : 0;
4390
  }
4391
  if (!opt_silent || info_type == INFO_ERROR)
4392
  {
4393
    if (!inited)
4394
    {
4395
      inited=1;
4396
#ifdef HAVE_SETUPTERM
4397
      (void) setupterm((char *)0, 1, (int *) 0);
4398
#endif
4399
    }
4400
    if (info_type == INFO_ERROR)
4401
    {
4402
      if (!opt_nobeep)
4403
        putchar('\a');		      	/* This should make a bell */
4404
      vidattr(A_STANDOUT);
4405
      if (error)
4406
      {
4407
	if (sqlstate)
4408
          (void) tee_fprintf(file, "ERROR %d (%s): ", error, sqlstate);
4409
        else
4410
          (void) tee_fprintf(file, "ERROR %d: ", error);
4411
      }
4412
      else
4413
        tee_puts("ERROR: ", file);
4414
    }
4415
    else
4416
      vidattr(A_BOLD);
4417
    (void) tee_puts(str, file);
4418
    vidattr(A_NORMAL);
4419
  }
4420
  if (unbuffered)
4421
    fflush(file);
4422
  return info_type == INFO_ERROR ? -1 : 0;
4423
}
4424
4425
4426
static int
4427
put_error(MYSQL *con)
4428
{
4429
  return put_info(mysql_error(con), INFO_ERROR, mysql_errno(con),
4430
		  mysql_sqlstate(con));
4431
}  
4432
4433
4434
static void remove_cntrl(String &buffer)
4435
{
4436
  char *start,*end;
4437
  end=(start=(char*) buffer.ptr())+buffer.length();
4438
  while (start < end && !my_isgraph(charset_info,end[-1]))
4439
    end--;
4440
  buffer.length((uint) (end-start));
4441
}
4442
4443
4444
void tee_fprintf(FILE *file, const char *fmt, ...)
4445
{
4446
  va_list args;
4447
4448
  va_start(args, fmt);
4449
  (void) vfprintf(file, fmt, args);
4450
  va_end(args);
4451
4452
  if (opt_outfile)
4453
  {
4454
    va_start(args, fmt);
4455
    (void) vfprintf(OUTFILE, fmt, args);
4456
    va_end(args);
4457
  }
4458
}
4459
4460
4461
void tee_fputs(const char *s, FILE *file)
4462
{
4463
  fputs(s, file);
4464
  if (opt_outfile)
4465
    fputs(s, OUTFILE);
4466
}
4467
4468
4469
void tee_puts(const char *s, FILE *file)
4470
{
4471
  fputs(s, file);
4472
  fputc('\n', file);
4473
  if (opt_outfile)
4474
  {
4475
    fputs(s, OUTFILE);
4476
    fputc('\n', OUTFILE);
4477
  }
4478
}
4479
4480
void tee_putc(int c, FILE *file)
4481
{
4482
  putc(c, file);
4483
  if (opt_outfile)
4484
    putc(c, OUTFILE);
4485
}
4486
4487
#include <sys/times.h>
4488
#ifdef _SC_CLK_TCK				// For mit-pthreads
4489
#undef CLOCKS_PER_SEC
4490
#define CLOCKS_PER_SEC (sysconf(_SC_CLK_TCK))
4491
#endif
4492
4493
static ulong start_timer(void)
4494
{
4495
  struct tms tms_tmp;
4496
  return times(&tms_tmp);
4497
}
4498
4499
4500
/** 
4501
  Write as many as 52+1 bytes to buff, in the form of a legible duration of time.
4502
4503
  len("4294967296 days, 23 hours, 59 minutes, 60.00 seconds")  ->  52
4504
*/
4505
static void nice_time(double sec,char *buff,bool part_second)
4506
{
4507
  ulong tmp;
4508
  if (sec >= 3600.0*24)
4509
  {
4510
    tmp=(ulong) floor(sec/(3600.0*24));
4511
    sec-=3600.0*24*tmp;
4512
    buff=int10_to_str((long) tmp, buff, 10);
4513
    buff=strmov(buff,tmp > 1 ? " days " : " day ");
4514
  }
4515
  if (sec >= 3600.0)
4516
  {
4517
    tmp=(ulong) floor(sec/3600.0);
4518
    sec-=3600.0*tmp;
4519
    buff=int10_to_str((long) tmp, buff, 10);
4520
    buff=strmov(buff,tmp > 1 ? " hours " : " hour ");
4521
  }
4522
  if (sec >= 60.0)
4523
  {
4524
    tmp=(ulong) floor(sec/60.0);
4525
    sec-=60.0*tmp;
4526
    buff=int10_to_str((long) tmp, buff, 10);
4527
    buff=strmov(buff," min ");
4528
  }
4529
  if (part_second)
4530
    sprintf(buff,"%.2f sec",sec);
4531
  else
4532
    sprintf(buff,"%d sec",(int) sec);
4533
}
4534
4535
4536
static void end_timer(ulong start_time,char *buff)
4537
{
4538
  nice_time((double) (start_timer() - start_time) /
4539
	    CLOCKS_PER_SEC,buff,1);
4540
}
4541
4542
4543
static void mysql_end_timer(ulong start_time,char *buff)
4544
{
4545
  buff[0]=' ';
4546
  buff[1]='(';
4547
  end_timer(start_time,buff+2);
4548
  strmov(strend(buff),")");
4549
}
4550
4551
static const char* construct_prompt()
4552
{
4553
  processed_prompt.free();			// Erase the old prompt
4554
  time_t  lclock = time(NULL);			// Get the date struct
4555
  struct tm *t = localtime(&lclock);
4556
4557
  /* parse thru the settings for the prompt */
4558
  for (char *c = current_prompt; *c ; *c++)
4559
  {
4560
    if (*c != PROMPT_CHAR)
4561
	processed_prompt.append(*c);
4562
    else
4563
    {
4564
      switch (*++c) {
4565
      case '\0':
4566
	c--;			// stop it from going beyond if ends with %
4567
	break;
4568
      case 'c':
4569
	add_int_to_prompt(++prompt_counter);
4570
	break;
4571
      case 'v':
4572
	if (connected)
4573
	  processed_prompt.append(mysql_get_server_info(&mysql));
4574
	else
4575
	  processed_prompt.append("not_connected");
4576
	break;
4577
      case 'd':
4578
	processed_prompt.append(current_db ? current_db : "(none)");
4579
	break;
4580
      case 'h':
4581
      {
4582
	const char *prompt;
4583
	prompt= connected ? mysql_get_host_info(&mysql) : "not_connected";
4584
	if (strstr(prompt, "Localhost"))
4585
	  processed_prompt.append("localhost");
4586
	else
4587
	{
4588
	  const char *end=strcend(prompt,' ');
4589
	  processed_prompt.append(prompt, (uint) (end-prompt));
4590
	}
4591
	break;
4592
      }
4593
      case 'p':
4594
      {
4595
#ifndef EMBEDDED_LIBRARY
4596
	if (!connected)
4597
	{
4598
	  processed_prompt.append("not_connected");
4599
	  break;
4600
	}
4601
4602
	const char *host_info = mysql_get_host_info(&mysql);
4603
	if (strstr(host_info, "memory")) 
4604
	{
4605
		processed_prompt.append( mysql.host );
4606
	}
4607
	else if (strstr(host_info,"TCP/IP") ||
4608
	    !mysql.unix_socket)
4609
	  add_int_to_prompt(mysql.port);
4610
	else
4611
	{
4612
	  char *pos=strrchr(mysql.unix_socket,'/');
4613
 	  processed_prompt.append(pos ? pos+1 : mysql.unix_socket);
4614
	}
4615
#endif
4616
      }
4617
	break;
4618
      case 'U':
4619
	if (!full_username)
4620
	  init_username();
4621
        processed_prompt.append(full_username ? full_username :
4622
                                (current_user ?  current_user : "(unknown)"));
4623
	break;
4624
      case 'u':
4625
	if (!full_username)
4626
	  init_username();
4627
        processed_prompt.append(part_username ? part_username :
4628
                                (current_user ?  current_user : "(unknown)"));
4629
	break;
4630
      case PROMPT_CHAR:
4631
	processed_prompt.append(PROMPT_CHAR);
4632
	break;
4633
      case 'n':
4634
	processed_prompt.append('\n');
4635
	break;
4636
      case ' ':
4637
      case '_':
4638
	processed_prompt.append(' ');
4639
	break;
4640
      case 'R':
4641
	if (t->tm_hour < 10)
4642
	  processed_prompt.append('0');
4643
	add_int_to_prompt(t->tm_hour);
4644
	break;
4645
      case 'r':
4646
	int getHour;
4647
	getHour = t->tm_hour % 12;
4648
	if (getHour == 0)
4649
	  getHour=12;
4650
	if (getHour < 10)
4651
	  processed_prompt.append('0');
4652
	add_int_to_prompt(getHour);
4653
	break;
4654
      case 'm':
4655
	if (t->tm_min < 10)
4656
	  processed_prompt.append('0');
4657
	add_int_to_prompt(t->tm_min);
4658
	break;
4659
      case 'y':
4660
	int getYear;
4661
	getYear = t->tm_year % 100;
4662
	if (getYear < 10)
4663
	  processed_prompt.append('0');
4664
	add_int_to_prompt(getYear);
4665
	break;
4666
      case 'Y':
4667
	add_int_to_prompt(t->tm_year+1900);
4668
	break;
4669
      case 'D':
4670
	char* dateTime;
4671
	dateTime = ctime(&lclock);
4672
	processed_prompt.append(strtok(dateTime,"\n"));
4673
	break;
4674
      case 's':
4675
	if (t->tm_sec < 10)
4676
	  processed_prompt.append('0');
4677
	add_int_to_prompt(t->tm_sec);
4678
	break;
4679
      case 'w':
4680
	processed_prompt.append(day_names[t->tm_wday]);
4681
	break;
4682
      case 'P':
4683
	processed_prompt.append(t->tm_hour < 12 ? "am" : "pm");
4684
	break;
4685
      case 'o':
4686
	add_int_to_prompt(t->tm_mon+1);
4687
	break;
4688
      case 'O':
4689
	processed_prompt.append(month_names[t->tm_mon]);
4690
	break;
4691
      case '\'':
4692
	processed_prompt.append("'");
4693
	break;
4694
      case '"':
4695
	processed_prompt.append('"');
4696
	break;
4697
      case 'S':
4698
	processed_prompt.append(';');
4699
	break;
4700
      case 't':
4701
	processed_prompt.append('\t');
4702
	break;
4703
      case 'l':
4704
	processed_prompt.append(delimiter_str);
4705
	break;
4706
      default:
4707
	processed_prompt.append(c);
4708
      }
4709
    }
4710
  }
4711
  processed_prompt.append('\0');
4712
  return processed_prompt.ptr();
4713
}
4714
4715
4716
static void add_int_to_prompt(int toadd)
4717
{
4718
  char buffer[16];
4719
  int10_to_str(toadd,buffer,10);
4720
  processed_prompt.append(buffer);
4721
}
4722
4723
static void init_username()
4724
{
4725
  my_free(full_username,MYF(MY_ALLOW_ZERO_PTR));
4726
  my_free(part_username,MYF(MY_ALLOW_ZERO_PTR));
4727
4728
  MYSQL_RES *result;
4729
  if (!mysql_query(&mysql,"select USER()") &&
4730
      (result=mysql_use_result(&mysql)))
4731
  {
4732
    MYSQL_ROW cur=mysql_fetch_row(result);
4733
    full_username=my_strdup(cur[0],MYF(MY_WME));
4734
    part_username=my_strdup(strtok(cur[0],"@"),MYF(MY_WME));
4735
    (void) mysql_fetch_row(result);		// Read eof
4736
  }
4737
}
4738
4739
static int com_prompt(String *buffer, char *line)
4740
{
4741
  char *ptr=strchr(line, ' ');
4742
  prompt_counter = 0;
4743
  my_free(current_prompt,MYF(MY_ALLOW_ZERO_PTR));
4744
  current_prompt=my_strdup(ptr ? ptr+1 : default_prompt,MYF(MY_WME));
4745
  if (!ptr)
4746
    tee_fprintf(stdout, "Returning to default PROMPT of %s\n", default_prompt);
4747
  else
4748
    tee_fprintf(stdout, "PROMPT set to '%s'\n", current_prompt);
4749
  return 0;
4750
}
4751
4752
#ifndef EMBEDDED_LIBRARY
4753
/* Keep sql_string library happy */
4754
4755
void *sql_alloc(size_t Size)
4756
{
4757
  return my_malloc(Size,MYF(MY_WME));
4758
}
4759
4760
void sql_element_free(void *ptr)
4761
{
4762
  my_free(ptr,MYF(0));
4763
}
4764
#endif /* EMBEDDED_LIBRARY */