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