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