Merge lp:~posulliv/drizzle/inject into lp:drizzle/7.0

Proposed by Padraig O'Sullivan
Status: Work in progress
Proposed branch: lp:~posulliv/drizzle/inject
Merge into: lp:drizzle/7.0
Diff against target: 6024 lines (+5945/-0)
15 files modified
plugin/stad/client.cc (+4635/-0)
plugin/stad/common/injection_exception.hpp (+60/-0)
plugin/stad/common/keywords.cpp (+67/-0)
plugin/stad/common/keywords.hpp (+42/-0)
plugin/stad/common/randomizer.cpp (+49/-0)
plugin/stad/common/sql_tokenizer.cpp (+148/-0)
plugin/stad/common/sql_tokenizer.hpp (+63/-0)
plugin/stad/injection_prevention.cpp (+187/-0)
plugin/stad/injection_prevention.hpp (+52/-0)
plugin/stad/plugin.am (+32/-0)
plugin/stad/plugin.ini (+10/-0)
plugin/stad/tests/r/attacks.result (+152/-0)
plugin/stad/tests/r/simple.result (+193/-0)
plugin/stad/tests/t/attacks.test (+150/-0)
plugin/stad/tests/t/simple.test (+105/-0)
To merge this branch: bzr merge lp:~posulliv/drizzle/inject
Reviewer Review Type Date Requested Status
Padraig O'Sullivan (community) Disapprove
Review via email: mp+41791@code.launchpad.net

Description of the change

This branch contains a query rewriting plugin that can prevent SQL injection attacks. The concept of SQL randomization is used in this plugin (for more info see the research paper - http://www1.cs.columbia.edu/~locasto/projects/candidacy/papers/boyd2004sqlrand.pdf)

Examples of the types of SQL injection attacks it can prevent are in the test suite for the plugin. This plugin is not enabled by default. Examples of client applications that use this plugin are being developed and will be documented in the next few days.

I wasn't sure what the model for plugin distribution in drizzle is. I wouldn't ask to merge it into trunk if I knew of another way to distribute a plugin?

To post a comment you must log in.
Revision history for this message
Monty Taylor (mordred) wrote :

Setting this to work in progress since you're not actively wanting it merged at the moment.

Revision history for this message
Padraig O'Sullivan (posulliv) wrote :

Yep, I think I'll switch to just making this an out-of-tree plugin for now. It will help push me to understand what is needed to distribute a plugin not included in the source anyway which is good :)

review: Disapprove

Unmerged revisions

1954. By Padraig O'Sullivan

Adding SQL injection prevention plugin.

Preview Diff

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

Subscribers

People subscribed via source and target branches