1 // Copyright (c) 1999-2018 David Muse
2 // See the file COPYING for more information
3
4 #include <sqlrelay/sqlrclient.h>
5 #include <sqlrelay/sqlrutil.h>
6 #include <rudiments/file.h>
7 #include <rudiments/permissions.h>
8 #include <rudiments/filesystem.h>
9 #include <rudiments/filedescriptor.h>
10 #include <rudiments/process.h>
11 #include <rudiments/environment.h>
12 #include <rudiments/datetime.h>
13 #include <rudiments/signalclasses.h>
14 #include <rudiments/xmldom.h>
15 #include <rudiments/stdio.h>
16 #include <rudiments/character.h>
17 #include <rudiments/memorypool.h>
18 #include <rudiments/prompt.h>
19 #include <config.h>
20 #include <defaults.h>
21 #define NEED_IS_BIT_TYPE_CHAR 1
22 #define NEED_IS_NUMBER_TYPE_CHAR 1
23 #define NEED_IS_FLOAT_TYPE_CHAR 1
24 #define NEED_IS_NONSCALE_FLOAT_TYPE_CHAR 1
25 #include <datatypes.h>
26 #include <defines.h>
27 #include <parsedatetime.h>
28 #include <version.h>
29 // FIXME: use rudiments locale class instead
30 #include <locale.h>
31 #include <math.h>
32
33 class sqlrshbindvalue {
34 public:
35 union {
36 char *stringval;
37 int64_t integerval;
38 struct {
39 double value;
40 uint32_t precision;
41 uint32_t scale;
42 } doubleval;
43 struct {
44 int16_t year;
45 int16_t month;
46 int16_t day;
47 int16_t hour;
48 int16_t minute;
49 int16_t second;
50 int32_t microsecond;
51 const char *tz;
52 bool isnegative;
53 } dateval;
54 };
55 sqlrclientbindvartype_t type;
56 uint32_t outputstringbindlength;
57
print()58 void print() {}
59 };
60
61 enum sqlrshformat {
62 SQLRSH_FORMAT_PLAIN=0,
63 SQLRSH_FORMAT_CSV
64 };
65
66 class sqlrshenv {
67 public:
68 sqlrshenv();
69 ~sqlrshenv();
70 void clearbinds(
71 dictionary<char *, sqlrshbindvalue *> *binds);
72
73 bool headers;
74 bool divider;
75 bool stats;
76 uint64_t rsbs;
77 bool final;
78 bool autocommit;
79 bool lazyfetch;
80 char delimiter;
81 dictionary<char *, sqlrshbindvalue *> inputbinds;
82 memorypool inbindpool;
83 dictionary<char *, sqlrshbindvalue *> outputbinds;
84 dictionary<char *, sqlrshbindvalue *> inputoutputbinds;
85 char *cacheto;
86 sqlrshformat format;
87 bool getasnumber;
88 bool noelapsed;
89 bool nextresultset;
90 };
91
sqlrshenv()92 sqlrshenv::sqlrshenv() {
93 headers=true;
94 divider=true;
95 stats=true;
96 rsbs=100;
97 final=false;
98 autocommit=false;
99 lazyfetch=false;
100 delimiter=';';
101 cacheto=NULL;
102 format=SQLRSH_FORMAT_PLAIN;
103 getasnumber=false;
104 noelapsed=false;
105 nextresultset=false;
106 }
107
~sqlrshenv()108 sqlrshenv::~sqlrshenv() {
109 clearbinds(&inputbinds);
110 clearbinds(&outputbinds);
111 clearbinds(&inputoutputbinds);
112 delete[] cacheto;
113 }
114
clearbinds(dictionary<char *,sqlrshbindvalue * > * binds)115 void sqlrshenv::clearbinds(dictionary<char *, sqlrshbindvalue *> *binds) {
116
117 for (linkedlistnode<dictionarynode<char *, sqlrshbindvalue *> *>
118 *node=binds->getList()->getFirst();
119 node; node=node->getNext()) {
120
121 delete[] node->getValue()->getKey();
122 sqlrshbindvalue *bv=node->getValue()->getValue();
123 if (bv->type==SQLRCLIENTBINDVARTYPE_STRING) {
124 delete[] bv->stringval;
125 }
126 delete bv;
127 }
128 binds->clear();
129 inbindpool.clear();
130 }
131
132 enum querytype_t {
133 SHOW_DATABASES_QUERY=0,
134 SHOW_TABLES_QUERY,
135 SHOW_COLUMNS_QUERY,
136 SHOW_PRIMARY_KEYS_QUERY,
137 DESCRIBE_QUERY
138 };
139
140 class sqlrsh {
141 public:
142 sqlrsh();
143 ~sqlrsh();
144 bool execute(int argc, const char **argv);
145 private:
146 void startupMessage(sqlrshenv *env,
147 const char *host,
148 uint16_t port,
149 const char *user);
150 void userRcFile(sqlrconnection *sqlrcon,
151 sqlrcursor *sqlrcur,
152 sqlrshenv *env);
153 bool runScript(sqlrconnection *sqlrcon,
154 sqlrcursor *sqlrcur,
155 sqlrshenv *env,
156 const char *filename,
157 bool displayerror);
158 bool runCommands(sqlrconnection *sqlrcon,
159 sqlrcursor *sqlrcur,
160 sqlrshenv *env,
161 const char *commands,
162 bool *exitprogram);
163 bool getCommandFromFileOrString(file *fl,
164 const char *string,
165 const char **stringpos,
166 stringbuffer *cmdbuffer,
167 sqlrshenv *env);
168 bool runCommand(sqlrconnection *sqlrcon,
169 sqlrcursor *sqlrcur,
170 sqlrshenv *env,
171 const char *command,
172 bool *exitprogram);
173 int commandType(const char *command);
174 bool internalCommand(sqlrconnection *sqlrcon,
175 sqlrcursor *sqlrcur,
176 sqlrshenv *env,
177 const char *command);
178 bool externalCommand(sqlrconnection *sqlrcon,
179 sqlrcursor *sqlrcur,
180 sqlrshenv *env,
181 const char *command);
182 void executeQuery(sqlrcursor *sqlrcur,
183 sqlrshenv *env);
184 char *getWild(const char *command);
185 char *getTable(const char *command, bool in);
186 char *getProcedure(const char *command);
187 char *getType(const char *command);
188 void initStats(sqlrshenv *env);
189 void displayError(sqlrshenv *env,
190 const char *message,
191 const char *error,
192 int64_t errornumber);
193 void displayHeader(sqlrcursor *sqlrcur,
194 sqlrshenv *env);
195 void displayResultSet(sqlrcursor *sqlrcur,
196 sqlrshenv *env);
197 void displayStats(sqlrcursor *sqlrcur,
198 sqlrshenv *env);
199 bool ping(sqlrconnection *sqlrcon,
200 sqlrshenv *env);
201 bool identify(sqlrconnection *sqlrcon,
202 sqlrshenv *env);
203 bool dbversion(sqlrconnection *sqlrcon,
204 sqlrshenv *env);
205 bool dbhostname(sqlrconnection *sqlrcon,
206 sqlrshenv *env);
207 bool dbipaddress(sqlrconnection *sqlrcon,
208 sqlrshenv *env);
209 void clientversion(sqlrconnection *sqlrcon,
210 sqlrshenv *env);
211 bool serverversion(sqlrconnection *sqlrcon,
212 sqlrshenv *env);
213 bool lastinsertid(sqlrconnection *sqlrcon,
214 sqlrshenv *env);
215 bool inputbind(sqlrcursor *sqlrcur,
216 sqlrshenv *env,
217 const char *command);
218 bool inputbindblob(sqlrcursor *sqlrcur,
219 sqlrshenv *env,
220 const char *command);
221 bool outputbind(sqlrcursor *sqlrcur,
222 sqlrshenv *env,
223 const char *command);
224 bool inputoutputbind(sqlrcursor *sqlrcur,
225 sqlrshenv *env,
226 const char *command);
227 void printbinds(const char *type,
228 dictionary<char *, sqlrshbindvalue *> *binds);
229 void clearbinds(
230 dictionary<char *, sqlrshbindvalue *> *binds);
231 void setclientinfo(sqlrconnection *sqlrcon,
232 const char *command);
233 void getclientinfo(sqlrconnection *sqlrcon);
234 void responseTimeout(sqlrconnection *sqlrcon,
235 const char *command);
236 bool cache(sqlrshenv *env, sqlrcursor *sqlrcur,
237 const char *command);
238 bool openCache(sqlrshenv *env, sqlrcursor *sqlrcur,
239 const char *command);
240 void displayHelp(sqlrshenv *env);
241 void interactWithUser(sqlrconnection *sqlrcon,
242 sqlrcursor *sqlrcur,
243 sqlrshenv *env);
244
245 sqlrcmdline *cmdline;
246 sqlrpaths *sqlrpth;
247
248 datetime start;
249
250 prompt pr;
251 };
252
sqlrsh()253 sqlrsh::sqlrsh() {
254 cmdline=NULL;
255 sqlrpth=NULL;
256 }
257
~sqlrsh()258 sqlrsh::~sqlrsh() {
259 delete cmdline;
260 delete sqlrpth;
261 }
262
userRcFile(sqlrconnection * sqlrcon,sqlrcursor * sqlrcur,sqlrshenv * env)263 void sqlrsh::userRcFile(sqlrconnection *sqlrcon, sqlrcursor *sqlrcur,
264 sqlrshenv *env) {
265
266 // get user's home directory
267 const char *home=environment::getValue("HOME");
268 if (!home) {
269 home="~";
270 }
271
272 // build rcfilename
273 size_t userrcfilelen=charstring::length(home)+10+1;
274 char *userrcfile=new char[userrcfilelen];
275 charstring::copy(userrcfile,home);
276 charstring::append(userrcfile,"/.sqlrshrc");
277
278 // process the file
279 runScript(sqlrcon,sqlrcur,env,userrcfile,false);
280 delete[] userrcfile;
281 }
282
runScript(sqlrconnection * sqlrcon,sqlrcursor * sqlrcur,sqlrshenv * env,const char * filename,bool displayerror)283 bool sqlrsh::runScript(sqlrconnection *sqlrcon, sqlrcursor *sqlrcur,
284 sqlrshenv *env, const char *filename,
285 bool displayerror) {
286
287 bool retval=true;
288
289 char *trimmedfilename=charstring::duplicate(filename);
290 charstring::bothTrim(trimmedfilename);
291
292 // open the file
293 file scriptfile;
294 if (scriptfile.open(trimmedfilename,O_RDONLY)) {
295
296 // optimize
297 filesystem fs;
298 if (fs.open(trimmedfilename)) {
299 scriptfile.setReadBufferSize(
300 fs.getOptimumTransferBlockSize());
301 }
302
303 for (;;) {
304
305 // get a command
306 stringbuffer command;
307 if (!getCommandFromFileOrString(
308 &scriptfile,NULL,NULL,&command,env)) {
309 retval=false;
310 break;
311 }
312
313 // run the command
314 if (!runCommand(sqlrcon,sqlrcur,env,
315 command.getString(),
316 NULL)) {
317 retval=false;
318 break;
319 }
320 }
321
322 // close the file
323 scriptfile.close();
324
325 } else {
326
327 // error message
328 if (displayerror) {
329 stderror.printf("Couldn't open file: %s\n\n",
330 trimmedfilename);
331 }
332 retval=false;
333 }
334
335 delete[] trimmedfilename;
336
337 return retval;
338 }
339
runCommands(sqlrconnection * sqlrcon,sqlrcursor * sqlrcur,sqlrshenv * env,const char * commands,bool * exitprogram)340 bool sqlrsh::runCommands(sqlrconnection *sqlrcon,
341 sqlrcursor *sqlrcur,
342 sqlrshenv *env,
343 const char *commands,
344 bool *exitprogram) {
345
346 const char *nextcommand=commands;
347 for (;;) {
348 stringbuffer command;
349 if (!getCommandFromFileOrString(NULL,
350 nextcommand,
351 &nextcommand,
352 &command,
353 env)) {
354 break;
355 }
356 if (!runCommand(sqlrcon,sqlrcur,env,
357 command.getString(),
358 exitprogram)) {
359 return false;
360 }
361 }
362 return true;
363 }
364
getCommandFromFileOrString(file * fl,const char * string,const char ** stringpos,stringbuffer * cmdbuffer,sqlrshenv * env)365 bool sqlrsh::getCommandFromFileOrString(file *fl,
366 const char *string,
367 const char **stringpos,
368 stringbuffer *cmdbuffer,
369 sqlrshenv *env) {
370
371 bool ininitialwhitespace=true;
372 bool insinglequotes=false;
373 bool indoublequotes=false;
374 char ch;
375
376 for (;;) {
377
378 // get a character from the file or string
379 if (fl) {
380 if (fl->read(&ch)!=sizeof(ch)) {
381 // end of the command...
382 // only return false if we're at the
383 // beginning, prior to any actual command
384 return !ininitialwhitespace;
385 }
386 } else {
387 if (!*string) {
388 // end of the command...
389 // only return false if we're at the
390 // beginning, prior to any actual command
391 if (stringpos) {
392 *stringpos=string;
393 }
394 return !ininitialwhitespace;
395 }
396 ch=*string;
397 string++;
398 }
399
400 // skip whitespace at the beginning
401 if (ininitialwhitespace) {
402 if (character::isWhitespace(ch)) {
403 continue;
404 }
405 ininitialwhitespace=false;
406 }
407
408 // handle single-quoted strings, with escaping
409 if (ch=='\'') {
410 if (insinglequotes) {
411 cmdbuffer->append(ch);
412 if (fl) {
413 if (fl->read(&ch)!=sizeof(ch)) {
414 return true;
415 }
416 } else {
417 ch=*string;
418 string++;
419 }
420 if (ch!='\'') {
421 insinglequotes=false;
422 }
423 } else {
424 insinglequotes=true;
425 }
426 }
427
428 // handle double-quoted strings, with escaping
429 if (ch=='"') {
430 if (indoublequotes) {
431 cmdbuffer->append(ch);
432 if (fl) {
433 if (fl->read(&ch)!=sizeof(ch)) {
434 return true;
435 }
436 } else {
437 ch=*string;
438 string++;
439 }
440 if (ch!='"') {
441 indoublequotes=false;
442 }
443 } else {
444 indoublequotes=true;
445 }
446 }
447
448 // look for an end of command delimiter
449 if (!insinglequotes && !indoublequotes && ch==env->delimiter) {
450 if (string && stringpos) {
451 *stringpos=string;
452 }
453 return true;
454 }
455
456 // write character to buffer and move on
457 cmdbuffer->append(ch);
458 }
459 }
460
runCommand(sqlrconnection * sqlrcon,sqlrcursor * sqlrcur,sqlrshenv * env,const char * command,bool * exitprogram)461 bool sqlrsh::runCommand(sqlrconnection *sqlrcon,
462 sqlrcursor *sqlrcur,
463 sqlrshenv *env,
464 const char *command,
465 bool *exitprogram) {
466
467 int cmdtype=commandType(command);
468 if (exitprogram) {
469 *exitprogram=false;
470 }
471
472 // init stats
473 initStats(env);
474
475 if (cmdtype>0) {
476 // if the command an internal command, run it as one
477 return internalCommand(sqlrcon,sqlrcur,env,command);
478 } else if (cmdtype==0) {
479 // if the command is not an internal command,
480 // execute it as a query and display the result set
481 return externalCommand(sqlrcon,sqlrcur,env,command);
482 }
483
484 // exit
485 if (exitprogram) {
486 *exitprogram=true;
487 }
488 return true;
489 }
490
commandType(const char * command)491 int sqlrsh::commandType(const char *command) {
492
493 // skip white space
494 char *ptr=(char *)command;
495 while (*ptr==' ' || *ptr==' ' || *ptr=='\n') {
496 ptr++;
497 }
498
499 // compare to known internal commands
500 if (!charstring::compareIgnoringCase(ptr,"headers",7) ||
501 !charstring::compareIgnoringCase(ptr,"divider",7) ||
502 !charstring::compareIgnoringCase(ptr,"stats",5) ||
503 !charstring::compareIgnoringCase(ptr,"format",6) ||
504 !charstring::compareIgnoringCase(ptr,"debug",5) ||
505 !charstring::compareIgnoringCase(ptr,"nullsasnulls",12) ||
506 !charstring::compareIgnoringCase(ptr,"autocommit",10) ||
507 !charstring::compareIgnoringCase(ptr,"final",5) ||
508 !charstring::compareIgnoringCase(ptr,"help") ||
509 !charstring::compareIgnoringCase(ptr,"ping") ||
510 !charstring::compareIgnoringCase(ptr,"identify") ||
511 !charstring::compareIgnoringCase(ptr,"dbversion") ||
512 !charstring::compareIgnoringCase(ptr,"dbhostname") ||
513 !charstring::compareIgnoringCase(ptr,"dbipaddress") ||
514 !charstring::compareIgnoringCase(ptr,"clientversion") ||
515 !charstring::compareIgnoringCase(ptr,"serverversion") ||
516 !charstring::compareIgnoringCase(ptr,"use ",4) ||
517 !charstring::compareIgnoringCase(ptr,"currentdb") ||
518 !charstring::compareIgnoringCase(ptr,"currentschema") ||
519 !charstring::compareIgnoringCase(ptr,"run",3) ||
520 !charstring::compareIgnoringCase(ptr,"@",1) ||
521 !charstring::compareIgnoringCase(ptr,"delimiter",9) ||
522 !charstring::compareIgnoringCase(ptr,"delimeter",9) ||
523 !charstring::compareIgnoringCase(ptr,"inputbind ",10) ||
524 !charstring::compareIgnoringCase(ptr,"inputbindblob ",14) ||
525 !charstring::compareIgnoringCase(ptr,"outputbind ",11) ||
526 !charstring::compareIgnoringCase(ptr,"inputoutputbind ",16) ||
527 !charstring::compareIgnoringCase(ptr,"printinputbind",14) ||
528 !charstring::compareIgnoringCase(ptr,"printoutputbind",15) ||
529 !charstring::compareIgnoringCase(
530 ptr,"printinputoutputbind",20) ||
531 !charstring::compareIgnoringCase(ptr,"printbinds") ||
532 !charstring::compareIgnoringCase(ptr,"clearinputbind",14) ||
533 !charstring::compareIgnoringCase(ptr,"clearoutputbind",15) ||
534 !charstring::compareIgnoringCase(
535 ptr,"clearinputoutputbind",20) ||
536 !charstring::compareIgnoringCase(ptr,"clearbinds") ||
537 !charstring::compareIgnoringCase(ptr,"lastinsertid") ||
538 !charstring::compareIgnoringCase(ptr,"setclientinfo ",14) ||
539 !charstring::compareIgnoringCase(ptr,"getclientinfo") ||
540 !charstring::compareIgnoringCase(ptr,
541 "setresultsetbuffersize ",23) ||
542 !charstring::compareIgnoringCase(ptr,
543 "getresultsetbuffersize") ||
544 !charstring::compareIgnoringCase(ptr,"lazyfetch ",10) ||
545 !charstring::compareIgnoringCase(ptr,"endsession") ||
546 !charstring::compareIgnoringCase(ptr,"querytree") ||
547 !charstring::compareIgnoringCase(ptr,"translatedquery") ||
548 !charstring::compareIgnoringCase(ptr,"response timeout",16) ||
549 !charstring::compareIgnoringCase(ptr,"cache ",6) ||
550 !charstring::compareIgnoringCase(ptr,"opencache ",10)) {
551
552 // return value of 1 is internal command
553 return 1;
554 }
555
556 // look for an exit command
557 if (!charstring::compareIgnoringCase(ptr,"quit",4) ||
558 !charstring::compareIgnoringCase(ptr,"exit",4)) {
559 return -1;
560 }
561
562 // return value of 0 is external command
563 return 0;
564 }
565
internalCommand(sqlrconnection * sqlrcon,sqlrcursor * sqlrcur,sqlrshenv * env,const char * command)566 bool sqlrsh::internalCommand(sqlrconnection *sqlrcon, sqlrcursor *sqlrcur,
567 sqlrshenv *env, const char *command) {
568
569 // skip white space
570 char *ptr=(char *)command;
571 while (*ptr==' ' || *ptr==' ' || *ptr=='\n') {
572 ptr++;
573 }
574
575 // compare to known internal commands
576 int cmdtype=0;
577 if (!charstring::compareIgnoringCase(ptr,"headers",7)) {
578 ptr=ptr+7;
579 cmdtype=2;
580 } else if (!charstring::compareIgnoringCase(ptr,"divider",7)) {
581 ptr=ptr+7;
582 cmdtype=11;
583 } else if (!charstring::compareIgnoringCase(ptr,"stats",5)) {
584 ptr=ptr+5;
585 cmdtype=3;
586 } else if (!charstring::compareIgnoringCase(ptr,"format",6)) {
587 ptr=ptr+6;
588 cmdtype=10;
589 } else if (!charstring::compareIgnoringCase(ptr,"debug",5)) {
590 ptr=ptr+5;
591 cmdtype=4;
592 } else if (!charstring::compareIgnoringCase(ptr,"nullsasnulls",12)) {
593 ptr=ptr+13;
594 cmdtype=9;
595 } else if (!charstring::compareIgnoringCase(ptr,"autocommit",10)) {
596 ptr=ptr+10;
597 cmdtype=8;
598 } else if (!charstring::compareIgnoringCase(ptr,"final",5)) {
599 ptr=ptr+5;
600 cmdtype=5;
601 } else if (!charstring::compareIgnoringCase(ptr,"help")) {
602 displayHelp(env);
603 return true;
604 } else if (!charstring::compareIgnoringCase(ptr,"ping")) {
605 return ping(sqlrcon,env);
606 } else if (!charstring::compareIgnoringCase(ptr,"use ",4)) {
607 if (!sqlrcon->selectDatabase(ptr+4)) {
608 displayError(env,NULL,
609 sqlrcon->errorMessage(),
610 sqlrcon->errorNumber());
611 return false;
612 }
613 return true;
614 } else if (!charstring::compareIgnoringCase(ptr,"currentdb")) {
615 const char *currentdb=sqlrcon->getCurrentDatabase();
616 if (currentdb) {
617 stdoutput.printf("%s\n",currentdb);
618 } else if (sqlrcon->errorMessage()) {
619 displayError(env,NULL,
620 sqlrcon->errorMessage(),
621 sqlrcon->errorNumber());
622 return false;
623 } else {
624 stdoutput.printf("\n");
625 }
626 return true;
627 } else if (!charstring::compareIgnoringCase(ptr,"currentschema")) {
628 const char *currentschema=sqlrcon->getCurrentSchema();
629 if (currentschema) {
630 stdoutput.printf("%s\n",currentschema);
631 } else if (sqlrcon->errorMessage()) {
632 displayError(env,NULL,
633 sqlrcon->errorMessage(),
634 sqlrcon->errorNumber());
635 return false;
636 } else {
637 stdoutput.printf("\n");
638 }
639 return true;
640 } else if (!charstring::compareIgnoringCase(ptr,"run",3)) {
641 ptr=ptr+3;
642 cmdtype=6;
643 } else if (!charstring::compareIgnoringCase(ptr,"@",1)) {
644 ptr=ptr+1;
645 cmdtype=6;
646 } else if (!charstring::compareIgnoringCase(ptr,"delimiter",9) ||
647 !charstring::compareIgnoringCase(ptr,"delimeter",9)) {
648 ptr=ptr+9;
649 cmdtype=7;
650 } else if (!charstring::compareIgnoringCase(ptr,"identify")) {
651 return identify(sqlrcon,env);
652 } else if (!charstring::compareIgnoringCase(ptr,"dbversion")) {
653 return dbversion(sqlrcon,env);
654 } else if (!charstring::compareIgnoringCase(ptr,"dbhostname")) {
655 return dbhostname(sqlrcon,env);
656 } else if (!charstring::compareIgnoringCase(ptr,"dbipaddress")) {
657 return dbipaddress(sqlrcon,env);
658 } else if (!charstring::compareIgnoringCase(ptr,"clientversion")) {
659 clientversion(sqlrcon,env);
660 return true;
661 } else if (!charstring::compareIgnoringCase(ptr,"serverversion")) {
662 return serverversion(sqlrcon,env);
663 } else if (!charstring::compareIgnoringCase(ptr,"inputbind ",10)) {
664 return inputbind(sqlrcur,env,command);
665 } else if (!charstring::compareIgnoringCase(ptr,"inputbindblob ",14)) {
666 return inputbindblob(sqlrcur,env,command);
667 } else if (!charstring::compareIgnoringCase(ptr,"outputbind ",11)) {
668 return outputbind(sqlrcur,env,command);
669 } else if (!charstring::compareIgnoringCase(
670 ptr,"inputoutputbind ",16)) {
671 return inputoutputbind(sqlrcur,env,command);
672 } else if (!charstring::compareIgnoringCase(ptr,"printbinds")) {
673 printbinds("Input",&env->inputbinds);
674 stdoutput.printf("\n");
675 printbinds("Output",&env->outputbinds);
676 stdoutput.printf("\n");
677 printbinds("Input/Output",&env->inputoutputbinds);
678 return true;
679 } else if (!charstring::compareIgnoringCase(ptr,"clearinputbind",14)) {
680 env->clearbinds(&env->inputbinds);
681 return true;
682 } else if (!charstring::compareIgnoringCase(ptr,"clearoutputbind",15)) {
683 env->clearbinds(&env->outputbinds);
684 return true;
685 } else if (!charstring::compareIgnoringCase(ptr,
686 "clearinputoutputbind",20)) {
687 env->clearbinds(&env->inputoutputbinds);
688 return true;
689 } else if (!charstring::compareIgnoringCase(ptr,"clearbinds")) {
690 env->clearbinds(&env->inputbinds);
691 env->clearbinds(&env->outputbinds);
692 env->clearbinds(&env->inputoutputbinds);
693 return true;
694 } else if (!charstring::compareIgnoringCase(ptr,"lastinsertid")) {
695 if (!lastinsertid(sqlrcon,env)) {
696 displayError(env,NULL,
697 sqlrcon->errorMessage(),
698 sqlrcon->errorNumber());
699 return false;
700 }
701 return true;
702 } else if (!charstring::compareIgnoringCase(ptr,"setclientinfo ",14)) {
703 setclientinfo(sqlrcon,command);
704 return true;
705 } else if (!charstring::compareIgnoringCase(ptr,"getclientinfo")) {
706 getclientinfo(sqlrcon);
707 return true;
708 } else if (!charstring::compareIgnoringCase(
709 ptr,"setresultsetbuffersize ",23)) {
710 ptr=ptr+23;
711 env->rsbs=charstring::toInteger(ptr);
712 return true;
713 } else if (!charstring::compareIgnoringCase(ptr,"lazyfetch ",10)) {
714 ptr=ptr+10;
715 cmdtype=12;
716 } else if (!charstring::compareIgnoringCase(
717 ptr,"getresultsetbuffersize")) {
718 stdoutput.printf("%lld\n",(long long)env->rsbs);
719 return true;
720 } else if (!charstring::compareIgnoringCase(ptr,"endsession")) {
721 sqlrcon->endSession();
722 return true;
723 } else if (!charstring::compareIgnoringCase(ptr,"querytree")) {
724 xmldom xmld;
725 if (xmld.parseString(sqlrcur->getQueryTree())) {
726 xmld.getRootNode()->write(&stdoutput,true);
727 }
728 return true;
729 } else if (!charstring::compareIgnoringCase(ptr,"translatedquery")) {
730 stdoutput.printf("%s\n",sqlrcur->getTranslatedQuery());
731 return true;
732 } else if (!charstring::compareIgnoringCase(
733 ptr,"response timeout",16)) {
734 responseTimeout(sqlrcon,command);
735 return true;
736 } else if (!charstring::compareIgnoringCase(ptr,"cache ",6)) {
737 return cache(env,sqlrcur,command);
738 } else if (!charstring::compareIgnoringCase(ptr,"opencache ",10)) {
739 return openCache(env,sqlrcur,command);
740 } else {
741 return false;
742 }
743
744 // skip white space
745 while (*ptr==' ' || *ptr==' ' || *ptr=='\n') {
746 ptr++;
747 }
748
749 // handle scripts
750 if (cmdtype==6) {
751 return runScript(sqlrcon,sqlrcur,env,ptr,true);
752 }
753
754 // handle debug
755 if (cmdtype==4) {
756 if (!charstring::compareIgnoringCase(ptr,"on",2)) {
757 sqlrcon->debugOn();
758 sqlrcon->setDebugFile(NULL);
759 } else if (!charstring::compareIgnoringCase(ptr,"off",3)) {
760 sqlrcon->debugOff();
761 sqlrcon->setDebugFile(NULL);
762 } else {
763 sqlrcon->debugOn();
764 sqlrcon->setDebugFile(ptr);
765 }
766 return true;
767 }
768
769 // handle nullsasnulls
770 if (cmdtype==9) {
771 if (!charstring::compareIgnoringCase(ptr,"on",2)) {
772 sqlrcur->getNullsAsNulls();
773 } else if (!charstring::compareIgnoringCase(ptr,"off",3)) {
774 sqlrcur->getNullsAsEmptyStrings();
775 }
776 return true;
777 }
778
779 // handle format
780 if (cmdtype==10) {
781 if (!charstring::compareIgnoringCase(ptr,"csv",3)) {
782 env->format=SQLRSH_FORMAT_CSV;
783 } else {
784 env->format=SQLRSH_FORMAT_PLAIN;
785 }
786 return true;
787 }
788
789 // on or off?
790 bool toggle=false;
791 if (!charstring::compareIgnoringCase(ptr,"on",2)) {
792 toggle=true;
793 }
794
795 // set parameter
796 switch (cmdtype) {
797 case 2:
798 env->headers=toggle;
799 break;
800 case 11:
801 env->divider=toggle;
802 break;
803 case 3:
804 env->stats=toggle;
805 break;
806 case 5:
807 env->final=toggle;
808 break;
809 case 7:
810 env->delimiter=ptr[0];
811 stdoutput.printf("Delimiter set to %c\n",
812 env->delimiter);
813 break;
814 case 8:
815 if (toggle) {
816 if (sqlrcon->autoCommitOn()) {
817 stdoutput.printf(
818 "Autocommit set on\n");
819 } else {
820 displayError(env,NULL,
821 sqlrcon->errorMessage(),
822 sqlrcon->errorNumber());
823 return false;
824 }
825 } else {
826 if (sqlrcon->autoCommitOff()) {
827 stdoutput.printf(
828 "Autocommit set off\n");
829 } else {
830 displayError(env,NULL,
831 sqlrcon->errorMessage(),
832 sqlrcon->errorNumber());
833 return false;
834 }
835 }
836 break;
837 case 12:
838 env->lazyfetch=toggle;
839 break;
840 }
841 return true;
842 }
843
externalCommand(sqlrconnection * sqlrcon,sqlrcursor * sqlrcur,sqlrshenv * env,const char * command)844 bool sqlrsh::externalCommand(sqlrconnection *sqlrcon,
845 sqlrcursor *sqlrcur, sqlrshenv *env,
846 const char *command) {
847
848 bool retval=true;
849
850 // handle begin, commit and rollback
851 if (!charstring::compareIgnoringCase(command,"begin")) {
852
853 if (!sqlrcon->begin()) {
854 displayError(env,NULL,
855 sqlrcon->errorMessage(),
856 sqlrcon->errorNumber());
857 retval=false;
858 }
859
860 } else if (!charstring::compareIgnoringCase(command,"commit")) {
861
862 if (!sqlrcon->commit()) {
863 displayError(env,NULL,
864 sqlrcon->errorMessage(),
865 sqlrcon->errorNumber());
866 retval=false;
867 }
868
869 } else if (!charstring::compareIgnoringCase(command,"rollback")) {
870
871 if (!sqlrcon->rollback()) {
872 displayError(env,NULL,
873 sqlrcon->errorMessage(),
874 sqlrcon->errorNumber());
875 retval=false;
876 }
877
878 } else if (!charstring::compareIgnoringCase(command,"fields ",7)) {
879
880 char *table=getTable(command,false);
881 sqlrcur->getColumnList(table,NULL);
882 delete[] table;
883
884 for (uint64_t j=0; j<sqlrcur->rowCount(); j++) {
885 if (j>0) {
886 stdoutput.printf(",");
887 }
888 stdoutput.printf("%s",sqlrcur->getField(j,(uint32_t)0));
889 }
890 stdoutput.printf("\n");
891
892 if (env->final) {
893 sqlrcon->endSession();
894 }
895
896 } else {
897
898 sqlrcur->setResultSetBufferSize(env->rsbs);
899
900 if (env->lazyfetch) {
901 sqlrcur->lazyFetch();
902 } else {
903 sqlrcur->dontLazyFetch();
904 }
905
906 // send the query
907 if (!charstring::compareIgnoringCase(command,
908 "show databases odbc",19)) {
909 char *wild=getWild(command);
910 sqlrcur->getDatabaseList(wild,
911 SQLRCLIENTLISTFORMAT_ODBC);
912 delete[] wild;
913 } else if (!charstring::compareIgnoringCase(command,
914 "show databases",14)) {
915 char *wild=getWild(command);
916 sqlrcur->getDatabaseList(wild);
917 delete[] wild;
918 } else if (!charstring::compareIgnoringCase(command,
919 "show schemas",12)) {
920 char *wild=getWild(command);
921 sqlrcur->getSchemaList(wild);
922 delete[] wild;
923 } else if (!charstring::compareIgnoringCase(command,
924 "show tables odbc",16)) {
925 char *wild=getWild(command);
926 sqlrcur->getTableList(wild,
927 SQLRCLIENTLISTFORMAT_ODBC,
928 DB_OBJECT_TABLE|
929 DB_OBJECT_VIEW|
930 DB_OBJECT_ALIAS|
931 DB_OBJECT_SYNONYM);
932 delete[] wild;
933 } else if (!charstring::compareIgnoringCase(command,
934 "show only tables odbc",21)) {
935 char *wild=getWild(command);
936 sqlrcur->getTableList(wild,
937 SQLRCLIENTLISTFORMAT_ODBC,
938 DB_OBJECT_TABLE);
939 delete[] wild;
940 } else if (!charstring::compareIgnoringCase(command,
941 "show only views odbc",20)) {
942 char *wild=getWild(command);
943 sqlrcur->getTableList(wild,
944 SQLRCLIENTLISTFORMAT_ODBC,
945 DB_OBJECT_VIEW);
946 delete[] wild;
947 } else if (!charstring::compareIgnoringCase(command,
948 "show only aliases odbc",22)) {
949 char *wild=getWild(command);
950 sqlrcur->getTableList(wild,
951 SQLRCLIENTLISTFORMAT_ODBC,
952 DB_OBJECT_ALIAS);
953 delete[] wild;
954 } else if (!charstring::compareIgnoringCase(command,
955 "show only synonyms odbc",23)) {
956 char *wild=getWild(command);
957 sqlrcur->getTableList(wild,
958 SQLRCLIENTLISTFORMAT_ODBC,
959 DB_OBJECT_SYNONYM);
960 delete[] wild;
961 } else if (!charstring::compareIgnoringCase(command,
962 "show tables",11)) {
963 char *wild=getWild(command);
964 sqlrcur->getTableList(wild);
965 delete[] wild;
966 } else if (!charstring::compareIgnoringCase(command,
967 "show table types",16)) {
968 char *wild=getWild(command);
969 sqlrcur->getTableTypeList(wild);
970 delete[] wild;
971 } else if (!charstring::compareIgnoringCase(command,
972 "show columns odbc",17)) {
973 char *table=getTable(command,true);
974 char *wild=getWild(command);
975 sqlrcur->getColumnList(table,wild,
976 SQLRCLIENTLISTFORMAT_ODBC);
977 delete[] table;
978 delete[] wild;
979 } else if (!charstring::compareIgnoringCase(command,
980 "show columns",12)) {
981 char *table=getTable(command,true);
982 char *wild=getWild(command);
983 sqlrcur->getColumnList(table,wild);
984 delete[] table;
985 delete[] wild;
986 } else if (!charstring::compareIgnoringCase(command,
987 "show primary keys",17)) {
988 char *table=getTable(command,true);
989 char *wild=getWild(command);
990 sqlrcur->getPrimaryKeysList(table,wild);
991 delete[] table;
992 delete[] wild;
993 } else if (!charstring::compareIgnoringCase(command,
994 "show keys and indexes",21)) {
995 char *table=getTable(command,true);
996 char *wild=getWild(command);
997 sqlrcur->getKeyAndIndexList(table,wild);
998 delete[] table;
999 delete[] wild;
1000 } else if (!charstring::compareIgnoringCase(command,
1001 "describe ",9)) {
1002 char *table=getTable(command,false);
1003 char *wild=getWild(command);
1004 sqlrcur->getColumnList(table,wild);
1005 delete[] table;
1006 delete[] wild;
1007 } else if (!charstring::compareIgnoringCase(command,
1008 "show procedure binds and columns",32)) {
1009 char *procedure=getProcedure(command);
1010 char *wild=getWild(command);
1011 sqlrcur->getProcedureBindAndColumnList(procedure,wild);
1012 delete[] procedure;
1013 delete[] wild;
1014 } else if (!charstring::compareIgnoringCase(command,
1015 "show type info",14)) {
1016 char *type=getType(command);
1017 sqlrcur->getTypeInfoList(type,NULL);
1018 delete[] type;
1019 } else if (!charstring::compareIgnoringCase(command,
1020 "show procedures",15)) {
1021 char *wild=getWild(command);
1022 sqlrcur->getProcedureList(wild);
1023 delete[] wild;
1024 } else if (!charstring::compareIgnoringCase(command,
1025 "reexecute")) {
1026 executeQuery(sqlrcur,env);
1027 } else {
1028 sqlrcur->prepareQuery(command);
1029 executeQuery(sqlrcur,env);
1030 }
1031
1032 // look for an error
1033 if (sqlrcur->errorMessage()) {
1034
1035 // display the error
1036 displayError(env,NULL,
1037 sqlrcur->errorMessage(),
1038 sqlrcur->errorNumber());
1039 retval=false;
1040
1041 } else if (env->nextresultset) {
1042
1043 do {
1044
1045 // display the header
1046 displayHeader(sqlrcur,env);
1047
1048 // display the result set
1049 displayResultSet(sqlrcur,env);
1050
1051 // display any errors
1052 if (sqlrcur->errorMessage()) {
1053 displayError(env,NULL,
1054 sqlrcur->errorMessage(),
1055 sqlrcur->errorNumber());
1056 retval=false;
1057 }
1058
1059 } while (sqlrcur->nextResultSet());
1060
1061 } else {
1062
1063 // display the header
1064 displayHeader(sqlrcur,env);
1065
1066 // display the result set
1067 displayResultSet(sqlrcur,env);
1068 }
1069
1070 if (env->final) {
1071 sqlrcon->endSession();
1072 }
1073 }
1074
1075 // display statistics
1076 displayStats(sqlrcur,env);
1077
1078 return retval;
1079 }
1080
executeQuery(sqlrcursor * sqlrcur,sqlrshenv * env)1081 void sqlrsh::executeQuery(sqlrcursor *sqlrcur, sqlrshenv *env) {
1082
1083 sqlrcur->clearBinds();
1084
1085 if (env->inputbinds.getList()->getLength()) {
1086
1087 for (linkedlistnode<dictionarynode<char *, sqlrshbindvalue *> *>
1088 *node=env->inputbinds.getList()->getFirst();
1089 node; node=node->getNext()) {
1090
1091 const char *name=node->getValue()->getKey();
1092 sqlrshbindvalue *bv=node->getValue()->getValue();
1093 if (bv->type==SQLRCLIENTBINDVARTYPE_STRING) {
1094 sqlrcur->inputBind(name,bv->stringval);
1095 } else if (bv->type==SQLRCLIENTBINDVARTYPE_INTEGER) {
1096 sqlrcur->inputBind(name,bv->integerval);
1097 } else if (bv->type==SQLRCLIENTBINDVARTYPE_DOUBLE) {
1098 sqlrcur->inputBind(name,bv->doubleval.value,
1099 bv->doubleval.precision,
1100 bv->doubleval.scale);
1101 } else if (bv->type==SQLRCLIENTBINDVARTYPE_DATE) {
1102 sqlrcur->inputBind(name,
1103 bv->dateval.year,
1104 bv->dateval.month,
1105 bv->dateval.day,
1106 bv->dateval.hour,
1107 bv->dateval.minute,
1108 bv->dateval.second,
1109 bv->dateval.microsecond,
1110 bv->dateval.tz,
1111 bv->dateval.isnegative);
1112 } else if (bv->type==SQLRCLIENTBINDVARTYPE_BLOB) {
1113 sqlrcur->inputBindBlob(name,bv->stringval,
1114 charstring::length(bv->stringval));
1115 } else if (bv->type==SQLRCLIENTBINDVARTYPE_NULL) {
1116 sqlrcur->inputBind(name,(const char *)NULL);
1117 }
1118 }
1119 }
1120
1121 if (env->outputbinds.getList()->getLength()) {
1122
1123 for (linkedlistnode<dictionarynode<char *, sqlrshbindvalue *> *>
1124 *node=env->outputbinds.getList()->getFirst();
1125 node; node=node->getNext()) {
1126
1127 const char *name=node->getValue()->getKey();
1128 sqlrshbindvalue *bv=node->getValue()->getValue();
1129 if (bv->type==SQLRCLIENTBINDVARTYPE_STRING) {
1130 // FIXME: make buffer length variable
1131 sqlrcur->defineOutputBindString(name,
1132 bv->outputstringbindlength);
1133 } else if (bv->type==SQLRCLIENTBINDVARTYPE_INTEGER) {
1134 sqlrcur->defineOutputBindInteger(name);
1135 } else if (bv->type==SQLRCLIENTBINDVARTYPE_DOUBLE) {
1136 sqlrcur->defineOutputBindDouble(name);
1137 } else if (bv->type==SQLRCLIENTBINDVARTYPE_DATE) {
1138 sqlrcur->defineOutputBindDate(name);
1139 }
1140 }
1141 }
1142
1143 if (env->inputoutputbinds.getList()->getLength()) {
1144
1145 for (linkedlistnode<dictionarynode<char *, sqlrshbindvalue *> *>
1146 *node=env->inputoutputbinds.getList()->getFirst();
1147 node; node=node->getNext()) {
1148
1149 const char *name=node->getValue()->getKey();
1150 sqlrshbindvalue *bv=node->getValue()->getValue();
1151 if (bv->type==SQLRCLIENTBINDVARTYPE_STRING) {
1152 sqlrcur->defineInputOutputBindString(name,
1153 bv->stringval,
1154 bv->outputstringbindlength);
1155 } else if (bv->type==SQLRCLIENTBINDVARTYPE_INTEGER) {
1156 sqlrcur->defineInputOutputBindInteger(name,
1157 bv->integerval);
1158 } else if (bv->type==SQLRCLIENTBINDVARTYPE_DOUBLE) {
1159 sqlrcur->defineInputOutputBindDouble(name,
1160 bv->doubleval.value,
1161 bv->doubleval.precision,
1162 bv->doubleval.scale);
1163 } else if (bv->type==SQLRCLIENTBINDVARTYPE_DATE) {
1164 sqlrcur->defineInputOutputBindDate(name,
1165 bv->dateval.year,
1166 bv->dateval.month,
1167 bv->dateval.day,
1168 bv->dateval.hour,
1169 bv->dateval.minute,
1170 bv->dateval.second,
1171 bv->dateval.microsecond,
1172 bv->dateval.tz,
1173 bv->dateval.isnegative);
1174 }
1175 }
1176 }
1177
1178 sqlrcur->executeQuery();
1179
1180 if (env->outputbinds.getList()->getLength()) {
1181
1182 for (linkedlistnode<dictionarynode<char *, sqlrshbindvalue *> *>
1183 *node=env->outputbinds.getList()->getFirst();
1184 node; node=node->getNext()) {
1185
1186 const char *name=node->getValue()->getKey();
1187 sqlrshbindvalue *bv=node->getValue()->getValue();
1188 if (bv->type==SQLRCLIENTBINDVARTYPE_STRING) {
1189 delete[] bv->stringval;
1190 bv->stringval=charstring::duplicate(
1191 sqlrcur->getOutputBindString(name));
1192 } else if (bv->type==SQLRCLIENTBINDVARTYPE_INTEGER) {
1193 bv->integerval=
1194 sqlrcur->getOutputBindInteger(name);
1195 } else if (bv->type==SQLRCLIENTBINDVARTYPE_DOUBLE) {
1196 bv->doubleval.value=
1197 sqlrcur->getOutputBindDouble(name);
1198 } else if (bv->type==SQLRCLIENTBINDVARTYPE_DATE) {
1199 sqlrcur->getOutputBindDate(name,
1200 &(bv->dateval.year),
1201 &(bv->dateval.month),
1202 &(bv->dateval.day),
1203 &(bv->dateval.hour),
1204 &(bv->dateval.minute),
1205 &(bv->dateval.second),
1206 &(bv->dateval.microsecond),
1207 &(bv->dateval.tz),
1208 &(bv->dateval.isnegative));
1209 }
1210 }
1211 }
1212
1213 if (env->inputoutputbinds.getList()->getLength()) {
1214
1215 for (linkedlistnode<dictionarynode<char *, sqlrshbindvalue *> *>
1216 *node=env->inputoutputbinds.getList()->getFirst();
1217 node; node=node->getNext()) {
1218
1219 const char *name=node->getValue()->getKey();
1220 sqlrshbindvalue *bv=node->getValue()->getValue();
1221 if (bv->type==SQLRCLIENTBINDVARTYPE_STRING) {
1222 delete[] bv->stringval;
1223 bv->stringval=charstring::duplicate(
1224 sqlrcur->getInputOutputBindString(name));
1225 } else if (bv->type==SQLRCLIENTBINDVARTYPE_INTEGER) {
1226 bv->integerval=
1227 sqlrcur->getInputOutputBindInteger(name);
1228 } else if (bv->type==SQLRCLIENTBINDVARTYPE_DOUBLE) {
1229 bv->doubleval.value=
1230 sqlrcur->getInputOutputBindDouble(name);
1231 } else if (bv->type==SQLRCLIENTBINDVARTYPE_DATE) {
1232 sqlrcur->getInputOutputBindDate(name,
1233 &(bv->dateval.year),
1234 &(bv->dateval.month),
1235 &(bv->dateval.day),
1236 &(bv->dateval.hour),
1237 &(bv->dateval.minute),
1238 &(bv->dateval.second),
1239 &(bv->dateval.microsecond),
1240 &(bv->dateval.tz),
1241 &(bv->dateval.isnegative));
1242 }
1243 }
1244 }
1245 }
1246
getWild(const char * command)1247 char *sqlrsh::getWild(const char *command) {
1248 const char *wildptr=charstring::findFirst(command,"'");
1249 if (!wildptr) {
1250 return NULL;
1251 }
1252 wildptr++;
1253 const char *endptr=charstring::findLast(wildptr,"'");
1254 if (!endptr) {
1255 return NULL;
1256 }
1257
1258 // unescape single quotes
1259 stringbuffer output;
1260 for (const char *ch=wildptr; ch<endptr; ch++) {
1261 if (*ch=='\'' && *(ch+1)=='\'') {
1262 ch++;
1263 }
1264 output.append(*ch);
1265 }
1266
1267 return output.detachString();
1268 }
1269
getTable(const char * command,bool in)1270 char *sqlrsh::getTable(const char *command, bool in) {
1271 const char *tableptr=NULL;
1272 if (in) {
1273 tableptr=charstring::findFirst(command," in ");
1274 if (!tableptr) {
1275 return NULL;
1276 }
1277 tableptr=tableptr+4;
1278 const char *endptr=charstring::findFirst(tableptr," ");
1279 if (!endptr) {
1280 return charstring::duplicate(tableptr);
1281 }
1282 return charstring::duplicate(tableptr,endptr-tableptr);
1283 } else {
1284 tableptr=charstring::findFirst(command," ");
1285 if (!tableptr) {
1286 return NULL;
1287 }
1288 return charstring::duplicate(tableptr+1);
1289 }
1290 return NULL;
1291 }
1292
getProcedure(const char * command)1293 char *sqlrsh::getProcedure(const char *command) {
1294 const char *procptr=charstring::findFirst(command," in ");
1295 if (!procptr) {
1296 return NULL;
1297 }
1298 procptr=procptr+4;
1299 const char *endptr=charstring::findFirst(procptr," ");
1300 if (!endptr) {
1301 return charstring::duplicate(procptr);
1302 }
1303 return charstring::duplicate(procptr,endptr-procptr);
1304 }
1305
getType(const char * command)1306 char *sqlrsh::getType(const char *command) {
1307 const char *procptr=charstring::findFirst(command," for ");
1308 if (!procptr) {
1309 return NULL;
1310 }
1311 procptr=procptr+5;
1312 const char *endptr=charstring::findFirst(procptr," ");
1313 if (!endptr) {
1314 return charstring::duplicate(procptr);
1315 }
1316 return charstring::duplicate(procptr,endptr-procptr);
1317 }
1318
initStats(sqlrshenv * env)1319 void sqlrsh::initStats(sqlrshenv *env) {
1320
1321 if (!env->stats) {
1322 return;
1323 }
1324
1325 start.getSystemDateAndTime();
1326 }
1327
displayError(sqlrshenv * env,const char * message,const char * error,int64_t errornumber)1328 void sqlrsh::displayError(sqlrshenv *env,
1329 const char *message,
1330 const char *error,
1331 int64_t errornumber) {
1332 if (!charstring::isNullOrEmpty(message)) {
1333 stderror.printf("%s\n",message);
1334 }
1335 stderror.printf("%lld:\n",(long long)errornumber);
1336 if (!charstring::isNullOrEmpty(error)) {
1337 stderror.printf("%s\n\n",error);
1338 }
1339 }
1340
displayHeader(sqlrcursor * sqlrcur,sqlrshenv * env)1341 void sqlrsh::displayHeader(sqlrcursor *sqlrcur, sqlrshenv *env) {
1342
1343 if (!env->headers) {
1344 return;
1345 }
1346
1347 // display column names
1348 uint32_t charcount=0;
1349 uint32_t colcount=sqlrcur->colCount();
1350 const char *name;
1351 uint32_t namelen;
1352 uint32_t longest;
1353
1354 if (!colcount) {
1355 return;
1356 }
1357
1358 // iterate through columns
1359 for (uint32_t ci=0; ci<sqlrcur->colCount(); ci++) {
1360
1361 // put a comma or extra space between field names
1362 if (ci) {
1363 if (env->format==SQLRSH_FORMAT_CSV) {
1364 stdoutput.write(',');
1365 } else {
1366 stdoutput.write(' ');
1367 }
1368 charcount=charcount+1;
1369 }
1370
1371 // write the column name
1372 if (env->format==SQLRSH_FORMAT_CSV) {
1373 stdoutput.write('\"');
1374 }
1375 name=sqlrcur->getColumnName(ci);
1376 stdoutput.write(name);
1377 if (env->format==SQLRSH_FORMAT_CSV) {
1378 stdoutput.write('\"');
1379 }
1380 namelen=charstring::length(name);
1381
1382 // space-pad after the name, if necessary
1383 if (env->format==SQLRSH_FORMAT_PLAIN) {
1384 longest=sqlrcur->getLongest(ci);
1385 if (namelen>longest) {
1386 longest=namelen;
1387 }
1388 charcount=charcount+longest;
1389
1390 // pad after the name with spaces
1391 for (uint32_t j=namelen; j<longest; j++) {
1392 stdoutput.write(' ');
1393 }
1394 } else {
1395 charcount=charcount+namelen+2;
1396 }
1397 }
1398 stdoutput.printf("\n");
1399
1400 // display divider
1401 if (env->divider) {
1402 for (uint32_t i=0; i<charcount; i++) {
1403 stdoutput.printf("=");
1404 }
1405 stdoutput.printf("\n");
1406 }
1407 }
1408
displayResultSet(sqlrcursor * sqlrcur,sqlrshenv * env)1409 void sqlrsh::displayResultSet(sqlrcursor *sqlrcur, sqlrshenv *env) {
1410
1411 uint32_t colcount=sqlrcur->colCount();
1412 if (!colcount) {
1413 return;
1414 }
1415
1416 uint32_t namelen;
1417 uint32_t longest;
1418 const char *field;
1419 uint32_t fieldlength;
1420 const char *fieldtype;
1421 char numberfieldbuffer[256];
1422
1423 bool done=false;
1424 for (uint64_t row=0; !done; row++) {
1425
1426 for (uint32_t col=0; col<colcount; col++) {
1427
1428 // put a comma or extra space between fields
1429 if (col) {
1430 if (env->format==SQLRSH_FORMAT_CSV) {
1431 stdoutput.write(',');
1432 } else {
1433 stdoutput.write(' ');
1434 }
1435 }
1436
1437 // get the field
1438 field=sqlrcur->getField(row,col);
1439 fieldlength=sqlrcur->getFieldLength(row,col);
1440 fieldtype=sqlrcur->getColumnType(col);
1441
1442 // FIXME: move this down below the end-of-rs check?
1443 // The purpose of this is to verify the functionality
1444 // of the getFieldAsXXX() methods.
1445 if (field && env->getasnumber &&
1446 (isBitTypeChar(fieldtype) ||
1447 isNumberTypeChar(fieldtype))) {
1448
1449 if (isFloatTypeChar(fieldtype)) {
1450 double fd=sqlrcur->getFieldAsDouble(row,col);
1451 if (isNonScaleFloatTypeChar(fieldtype)) {
1452 int32_t precision=sqlrcur->getColumnPrecision(col);
1453 // here precision is a number of bits, but printf %g wants digits.
1454 // FIXME: precision should actually be the number of digits, not bits...
1455 int32_t digits=(int32_t)(ceil(precision/3.33));
1456 charstring::printf(&numberfieldbuffer[0],sizeof(numberfieldbuffer),"%.*g",digits,fd);
1457 } else {
1458 int scale=sqlrcur->getColumnScale(col);
1459 // NOTE: we are not using the precision to format the number to a string.
1460 charstring::printf(&numberfieldbuffer[0],sizeof(numberfieldbuffer),"%.*f",scale,fd);
1461 }
1462 } else {
1463 int64_t fi = sqlrcur->getFieldAsInteger(row,col);
1464 charstring::printf(&numberfieldbuffer[0], sizeof(numberfieldbuffer), "%ld", fi);
1465 }
1466 field=numberfieldbuffer;
1467 fieldlength=charstring::length(field);
1468 }
1469
1470 // check for end-of-result-set condition
1471 // (since nullsasnulls might be set, we have to do
1472 // a bit more than just check for a NULL)
1473 if (!col && !field &&
1474 sqlrcur->endOfResultSet() &&
1475 row==sqlrcur->rowCount()) {
1476 done=true;
1477 break;
1478 }
1479
1480 // handle nulls
1481 if (!field) {
1482 field="NULL";
1483 fieldlength=4;
1484 }
1485
1486 // write the field
1487 if (env->format==SQLRSH_FORMAT_CSV) {
1488 stdoutput.write('\"');
1489 }
1490 stdoutput.write(field);
1491 if (env->format==SQLRSH_FORMAT_CSV) {
1492 stdoutput.write('\"');
1493 }
1494
1495 // space-pad after the field, if necessary
1496 if (env->format==SQLRSH_FORMAT_PLAIN) {
1497 longest=sqlrcur->getLongest(col);
1498 if (env->headers) {
1499 namelen=charstring::length(
1500 sqlrcur->getColumnName(col));
1501 if (namelen>longest) {
1502 longest=namelen;
1503 }
1504 }
1505 for (uint32_t i=fieldlength; i<longest; i++) {
1506 stdoutput.write(' ');
1507 }
1508 }
1509 }
1510 stdoutput.write('\n');
1511 }
1512 }
1513
displayStats(sqlrcursor * sqlrcur,sqlrshenv * env)1514 void sqlrsh::displayStats(sqlrcursor *sqlrcur, sqlrshenv *env) {
1515
1516 if (!env->stats) {
1517 return;
1518 }
1519
1520 // calculate elapsed time
1521 datetime end;
1522 end.getSystemDateAndTime();
1523 uint64_t startusec=start.getEpoch()*1000000+
1524 start.getMicroseconds();
1525 uint64_t endusec=end.getEpoch()*1000000+
1526 end.getMicroseconds();
1527 double time=((double)(endusec-startusec))/1000000;
1528
1529 // display stats
1530 stdoutput.printf(" Rows Returned : ");
1531 stdoutput.printf("%lld\n",(long long)sqlrcur->rowCount());
1532 stdoutput.printf(" Fields Returned : ");
1533 stdoutput.printf("%lld\n",
1534 (long long)sqlrcur->rowCount()*sqlrcur->colCount());
1535 if (!env->noelapsed) {
1536 stdoutput.printf(" Elapsed Time : ");
1537 stdoutput.printf("%.6f sec\n",time);
1538 }
1539 stdoutput.printf("\n");
1540 }
1541
ping(sqlrconnection * sqlrcon,sqlrshenv * env)1542 bool sqlrsh::ping(sqlrconnection *sqlrcon, sqlrshenv *env) {
1543 bool result=sqlrcon->ping();
1544 if (result) {
1545 stdoutput.printf(" The database is up.\n");
1546 } else if (sqlrcon->errorMessage()) {
1547 displayError(env,NULL,
1548 sqlrcon->errorMessage(),
1549 sqlrcon->errorNumber());
1550 return false;
1551 } else {
1552 stdoutput.printf(" The database is down.\n");
1553 }
1554 return true;
1555 }
1556
lastinsertid(sqlrconnection * sqlrcon,sqlrshenv * env)1557 bool sqlrsh::lastinsertid(sqlrconnection *sqlrcon, sqlrshenv *env) {
1558 bool retval=false;
1559 uint64_t id=sqlrcon->getLastInsertId();
1560 if (id!=0 || !sqlrcon->errorMessage()) {
1561 stdoutput.printf("%lld\n",(long long)id);
1562 retval=true;
1563 }
1564 return retval;
1565 }
1566
identify(sqlrconnection * sqlrcon,sqlrshenv * env)1567 bool sqlrsh::identify(sqlrconnection *sqlrcon, sqlrshenv *env) {
1568 const char *value=sqlrcon->identify();
1569 if (value) {
1570 stdoutput.printf("%s\n",value);
1571 } else if (sqlrcon->errorMessage()) {
1572 displayError(env,NULL,
1573 sqlrcon->errorMessage(),
1574 sqlrcon->errorNumber());
1575 return false;
1576 } else {
1577 stdoutput.printf("\n");
1578 }
1579 return true;
1580 }
1581
dbversion(sqlrconnection * sqlrcon,sqlrshenv * env)1582 bool sqlrsh::dbversion(sqlrconnection *sqlrcon, sqlrshenv *env) {
1583 const char *value=sqlrcon->dbVersion();
1584 if (value) {
1585 stdoutput.printf("%s\n",value);
1586 } else if (sqlrcon->errorMessage()) {
1587 displayError(env,NULL,
1588 sqlrcon->errorMessage(),
1589 sqlrcon->errorNumber());
1590 return false;
1591 } else {
1592 stdoutput.printf("\n");
1593 }
1594 return true;
1595 }
1596
dbhostname(sqlrconnection * sqlrcon,sqlrshenv * env)1597 bool sqlrsh::dbhostname(sqlrconnection *sqlrcon, sqlrshenv *env) {
1598 const char *value=sqlrcon->dbHostName();
1599 if (value) {
1600 stdoutput.printf("%s\n",value);
1601 } else if (sqlrcon->errorMessage()) {
1602 displayError(env,NULL,
1603 sqlrcon->errorMessage(),
1604 sqlrcon->errorNumber());
1605 return false;
1606 } else {
1607 stdoutput.printf("\n");
1608 }
1609 return true;
1610 }
1611
dbipaddress(sqlrconnection * sqlrcon,sqlrshenv * env)1612 bool sqlrsh::dbipaddress(sqlrconnection *sqlrcon, sqlrshenv *env) {
1613 const char *value=sqlrcon->dbIpAddress();
1614 if (value) {
1615 stdoutput.printf("%s\n",value);
1616 } else if (sqlrcon->errorMessage()) {
1617 displayError(env,NULL,
1618 sqlrcon->errorMessage(),
1619 sqlrcon->errorNumber());
1620 return false;
1621 } else {
1622 stdoutput.printf("\n");
1623 }
1624 return true;
1625 }
1626
clientversion(sqlrconnection * sqlrcon,sqlrshenv * env)1627 void sqlrsh::clientversion(sqlrconnection *sqlrcon, sqlrshenv *env) {
1628 stdoutput.printf("%s\n",sqlrcon->clientVersion());
1629 }
1630
serverversion(sqlrconnection * sqlrcon,sqlrshenv * env)1631 bool sqlrsh::serverversion(sqlrconnection *sqlrcon, sqlrshenv *env) {
1632 const char *value=sqlrcon->serverVersion();
1633 if (value) {
1634 stdoutput.printf("%s\n",value);
1635 } else if (sqlrcon->errorMessage()) {
1636 displayError(env,NULL,
1637 sqlrcon->errorMessage(),
1638 sqlrcon->errorNumber());
1639 return false;
1640 } else {
1641 stdoutput.printf("\n");
1642 }
1643 return true;
1644 }
1645
inputbind(sqlrcursor * sqlrcur,sqlrshenv * env,const char * command)1646 bool sqlrsh::inputbind(sqlrcursor *sqlrcur,
1647 sqlrshenv *env, const char *command) {
1648
1649 // sanity check
1650 const char *ptr=command+10;
1651 const char *space=charstring::findFirst(ptr,' ');
1652 if (!space) {
1653 stderror.printf("usage: inputbind [variable] = [value]\n");
1654 return false;
1655 }
1656
1657 // get the variable name
1658 char *variable=charstring::duplicate(ptr,space-ptr);
1659
1660 // move on
1661 ptr=space;
1662 if (*(ptr+1)=='=' && *(ptr+2)==' ') {
1663 ptr=ptr+3;
1664 } else if (!charstring::compareIgnoringCase(ptr+1,"is null")) {
1665 ptr=NULL;
1666 } else {
1667 stderror.printf("usage: inputbind [variable] = [value]\n");
1668 stderror.printf(" inputbind [variable] is null\n");
1669 return false;
1670 }
1671
1672 // get the value
1673 char *value=charstring::duplicate(ptr);
1674 charstring::bothTrim(value);
1675 size_t valuelen=charstring::length(value);
1676
1677 // if the bind variable is already defined, clear it...
1678 sqlrshbindvalue *bv=NULL;
1679 if (env->inputbinds.getValue(variable,&bv)) {
1680 if (bv->type==SQLRCLIENTBINDVARTYPE_STRING) {
1681 delete[] bv->stringval;
1682 }
1683 delete bv;
1684 }
1685
1686 // define the variable
1687 bv=new sqlrshbindvalue;
1688
1689 // first handle nulls, then...
1690 // anything enclosed in quotes is a string
1691 // if it's unquoted, check to see if it's an integer, float or date
1692 // if it's not, then it's a string
1693 if (!value) {
1694 bv->type=SQLRCLIENTBINDVARTYPE_NULL;
1695 } else if ((value[0]=='\'' && value[valuelen-1]=='\'') ||
1696 (value[0]=='"' && value[valuelen-1]=='"')) {
1697
1698 bv->type=SQLRCLIENTBINDVARTYPE_STRING;
1699
1700 // trim off quotes
1701 char *newvalue=charstring::duplicate(value+1);
1702 newvalue[valuelen-2]='\0';
1703 delete[] value;
1704
1705 // unescape the string
1706 bv->stringval=charstring::unescape(newvalue);
1707 delete[] newvalue;
1708
1709 } else if (charstring::contains(value,"/") &&
1710 charstring::contains(value,":")) {
1711 int16_t year;
1712 int16_t month;
1713 int16_t day;
1714 int16_t hour;
1715 int16_t minute;
1716 int16_t second;
1717 int32_t microsecond;
1718 bool isnegative;
1719 parseDateTime(value,false,false,"/",
1720 &year,&month,&day,
1721 &hour,&minute,&second,
1722 µsecond,&isnegative);
1723 bv->type=SQLRCLIENTBINDVARTYPE_DATE;
1724 bv->dateval.year=year;
1725 bv->dateval.month=month;
1726 bv->dateval.day=day;
1727 bv->dateval.hour=hour;
1728 bv->dateval.minute=minute;
1729 bv->dateval.second=second;
1730 bv->dateval.microsecond=microsecond;
1731 bv->dateval.tz="";
1732 bv->dateval.isnegative=isnegative;
1733 delete[] value;
1734 } else if (charstring::isInteger(value)) {
1735 bv->type=SQLRCLIENTBINDVARTYPE_INTEGER;
1736 bv->integerval=charstring::toInteger(value);
1737 delete[] value;
1738 } else if (charstring::isNumber(value)) {
1739 bv->type=SQLRCLIENTBINDVARTYPE_DOUBLE;
1740 bv->doubleval.value=charstring::toFloatC(value);
1741 bv->doubleval.precision=valuelen-((value[0]=='-')?2:1);
1742 bv->doubleval.scale=
1743 charstring::findFirst(value,'.')-value+
1744 ((value[0]=='-')?0:1);
1745 delete[] value;
1746 } else {
1747 bv->type=SQLRCLIENTBINDVARTYPE_STRING;
1748 bv->stringval=value;
1749 }
1750
1751 // put the bind variable in the list
1752 env->inputbinds.setValue(variable,bv);
1753
1754 return true;
1755 }
1756
inputbindblob(sqlrcursor * sqlrcur,sqlrshenv * env,const char * command)1757 bool sqlrsh::inputbindblob(sqlrcursor *sqlrcur,
1758 sqlrshenv *env, const char *command) {
1759
1760 // sanity check
1761 const char *ptr=command+14;
1762 const char *space=charstring::findFirst(ptr,' ');
1763 if (!space) {
1764 stderror.printf("usage: inputbindblob [variable] = [value]\n");
1765 return false;
1766 }
1767
1768 // get the variable name
1769 char *variable=charstring::duplicate(ptr,space-ptr);
1770
1771 // move on
1772 ptr=space;
1773 if (*(ptr+1)=='=' && *(ptr+2)==' ') {
1774 ptr=ptr+3;
1775 } else if (!charstring::compareIgnoringCase(ptr+1,"is null")) {
1776 ptr=NULL;
1777 } else {
1778 stderror.printf("usage: inputbindblob [variable] = [value]\n");
1779 stderror.printf(" inputbindblob [variable] is null\n");
1780 return false;
1781 }
1782
1783 // get the value
1784 char *value=charstring::duplicate(ptr);
1785 charstring::bothTrim(value);
1786 size_t valuelen=charstring::length(value);
1787
1788 // if the bind variable is already defined, clear it...
1789 sqlrshbindvalue *bv=NULL;
1790 if (env->inputbinds.getValue(variable,&bv)) {
1791 if (bv->type==SQLRCLIENTBINDVARTYPE_STRING) {
1792 delete[] bv->stringval;
1793 }
1794 delete bv;
1795 }
1796
1797 // define the variable
1798 bv=new sqlrshbindvalue;
1799
1800 // first handle nulls, then...
1801 // anything enclosed in quotes is a string
1802 // if it's unquoted, check to see if it's an integer, float or date
1803 // if it's not, then it's a string
1804 if (!value) {
1805 bv->type=SQLRCLIENTBINDVARTYPE_NULL;
1806 } else if ((value[0]=='\'' && value[valuelen-1]=='\'') ||
1807 (value[0]=='"' && value[valuelen-1]=='"')) {
1808
1809 bv->type=SQLRCLIENTBINDVARTYPE_BLOB;
1810
1811 // trim off quotes
1812 char *newvalue=charstring::duplicate(value+1);
1813 newvalue[valuelen-2]='\0';
1814 delete[] value;
1815
1816 // unescape the string
1817 bv->stringval=charstring::unescape(newvalue);
1818 delete[] newvalue;
1819
1820 } else {
1821 bv->type=SQLRCLIENTBINDVARTYPE_BLOB;
1822 bv->stringval=value;
1823 }
1824
1825 // put the bind variable in the list
1826 env->inputbinds.setValue(variable,bv);
1827
1828 return true;
1829 }
1830
outputbind(sqlrcursor * sqlrcur,sqlrshenv * env,const char * command)1831 bool sqlrsh::outputbind(sqlrcursor *sqlrcur,
1832 sqlrshenv *env, const char *command) {
1833
1834 // split the command on ' '
1835 char **parts;
1836 uint64_t partcount;
1837 charstring::split(command," ",true,&parts,&partcount);
1838
1839 // sanity check...
1840 bool sane=true;
1841 if (partcount>2 && !charstring::compare(parts[0],"outputbind")) {
1842
1843 // if the bind variable is already defined, clear it...
1844 sqlrshbindvalue *bv=NULL;
1845 if (env->outputbinds.getValue(parts[1],&bv)) {
1846 if (bv->type==SQLRCLIENTBINDVARTYPE_STRING) {
1847 delete[] bv->stringval;
1848 }
1849 delete bv;
1850 }
1851
1852 // define the variable
1853 bv=new sqlrshbindvalue;
1854
1855 if (!charstring::compareIgnoringCase(
1856 parts[2],"string") &&
1857 partcount==4) {
1858 bv->type=SQLRCLIENTBINDVARTYPE_STRING;
1859 bv->stringval=NULL;
1860 bv->outputstringbindlength=
1861 charstring::toInteger(parts[3]);
1862 } else if (!charstring::compareIgnoringCase(
1863 parts[2],"integer") &&
1864 partcount==3) {
1865 bv->type=SQLRCLIENTBINDVARTYPE_INTEGER;
1866 bv->integerval=0;
1867 } else if (!charstring::compareIgnoringCase(
1868 parts[2],"double") &&
1869 partcount==5) {
1870 bv->type=SQLRCLIENTBINDVARTYPE_DOUBLE;
1871 bv->doubleval.value=0.0;
1872 bv->doubleval.precision=
1873 charstring::toInteger(parts[3]);
1874 bv->doubleval.scale=
1875 charstring::toInteger(parts[4]);
1876 } else if (!charstring::compareIgnoringCase(
1877 parts[2],"date") &&
1878 partcount==3) {
1879 bv->type=SQLRCLIENTBINDVARTYPE_DATE;
1880 bv->dateval.year=0;
1881 bv->dateval.month=0;
1882 bv->dateval.day=0;
1883 bv->dateval.hour=0;
1884 bv->dateval.minute=0;
1885 bv->dateval.second=0;
1886 bv->dateval.microsecond=0;
1887 bv->dateval.tz="";
1888 bv->dateval.isnegative=false;
1889 } else {
1890 sane=false;
1891 }
1892
1893 // put the bind variable in the list
1894 if (sane) {
1895 env->outputbinds.setValue(parts[1],bv);
1896 }
1897
1898 } else {
1899 sane=false;
1900 }
1901
1902 // clean up
1903 if (sane) {
1904 delete[] parts[0];
1905 } else {
1906 stderror.printf("usage: outputbind "
1907 // FIXME: not entirely accurate
1908 "[variable] [type] [length] [scale]\n");
1909 for (uint64_t i=0; i<partcount; i++) {
1910 delete[] parts[i];
1911 }
1912 }
1913 delete[] parts;
1914
1915 return sane;
1916 }
1917
inputoutputbind(sqlrcursor * sqlrcur,sqlrshenv * env,const char * command)1918 bool sqlrsh::inputoutputbind(sqlrcursor *sqlrcur,
1919 sqlrshenv *env, const char *command) {
1920
1921 // get the value
1922 char *value=NULL;
1923 const char *equals=charstring::findFirst(command,'=');
1924 if (equals) {
1925 value=charstring::duplicate(equals+1);
1926 charstring::bothTrim(value);
1927 charstring::bothTrim(value,'\'');
1928 } else if (charstring::compare(
1929 command+charstring::length(command)-8," is null")) {
1930 // FIXME: usage...
1931 return false;
1932 }
1933
1934 // split the command on ' '
1935 char **parts;
1936 uint64_t partcount;
1937 charstring::split(command," ",true,&parts,&partcount);
1938
1939 // sanity check...
1940 bool sane=true;
1941 if (partcount>=5 && !charstring::compare(parts[0],"inputoutputbind")) {
1942
1943 // if the bind variable is already defined, clear it...
1944 sqlrshbindvalue *bv=NULL;
1945 if (env->inputoutputbinds.getValue(parts[1],&bv)) {
1946 if (bv->type==SQLRCLIENTBINDVARTYPE_STRING) {
1947 delete[] bv->stringval;
1948 }
1949 delete bv;
1950 }
1951
1952 // define the variable
1953 bv=new sqlrshbindvalue;
1954
1955 if (!charstring::compareIgnoringCase(
1956 parts[2],"string") &&
1957 partcount>=6) {
1958 // inputoutputbind 1 string length = 'string'
1959 bv->type=SQLRCLIENTBINDVARTYPE_STRING;
1960 bv->outputstringbindlength=
1961 charstring::toInteger(parts[3]);
1962 bv->stringval=charstring::unescape(value);
1963 } else if (!charstring::compareIgnoringCase(
1964 parts[2],"integer") &&
1965 partcount==5) {
1966 // inputoutputbind 1 integer = value
1967 bv->type=SQLRCLIENTBINDVARTYPE_INTEGER;
1968 bv->integerval=charstring::toInteger(value);
1969 } else if (!charstring::compareIgnoringCase(
1970 parts[2],"double") &&
1971 partcount==7) {
1972 // inputoutputbind 1 double prec scale = value
1973 bv->type=SQLRCLIENTBINDVARTYPE_DOUBLE;
1974 bv->doubleval.value=charstring::toFloatC(value);
1975 bv->doubleval.precision=
1976 charstring::toInteger(parts[3]);
1977 bv->doubleval.scale=
1978 charstring::toInteger(parts[4]);
1979 } else if (!charstring::compareIgnoringCase(
1980 parts[2],"date") &&
1981 partcount>=5) {
1982 // inputoutputbind 1 date = '...'
1983 int16_t year;
1984 int16_t month;
1985 int16_t day;
1986 int16_t hour;
1987 int16_t minute;
1988 int16_t second;
1989 int32_t microsecond;
1990 bool isnegative;
1991 parseDateTime(value,false,false,"/",
1992 &year,&month,&day,
1993 &hour,&minute,&second,
1994 µsecond,&isnegative);
1995 bv->type=SQLRCLIENTBINDVARTYPE_DATE;
1996 bv->dateval.year=year;
1997 bv->dateval.month=month;
1998 bv->dateval.day=day;
1999 bv->dateval.hour=hour;
2000 bv->dateval.minute=minute;
2001 bv->dateval.second=second;
2002 bv->dateval.microsecond=microsecond;
2003 bv->dateval.tz="";
2004 bv->dateval.isnegative=isnegative;
2005 } else {
2006 sane=false;
2007 }
2008
2009 // put the bind variable in the list
2010 if (sane) {
2011 env->inputoutputbinds.setValue(parts[1],bv);
2012 }
2013
2014 } else {
2015 sane=false;
2016 }
2017
2018 // clean up
2019 if (sane) {
2020 delete[] parts[0];
2021 } else {
2022 stderror.printf("usage: inputoutputbind "
2023 // FIXME: not entirely accurate
2024 "[variable] [type] [length] [scale]\n");
2025 for (uint64_t i=0; i<partcount; i++) {
2026 delete[] parts[i];
2027 }
2028 }
2029 delete[] parts;
2030 delete[] value;
2031
2032 return sane;
2033 }
2034
printbinds(const char * type,dictionary<char *,sqlrshbindvalue * > * binds)2035 void sqlrsh::printbinds(const char *type,
2036 dictionary<char *, sqlrshbindvalue *> *binds) {
2037
2038 stdoutput.printf("%s bind variables:\n",type);
2039
2040 for (linkedlistnode<dictionarynode<char *, sqlrshbindvalue *> *>
2041 *node=binds->getList()->getFirst();
2042 node; node=node->getNext()) {
2043
2044 stdoutput.printf(" %s ",node->getValue()->getKey());
2045 sqlrshbindvalue *bv=node->getValue()->getValue();
2046 if (bv->type==SQLRCLIENTBINDVARTYPE_STRING) {
2047 stdoutput.printf("(STRING) = %s\n",bv->stringval);
2048 } else if (bv->type==SQLRCLIENTBINDVARTYPE_INTEGER) {
2049 stdoutput.printf("(INTEGER) = %lld\n",
2050 (long long)bv->integerval);
2051 } else if (bv->type==SQLRCLIENTBINDVARTYPE_DOUBLE) {
2052 stdoutput.printf("(DOUBLE %d,%d) = %*.*f\n",
2053 bv->doubleval.precision,
2054 bv->doubleval.scale,
2055 (int)bv->doubleval.precision,
2056 (int)bv->doubleval.scale,
2057 bv->doubleval.value);
2058 } else if (bv->type==SQLRCLIENTBINDVARTYPE_DATE) {
2059 stdoutput.printf("(DATE) = %02d/%02d/%04d "
2060 "%s%02d:%02d:%02d.%06d %s\n",
2061 bv->dateval.month,
2062 bv->dateval.day,
2063 bv->dateval.year,
2064 (bv->dateval.isnegative)?"-":"",
2065 bv->dateval.hour,
2066 bv->dateval.minute,
2067 bv->dateval.second,
2068 bv->dateval.microsecond,
2069 bv->dateval.tz);
2070 } else if (bv->type==SQLRCLIENTBINDVARTYPE_BLOB) {
2071 stdoutput.printf("(BLOB) = ");
2072 stdoutput.safePrint(bv->stringval,
2073 charstring::length(bv->stringval));
2074 stdoutput.printf("\n");
2075 } else if (bv->type==SQLRCLIENTBINDVARTYPE_NULL) {
2076 stdoutput.printf("NULL\n");
2077 }
2078 }
2079 }
2080
setclientinfo(sqlrconnection * sqlrcon,const char * command)2081 void sqlrsh::setclientinfo(sqlrconnection *sqlrcon, const char *command) {
2082 sqlrcon->setClientInfo(command+14);
2083 }
2084
getclientinfo(sqlrconnection * sqlrcon)2085 void sqlrsh::getclientinfo(sqlrconnection *sqlrcon) {
2086 const char *ci=sqlrcon->getClientInfo();
2087 stdoutput.printf("%s\n",(ci)?ci:"");
2088 }
2089
responseTimeout(sqlrconnection * sqlrcon,const char * command)2090 void sqlrsh::responseTimeout(sqlrconnection *sqlrcon, const char *command) {
2091
2092 // skip to timeout itself
2093 const char *value=command+16;
2094 while (character::isWhitespace(*value)) {
2095 value++;
2096 }
2097
2098 // get seconds
2099 uint32_t sec=charstring::toInteger(value);
2100
2101 // get milliseconds
2102 char msecbuf[5];
2103 bytestring::set(msecbuf,'0',4);
2104 msecbuf[4]='\0';
2105 const char *dot=charstring::findFirst(value,'.');
2106 if (dot) {
2107 value=dot+1;
2108 for (uint8_t i=0; i<4 && *value; i++) {
2109 msecbuf[i]=*value;
2110 value++;
2111 }
2112 }
2113 uint32_t msec=charstring::toInteger(msecbuf);
2114
2115 // set timeout
2116 sqlrcon->setResponseTimeout(sec,msec);
2117 stdoutput.printf("Response Timeout set to %d.%04d seconds\n",sec,msec);
2118 }
2119
cache(sqlrshenv * env,sqlrcursor * sqlrcur,const char * command)2120 bool sqlrsh::cache(sqlrshenv *env, sqlrcursor *sqlrcur, const char *command) {
2121
2122 // move to file name
2123 const char *ptr=command+6;
2124
2125 // skip whitespace
2126 while (*ptr==' ') {
2127 ptr++;
2128 }
2129
2130 // bail if no file name was given
2131 if (!*ptr) {
2132 stderror.printf(" No file name given\n\n");
2133 return false;
2134 }
2135
2136 // build filename
2137 stringbuffer fn;
2138 fn.append(sqlrpth->getCacheDir());
2139 bool inquotes=false;
2140 while (*ptr) {
2141 if (*ptr=='"') {
2142 inquotes=!inquotes;
2143 }
2144 if (*ptr==' ' && !inquotes) {
2145 break;
2146 }
2147 fn.append(*ptr);
2148 ptr++;
2149 }
2150 delete[] env->cacheto;
2151 env->cacheto=fn.detachString();
2152
2153 // find ttl
2154 while (*ptr==' ') {
2155 ptr++;
2156 }
2157 uint32_t cachettl=600;
2158 if (*ptr) {
2159 cachettl=charstring::toInteger(ptr);
2160 }
2161
2162 stdoutput.printf(" Caching To : %s\n",env->cacheto);
2163 stdoutput.printf(" Cache TTL Set To : %lld seconds\n\n",cachettl);
2164
2165 // begin caching
2166 sqlrcur->cacheToFile(env->cacheto);
2167 sqlrcur->setCacheTtl(cachettl);
2168
2169 return true;
2170 }
2171
openCache(sqlrshenv * env,sqlrcursor * sqlrcur,const char * command)2172 bool sqlrsh::openCache(sqlrshenv *env,
2173 sqlrcursor *sqlrcur, const char *command) {
2174
2175 // move to file name
2176 command=command+10;
2177
2178 // skip whitespace
2179 while (*command==' ') {
2180 command++;
2181 }
2182
2183 // bail if no file name was given
2184 if (!*command) {
2185 stderror.printf(" No file name given\n\n");
2186 return false;
2187 }
2188
2189 // if the file name starts with a slash then use it as-is, otherwise
2190 // prepend the default cache directory.
2191 stringbuffer fn;
2192 fn.append(sqlrpth->getCacheDir())->append(command);
2193
2194 // open the cached result set
2195 if (!sqlrcur->openCachedResultSet(fn.getString())) {
2196 stderror.printf(" Cannot open cache file\n\n");
2197 return false;
2198 }
2199
2200 // display the header
2201 displayHeader(sqlrcur,env);
2202
2203 // display the result set
2204 displayResultSet(sqlrcur,env);
2205
2206 // display statistics
2207 displayStats(sqlrcur,env);
2208
2209 return true;
2210 }
2211
displayHelp(sqlrshenv * env)2212 void sqlrsh::displayHelp(sqlrshenv *env) {
2213
2214 stdoutput.printf("\n");
2215 stdoutput.printf(" To run a query, simply type it at the prompt,\n"
2216 " followed by a semicolon. Queries may be \n"
2217 " split over multiple lines.\n\n");
2218 stdoutput.printf(" ping - ");
2219 stdoutput.printf("pings the database\n");
2220 stdoutput.printf(" identify - ");
2221 stdoutput.printf("returns the type of database\n");
2222 stdoutput.printf(" dbversion - ");
2223 stdoutput.printf("returns the version of the database\n");
2224 stdoutput.printf(" dbhostname - ");
2225 stdoutput.printf("returns the host name of the database\n");
2226 stdoutput.printf(" dbipaddress - ");
2227 stdoutput.printf("returns the ip address of the database\n");
2228 stdoutput.printf(" clientversion - ");
2229 stdoutput.printf("returns the version of the client library\n");
2230 stdoutput.printf(" serverversion - ");
2231 stdoutput.printf("returns the version of the server\n");
2232 stdoutput.printf(" use [database] - ");
2233 stdoutput.printf("change the current database/schema\n");
2234 stdoutput.printf(" currentdb - ");
2235 stdoutput.printf("shows the current database/schema\n");
2236 stdoutput.printf(" run script - ");
2237 stdoutput.printf("runs commands contained in file \"script\"\n");
2238 stdoutput.printf(" headers on|off - ");
2239 stdoutput.printf("toggles column descriptions before result set\n");
2240 stdoutput.printf(" divider on|off - ");
2241 stdoutput.printf("toggles the divider before the result set\n");
2242 stdoutput.printf(" stats on|off - ");
2243 stdoutput.printf("toggles statistics after result set\n");
2244 stdoutput.printf(" format plain|csv - ");
2245 stdoutput.printf("sets output format to plain or csv\n");
2246 stdoutput.printf(" debug on|off - ");
2247 stdoutput.printf("toggles debug messages\n");
2248 stdoutput.printf(" nullsasnulls on|off - ");
2249 stdoutput.printf("toggles getting nulls as nulls\n"
2250 " "
2251 "(rather than as empty strings)\n");
2252 stdoutput.printf(" autocommit on|off - ");
2253 stdoutput.printf("toggles autocommit\n");
2254 stdoutput.printf(" final on|off - ");
2255 stdoutput.printf("toggles use of one session per query\n");
2256 stdoutput.printf(" delimiter [character] - ");
2257 stdoutput.printf("sets delimiter character to [character]\n\n");
2258 stdoutput.printf(" response timeout [sec.msec] - ");
2259 stdoutput.printf("sets response timeout to [sec.msec]\n\n");
2260 stdoutput.printf(" inputbind ... - ");
2261 stdoutput.printf("defines an input bind variable\n");
2262 stdoutput.printf(" inputbind [variable] is null\n");
2263 stdoutput.printf(" inputbind [variable] = [stringvalue]\n");
2264 stdoutput.printf(" inputbind [variable] = [integervalue]\n");
2265 stdoutput.printf(" inputbind [variable] = [doublevalue]\n");
2266 stdoutput.printf(" inputbind [variable] = [MM/DD/YYYY HH:MM:SS:uS TZN]\n");
2267 stdoutput.printf(" inputbindblob [variable] = [value]\n");
2268 stdoutput.printf(" outputbind ... - ");
2269 stdoutput.printf("defines an output bind variable\n");
2270 stdoutput.printf(" outputbind [variable] string [length]\n");
2271 stdoutput.printf(" outputbind [variable] integer\n");
2272 stdoutput.printf(" outputbind [variable] double [precision] [scale}\n");
2273 stdoutput.printf(" outputbind [variable] date\n");
2274 stdoutput.printf(" printbinds - ");
2275 stdoutput.printf("prints all bind variables\n");
2276 stdoutput.printf(" clearinputbind [variable] - ");
2277 stdoutput.printf("clears an input bind variable\n");
2278 stdoutput.printf(" clearoutputbind [variable] - ");
2279 stdoutput.printf("clears an output bind variable\n");
2280 stdoutput.printf(" clearbinds - ");
2281 stdoutput.printf("clears all bind variables\n");
2282 stdoutput.printf(" reexecute - ");
2283 stdoutput.printf("reexecutes the previous query\n\n");
2284 stdoutput.printf(" lastinsertid - ");
2285 stdoutput.printf("returns the value of the most recently\n");
2286 stdoutput.printf("\t\t\t\t\t updated auto-increment or identity\n");
2287 stdoutput.printf("\t\t\t\t\t column, if the database supports it\n\n");
2288 stdoutput.printf(" show databases [like pattern] -\n");
2289 stdoutput.printf(" returns a list of known databases/schemas\n");
2290 stdoutput.printf(" show tables [like pattern] -\n");
2291 stdoutput.printf(" returns a list of known tables\n");
2292 stdoutput.printf(" show columns in table [like pattern] -\n");
2293 stdoutput.printf(" returns a list of column metadata for the table \"table\"\n");
2294 stdoutput.printf(" describe table -\n");
2295 stdoutput.printf(" returns a list of column metadata for the table \"table\"\n");
2296 stdoutput.printf(" fields table -\n");
2297 stdoutput.printf(" returns a list of column names for the table \"table\"\n\n");
2298 stdoutput.printf(" setclientinfo info - sets the client info\n");
2299 stdoutput.printf(" getclientinfo - displays the client info\n\n");
2300 stdoutput.printf(" setresultsetbuffersize size - fetch size rows at a time\n");
2301 stdoutput.printf(" getresultsetbuffersize - shows rows fetched at a time\n\n");
2302 stdoutput.printf(" endsession - ends the current session\n\n");
2303 stdoutput.printf(" cache [filename] [ttl] - caches the next result set to \"filename\"\n with ttl of \"ttl\"\n");
2304 stdoutput.printf(" opencache [filename] - opens and displays cached result set \n in \"filename\"\n\n");
2305 stdoutput.printf(" exit/quit - ");
2306 stdoutput.printf("exits\n\n");
2307 stdoutput.printf(" All commands must be followed by the delimiter: %c\n",
2308 env->delimiter);
2309 }
2310
startupMessage(sqlrshenv * env,const char * host,uint16_t port,const char * user)2311 void sqlrsh::startupMessage(sqlrshenv *env, const char *host,
2312 uint16_t port, const char *user) {
2313
2314 stdoutput.printf("%ssh - ",SQLR);
2315 stdoutput.printf("Version %s\n",SQLR_VERSION);
2316 stdoutput.printf(" Connected to: ");
2317 stdoutput.printf("%s:%d as %s\n\n",host,port,user);
2318 stdoutput.printf(" type help; for help.\n\n");
2319 }
2320
interactWithUser(sqlrconnection * sqlrcon,sqlrcursor * sqlrcur,sqlrshenv * env)2321 void sqlrsh::interactWithUser(sqlrconnection *sqlrcon, sqlrcursor *sqlrcur,
2322 sqlrshenv *env) {
2323
2324 // init some variables
2325 stringbuffer command;
2326 stringbuffer prmpt;
2327 bool exitprogram=false;
2328 uint32_t promptcount;
2329
2330 // Blocking mode is apparently not the default on some systems
2331 // (Syllable for sure, maybe others) and this causes hilariously
2332 // odd behavior when reading standard input.
2333 stdinput.useBlockingMode();
2334
2335 while (!exitprogram) {
2336
2337 // prompt the user
2338 promptcount=0;
2339
2340 // get the command
2341 bool done=false;
2342 while (!done) {
2343
2344 prmpt.append(promptcount);
2345 prmpt.append("> ");
2346 pr.setPrompt(prmpt.getString());
2347 prmpt.clear();
2348
2349 char *cmd=pr.read();
2350
2351 // cmd is NULL if you hit ctrl-D
2352 if (!cmd) {
2353 return;
2354 }
2355
2356 size_t len=charstring::length(cmd);
2357
2358 // len=0 and cmd="" if you just hit return
2359 if (len) {
2360 command.append(cmd);
2361 done=(cmd[len-1]==env->delimiter);
2362 }
2363
2364 if (!done) {
2365 promptcount++;
2366 command.append('\n');
2367 }
2368 }
2369
2370 char *cmd=command.detachString();
2371
2372 // run the command
2373 runCommands(sqlrcon,sqlrcur,env,cmd,&exitprogram);
2374
2375 // clean up
2376 delete[] cmd;
2377 }
2378 }
2379
execute(int argc,const char ** argv)2380 bool sqlrsh::execute(int argc, const char **argv) {
2381
2382 cmdline=new sqlrcmdline(argc,argv);
2383 sqlrpth=new sqlrpaths(cmdline);
2384 sqlrconfigs sqlrcfgs(sqlrpth);
2385
2386 // get command-line options
2387 const char *configurl=sqlrpth->getConfigUrl();
2388 const char *id=cmdline->getValue("id");
2389 const char *host=cmdline->getValue("host");
2390 uint16_t port=charstring::toInteger(
2391 (cmdline->found("port"))?
2392 cmdline->getValue("port"):DEFAULT_PORT);
2393 const char *socket=cmdline->getValue("socket");
2394 const char *user=cmdline->getValue("user");
2395 const char *password=cmdline->getValue("password");
2396 bool usekrb=cmdline->found("krb");
2397 const char *krbservice=cmdline->getValue("krbservice");
2398 const char *krbmech=cmdline->getValue("krbmech");
2399 const char *krbflags=cmdline->getValue("krbflags");
2400 bool usetls=cmdline->found("tls");
2401 const char *tlsversion=cmdline->getValue("tlsversion");
2402 const char *tlscert=cmdline->getValue("tlscert");
2403 const char *tlspassword=cmdline->getValue("tlspassword");
2404 const char *tlsciphers=cmdline->getValue("tlsciphers");
2405 const char *tlsvalidate="no";
2406 if (cmdline->found("tlsvalidate")) {
2407 tlsvalidate=cmdline->getValue("tlsvalidate");
2408 }
2409 const char *tlsca=cmdline->getValue("tlsca");
2410 uint16_t tlsdepth=charstring::toUnsignedInteger(
2411 cmdline->getValue("tlsdepth"));
2412 const char *localeargument=cmdline->getValue("locale");
2413 const char *script=cmdline->getValue("script");
2414 const char *command=cmdline->getValue("command");
2415
2416 // at least id, host, or socket is required
2417 if (charstring::isNullOrEmpty(id) &&
2418 charstring::isNullOrEmpty(host) &&
2419 charstring::isNullOrEmpty(socket)) {
2420
2421 stderror.printf("usage:\n"
2422 " %ssh -host host -port port -socket socket\n"
2423 " [-user user] [-password password]\n"
2424 " [-krb] [-krbservice svc] [-krbmech mech] "
2425 "[-krbflags flags]\n"
2426 " [-tls] [-tlsversion version]\n"
2427 " [-tlscert certfile] [-tlspassword password]\n"
2428 " [-tlsciphers cipherlist]\n"
2429 " [-tlsvalidate (no|ca|ca+domain|ca+host)] "
2430 "[-tlsca ca] [-tlsdepth depth]\n"
2431 " [-script script | -command command] [-quiet] "
2432 "[-format (plain|csv)] [-locale (env|name)] "
2433 "[-getasnumber] [-noelapsed] [-nextresultset]\n"
2434 " [-resultsetbuffersize rows]\n"
2435 " or\n"
2436 " %ssh [-config config] -id id\n"
2437 " [-script script | -command command] [-quiet] "
2438 "[-format (plain|csv)] [-locale (env|name)] "
2439 "[-getasnumber] [-noelapsed] [-nextresultset]\n"
2440 " [-resultsetbuffersize rows]\n",
2441 SQLR,SQLR);
2442 process::exit(1);
2443 }
2444
2445 // if an id was specified, then get various values from the config file
2446 if (!charstring::isNullOrEmpty(id)) {
2447 sqlrconfig *cfg=sqlrcfgs.load(configurl,id);
2448 if (cfg) {
2449 if (!cmdline->found("host")) {
2450 host="localhost";
2451 }
2452 if (!cmdline->found("port")) {
2453 port=cfg->getDefaultPort();
2454 }
2455 if (!cmdline->found("socket")) {
2456 socket=cfg->getDefaultSocket();
2457 }
2458 if (!cmdline->found("krb")) {
2459 usekrb=cfg->getDefaultKrb();
2460 }
2461 if (!cmdline->found("krbservice")) {
2462 krbservice=cfg->getDefaultKrbService();
2463 }
2464 if (!cmdline->found("krbmech")) {
2465 krbmech=cfg->getDefaultKrbMech();
2466 }
2467 if (!cmdline->found("krbflags")) {
2468 krbflags=cfg->getDefaultKrbFlags();
2469 }
2470 if (!cmdline->found("tls")) {
2471 usetls=cfg->getDefaultTls();
2472 }
2473 if (!cmdline->getValue("tlsciphers")) {
2474 tlsciphers=cfg->getDefaultTlsCiphers();
2475 }
2476 if (!cmdline->found("user")) {
2477 user=cfg->getDefaultUser();
2478 password=cfg->getDefaultPassword();
2479 }
2480 }
2481 }
2482
2483 if (!charstring::isNullOrEmpty(localeargument)) {
2484 // This is useful for making sure that decimals still work
2485 // when the locale is changed to say, de_DE that has different
2486 // number formats.
2487 char *localeresult=setlocale(LC_ALL,
2488 (!charstring::compare(localeargument,"env"))?
2489 "":localeargument);
2490 if (!localeresult) {
2491 stderror.printf("ERROR: setlocale failed\n");
2492 return false;
2493 }
2494 }
2495
2496 // configure sql relay connection
2497 sqlrconnection sqlrcon(host,port,socket,user,password,0,1);
2498 sqlrcursor sqlrcur(&sqlrcon);
2499
2500 // configure kerberos/tls
2501 if (usekrb) {
2502 sqlrcon.enableKerberos(krbservice,krbmech,krbflags);
2503 } else if (usetls) {
2504 sqlrcon.enableTls(tlsversion,tlscert,tlspassword,tlsciphers,
2505 tlsvalidate,tlsca,tlsdepth);
2506 }
2507
2508 // set up an sqlrshenv
2509 sqlrshenv env;
2510
2511 // handle quiet flag
2512 if (cmdline->found("quiet")) {
2513 env.headers=false;
2514 env.stats=false;
2515 }
2516
2517 // handle the result set format
2518 if (!charstring::compare(cmdline->getValue("format"),"csv")) {
2519 env.format=SQLRSH_FORMAT_CSV;
2520 }
2521
2522 // handle the result set buffer size
2523 if (cmdline->found("resultsetbuffersize")) {
2524 env.rsbs=charstring::toInteger(
2525 cmdline->getValue("resultsetbuffersize"));
2526 }
2527
2528 // FIXME: make these commands instead of commandline args
2529 env.getasnumber=cmdline->found("getasnumber");
2530 env.noelapsed=cmdline->found("noelapsed");
2531 env.nextresultset=cmdline->found("nextresultset");
2532
2533 // process RC files
2534 userRcFile(&sqlrcon,&sqlrcur,&env);
2535
2536
2537 // handle the history file
2538 const char *home=environment::getValue("HOME");
2539 if (!charstring::isNullOrEmpty(home)) {
2540 char *filename=new char[charstring::length(home)+16+1];
2541 charstring::copy(filename,home);
2542 charstring::append(filename,"/.sqlrsh_history");
2543 pr.setHistoryFile(filename);
2544 pr.setMaxHistoryLines(100);
2545 }
2546
2547 bool retval=true;
2548
2549 if (!charstring::isNullOrEmpty(script)) {
2550 // if a script was specified, run it
2551 retval=runScript(&sqlrcon,&sqlrcur,&env,script,true);
2552 } else if (!charstring::isNullOrEmpty(command)) {
2553 // if a command was specified, run it
2554 retval=runCommands(&sqlrcon,&sqlrcur,&env,command,NULL);
2555 } else {
2556 // otherwise go into interactive mode
2557 startupMessage(&env,host,port,user);
2558 interactWithUser(&sqlrcon,&sqlrcur,&env);
2559 }
2560
2561 // clean up
2562 pr.flushHistory();
2563
2564 return retval;
2565 }
2566
helpmessage(const char * progname)2567 static void helpmessage(const char *progname) {
2568 stdoutput.printf(
2569 "%s is the %s command line database shell.\n"
2570 "\n"
2571 "It can be used interactively, or non-interactively to run queries directly from the command line, or scripts containing queries.\n"
2572 "\n"
2573 "Usage: %s [OPTIONS]\n"
2574 "\n"
2575 "Options:\n"
2576 "\n"
2577 CONNECTIONOPTIONS
2578 "\n"
2579 "Command options:\n"
2580 " -script filename Run the specified script which contains commands\n"
2581 " or queries that could otherwise be run at the\n"
2582 " %s prompt.\n"
2583 "\n"
2584 " -command \"commands\" Run the provided string which contains commands\n"
2585 " or queries that could otherwise be run at the\n"
2586 " %s prompt.\n"
2587 "\n"
2588 " -quiet Omit headers and stats in output.\n"
2589 "\n"
2590 " -format plain|csv Format the output as specified.\n"
2591 " Defaults to plain.\n"
2592 "\n"
2593 " -locale env|locale_name calls setlocale(LC_ALL, locale_name).\n"
2594 " env means use LC variables.\n"
2595 "\n"
2596 " -getasnumber calls getFieldAs(Integer|Double) as appropriate\n"
2597 "\n"
2598 " -noelapsed do not print elapsed time\n"
2599 "\n"
2600 " -nextresultset attempt to fetch multiple resultsets\n"
2601 "\n"
2602 " -resultsetbuffersize rows\n"
2603 " Fetch result sets using the specified number of\n"
2604 " rows at once.\n"
2605 "\n"
2606 "Examples:\n"
2607 "\n"
2608 "Interactive session with server at svr:9000 as usr/pwd.\n"
2609 "\n"
2610 " %s -host svr -port 9000 -user usr -password pwd\n"
2611 "\n"
2612 "Interactive session with local server on socket /tmp/svr.sock as usr/pwd.\n"
2613 "\n"
2614 " %s -socket /tmp/svr.sock -user usr -password pwd\n"
2615 "\n"
2616 "Interactive session using connection info and credentials from an instance\n"
2617 "defined in the default configuration.\n"
2618 " %s -id myinst\n"
2619 "\n"
2620 "Interactive session using connection info and credentials from an instance\n"
2621 "defined in the config file ./myconfig.conf\n"
2622 "\n"
2623 " %s -config ./myconfig.conf -id myinst\n"
2624 "\n"
2625 "Non-interactive session, running commands from ./script.sql\n"
2626 "\n"
2627 " %s -id myinst -script ./script.sql\n"
2628 "\n"
2629 "Non-interactive session, running query \"select * from mytable\" with csv output.\n"
2630 "\n"
2631 " %s -id myinst -command \"select * from mytable\" -quiet -format csv\n"
2632 "\n",
2633 progname,SQL_RELAY,progname,progname,progname,progname,
2634 progname,progname,progname,progname,progname);
2635 }
2636
main(int argc,const char ** argv)2637 int main(int argc, const char **argv) {
2638
2639 version(argc,argv);
2640 help(argc,argv);
2641
2642 #ifdef SIGPIPE
2643 // ignore SIGPIPE
2644 signalset set;
2645 set.removeAllSignals();
2646 set.addSignal(SIGPIPE);
2647 signalmanager::ignoreSignals(&set);
2648 #endif
2649
2650 int32_t exitcode=0;
2651 {
2652 sqlrsh s;
2653 exitcode=!s.execute(argc,argv);
2654 }
2655 process::exit(exitcode);
2656 }
2657