// Copyright (c) 1999-2001 David Muse // See the file COPYING for more information #include #include #include #include #include #include #include // for clock() #include // for printf, fflush #include #ifdef RUDIMENTS_NAMESPACE using namespace rudiments; #endif #ifdef HAVE_READLINE #include // This is an interesting story... // readline 2's include files don't list any parameters for any of // the functions. This is fine for C, but not C++, at least not with // the compiler I'm using, even with the extern "C" {} bit. // This is fixed in readline 4, but, to maintain compatibility with // readline 2, I define the functions myself. extern "C" { extern char *readline(char *prompt); extern void add_history(char *line); extern void read_history(char *file); extern void write_history(char *file); extern void history_truncate_file(char *file, int line); } #endif class environment { public: environment(); bool color; bool headers; bool stats; bool debug; bool final; char delimiter; }; environment::environment() { color=false; headers=true; stats=true; debug=false; final=false; delimiter=';'; } class sqlrsh { public: #ifndef HAVE_READLINE sqlrsh(); #endif void execute(int argc, const char **argv); private: void startupMessage(environment *env, const char *host, uint16_t port, const char *user); void systemRcFile(sqlrconnection *sqlrcon, sqlrcursor *sqlrcur, environment *env); void userRcFile(sqlrconnection *sqlrcon, sqlrcursor *sqlrcur, environment *env); void runScript(sqlrconnection *sqlrcon, sqlrcursor *sqlrcur, environment *env, const char *filename, bool returnerror, bool displaycommand); bool getCommandFromFile(file *fl, stringbuffer *cmdbuffer, environment *env); bool runCommand(sqlrconnection *sqlrcon, sqlrcursor *sqlrcur, environment *env, const char *command); int commandType(const char *command); void internalCommand(sqlrconnection *sqlrcon, sqlrcursor *sqlrcur, environment *env, const char *command); void externalCommand(sqlrconnection *sqlrcon, sqlrcursor *sqlrcur, environment *env, const char *command); void initStats(environment *env); void displayError(sqlrcursor *sqlrcur, environment *env); void displayHeader(sqlrcursor *sqlrcur, environment *env); void displayResultSet(sqlrcursor *sqlrcur, environment *env); void displayStats(sqlrcursor *sqlrcur, environment *env); void ping(sqlrconnection *sqlrcon, environment *env); void identify(sqlrconnection *sqlrcon, environment *env); void displayHelp(environment *env); void interactWithUser(sqlrconnection *sqlrcon, sqlrcursor *sqlrcur, environment *env); void prompt(unsigned long promptcount); void error(const char *errstring); void setColor(environment *env, int value); void black(environment *env); void red(environment *env); void green(environment *env); void yellow(environment *env); void blue(environment *env); void magenta(environment *env); void cyan(environment *env); void white(environment *env); #ifndef HAVE_READLINE filedescriptor standardin; #endif }; #ifndef HAVE_READLINE sqlrsh::sqlrsh() { standardin.setFileDescriptor(0); standardin.allowShortReads(); } #endif void sqlrsh::systemRcFile(sqlrconnection *sqlrcon, sqlrcursor *sqlrcur, environment *env) { runScript(sqlrcon,sqlrcur,env,SYSTEM_SQLRSHRC,false,false); } void sqlrsh::userRcFile(sqlrconnection *sqlrcon, sqlrcursor *sqlrcur, environment *env) { // get user's home directory char *home=getenv("HOME"); if (!home) { home="~"; } // build rcfilename size_t userrcfilelen=charstring::length(home)+10+1; char *userrcfile=new char[userrcfilelen]; snprintf(userrcfile,userrcfilelen,"%s/.sqlrshrc",home); // process the file runScript(sqlrcon,sqlrcur,env,userrcfile,false,false); delete[] userrcfile; } void sqlrsh::runScript(sqlrconnection *sqlrcon, sqlrcursor *sqlrcur, environment *env, const char *filename, bool returnerror, bool displaycommand) { char *trimmedfilename=charstring::duplicate(filename); charstring::bothTrim(trimmedfilename); // open the file file scriptfile; if (scriptfile.open(trimmedfilename,O_RDONLY)) { for (;;) { // get a command stringbuffer command; if (!getCommandFromFile(&scriptfile,&command,env)) { break; } if (displaycommand) { cyan(env); printf("%s\n",command.getString()); white(env); } // run the command runCommand(sqlrcon,sqlrcur,env,command.getString()); } // close the file scriptfile.close(); } else { // error message if (returnerror) { stringbuffer errmesg; errmesg.append("Couldn't open file: "); errmesg.append(trimmedfilename); error(errmesg.getString()); } } delete[] trimmedfilename; } bool sqlrsh::getCommandFromFile(file *fl, stringbuffer *cmdbuffer, environment *env) { char character; for (;;) { // get a character from the file if (fl->read(&character)!=sizeof(character)) { return false; } // look for an escape character if (character=='\\') { if (fl->read(&character)!=sizeof(character)) { return false; } cmdbuffer->append(character); if (fl->read(&character)!=sizeof(character)) { return false; } } // look for an end of command delimiter if (character==env->delimiter) { return true; } // write character to buffer and move on cmdbuffer->append(character); } } bool sqlrsh::runCommand(sqlrconnection *sqlrcon, sqlrcursor *sqlrcur, environment *env, const char *command) { int cmdtype=commandType(command); if (cmdtype>0) { // if the command an internal command, run it as one internalCommand(sqlrcon,sqlrcur,env,command); return true; } else if (cmdtype==0) { // if the command is not an internal command, // execute it as a query and display the result set externalCommand(sqlrcon,sqlrcur,env,command); return true; } else { return false; } } int sqlrsh::commandType(const char *command) { // skip white space char *ptr=(char *)command; while (*ptr==' ' || *ptr==' ' || *ptr=='\n') { ptr++; } // compare to known internal commands if (!charstring::compareIgnoringCase(ptr,"color",5) || !charstring::compareIgnoringCase(ptr,"headers",7) || !charstring::compareIgnoringCase(ptr,"stats",5) || !charstring::compareIgnoringCase(ptr,"debug",5) || !charstring::compareIgnoringCase(ptr,"final",5) || !charstring::compareIgnoringCase(ptr,"help",4) || !charstring::compareIgnoringCase(ptr,"ping",4) || !charstring::compareIgnoringCase(ptr,"identify",8) || !charstring::compareIgnoringCase(ptr,"run",3) || !charstring::compareIgnoringCase(ptr,"@",1) || !charstring::compareIgnoringCase(ptr,"delimiter",9) || !charstring::compareIgnoringCase(ptr,"delimeter",9)) { // return value of 1 is internal command return 1; } // look for an exit command if (!charstring::compareIgnoringCase(ptr,"quit",4) || !charstring::compareIgnoringCase(ptr,"exit",4)) { return -1; } // return value of 0 is external command return 0; } void sqlrsh::internalCommand(sqlrconnection *sqlrcon, sqlrcursor *sqlrcur, environment *env, const char *command) { // skip white space char *ptr=(char *)command; while (*ptr==' ' || *ptr==' ' || *ptr=='\n') { ptr++; } // compare to known internal commands int cmdtype=0; if (!charstring::compareIgnoringCase(ptr,"color",5)) { ptr=ptr+5; cmdtype=1; } else if (!charstring::compareIgnoringCase(ptr,"headers",7)) { ptr=ptr+7; cmdtype=2; } else if (!charstring::compareIgnoringCase(ptr,"stats",5)) { ptr=ptr+5; cmdtype=3; } else if (!charstring::compareIgnoringCase(ptr,"debug",5)) { ptr=ptr+5; cmdtype=4; } else if (!charstring::compareIgnoringCase(ptr,"final",5)) { ptr=ptr+5; cmdtype=5; } else if (!charstring::compareIgnoringCase(ptr,"help",4)) { displayHelp(env); return; } else if (!charstring::compareIgnoringCase(ptr,"ping",4)) { ping(sqlrcon,env); return; } else if (!charstring::compareIgnoringCase(ptr,"run",3)) { ptr=ptr+3; cmdtype=6; } else if (!charstring::compareIgnoringCase(ptr,"@",1)) { ptr=ptr+1; cmdtype=6; } else if (!charstring::compareIgnoringCase(ptr,"delimiter",9) || !charstring::compareIgnoringCase(ptr,"delimeter",9)) { ptr=ptr+9; cmdtype=7; } else if (!charstring::compareIgnoringCase(ptr,"identify",8)) { identify(sqlrcon,env); return; } else { return; } // skip white space while (*ptr==' ' || *ptr==' ' || *ptr=='\n') { ptr++; } // handle scripts if (cmdtype==6) { runScript(sqlrcon,sqlrcur,env,ptr,true,false); return; } // on or off? bool toggle=false; if (!charstring::compareIgnoringCase(ptr,"on",2)) { toggle=true; } // set parameter if (cmdtype==1) { env->color=toggle; } else if (cmdtype==2) { env->headers=toggle; } else if (cmdtype==3) { env->stats=toggle; } else if (cmdtype==4) { env->debug=toggle; } else if (cmdtype==5) { env->final=toggle; } else if (cmdtype==7) { env->delimiter=ptr[0]; cyan(env); printf("Delimiter set to %c\n",env->delimiter); white(env); } } void sqlrsh::externalCommand(sqlrconnection *sqlrcon, sqlrcursor *sqlrcur, environment *env, const char *command) { // init stats initStats(env); // handle debug if (env->debug) { sqlrcon->debugOn(); } // handle a commit/rollback if (!charstring::compareIgnoringCase(command,"commit",6)) { sqlrcon->commit(); } else if (!charstring::compareIgnoringCase(command,"rollback",8)) { sqlrcon->rollback(); } else { // send the query sqlrcur->setResultSetBufferSize(100); sqlrcur->sendQuery(command); // look for an error if (sqlrcur->errorMessage()) { // display the error displayError(sqlrcur,env); } else { // display the header displayHeader(sqlrcur,env); // display the result set displayResultSet(sqlrcur,env); } if (env->final) { sqlrcon->endSession(); } } // set debug back off sqlrcon->debugOff(); // display statistics displayStats(sqlrcur,env); } void sqlrsh::initStats(environment *env) { if (!env->stats) { return; } // call clock here or something clock(); } void sqlrsh::displayError(sqlrcursor *sqlrcur, environment *env) { cyan(env); printf("%s\n\n",sqlrcur->errorMessage()); white(env); } void sqlrsh::displayHeader(sqlrcursor *sqlrcur, environment *env) { if (!env->headers) { return; } // display column names uint32_t charcount=0; uint32_t colcount=sqlrcur->colCount(); const char *name; uint32_t namelen; uint32_t longest; if (!colcount) { return; } // iterate through columns for (uint32_t i=0; icolCount(); i++) { // write the column name if (i%2==1) { green(env); } else { yellow(env); } name=sqlrcur->getColumnName(i); printf("%s",name); white(env); // which is longer, field name or longest field namelen=charstring::length(name); longest=sqlrcur->getLongest(i); if (namelen>longest) { longest=namelen; } charcount=charcount+longest; // pad after the name with spaces for (uint32_t j=namelen; jcolCount(); uint32_t namelen; uint32_t longest; if (!colcount) { return; } uint32_t i=0; const char *field=""; while (field && colcount) { for (uint32_t j=0; jgetField(i,j))) { break; } // write the column value if (i%2==1) { cyan(env); } else { white(env); } printf("%s",field); white(env); // which is longer, field name or longest field longest=sqlrcur->getLongest(j); if (env->headers) { namelen=charstring::length( sqlrcur->getColumnName(j)); if (namelen>longest) { longest=namelen; } } // pad after the name with spaces for (uint32_t k=sqlrcur->getFieldLength(i,j); kstats) { return; } // call clock again, display results red(env); printf(" Rows Returned : "); magenta(env); printf("%lld\n",sqlrcur->rowCount()); red(env); printf(" Fields Returned : "); magenta(env); printf("%lld\n",sqlrcur->rowCount()*sqlrcur->colCount()); red(env); printf(" System time : "); magenta(env); printf("%ld\n",clock()); white(env); printf("\n"); } void sqlrsh::ping(sqlrconnection *sqlrcon, environment *env) { red(env); printf((sqlrcon->ping())?" The database is up.\n": " The database is down.\n"); white(env); } void sqlrsh::identify(sqlrconnection *sqlrcon, environment *env) { red(env); printf("%s\n",sqlrcon->identify()); white(env); } void sqlrsh::displayHelp(environment *env) { printf("\n"); yellow(env); printf(" To run a query, simply type it at the prompt,\n" " followed by a semicolon. Queries may be \n" " split over multiple lines.\n\n"); cyan(env); printf(" ping - "); green(env); printf("pings the database\n"); cyan(env); printf(" identify - "); green(env); printf("returns the type of database\n"); cyan(env); printf(" run script - "); green(env); printf("runs commands contained in file \"script\"\n"); cyan(env); printf(" color on/off - "); green(env); printf("toggles colorizing\n"); cyan(env); printf(" headers on/off - "); green(env); printf("toggles column descriptions before result set\n"); cyan(env); printf(" stats on/off - "); green(env); printf("toggles statistics after result set\n"); cyan(env); printf(" debug on/off - "); green(env); printf("toggles debug messages\n"); cyan(env); printf(" final on/off - "); green(env); printf("toggles use of one session per query\n"); cyan(env); printf(" delimiter [character] - "); green(env); printf("sets delimiter character to [character]\n"); cyan(env); printf(" exit/quit - "); green(env); printf("exits\n\n"); yellow(env); printf(" All commands must be followed by a semicolon.\n"); white(env); } void sqlrsh::startupMessage(environment *env, const char *host, uint16_t port, const char *user) { red(env); printf("SQLRShell - "); green(env); printf("Version 0.22\n"); yellow(env); printf(" Connected to: "); blue(env); printf("%s:%d as %s\n\n",host,port,user); yellow(env); printf(" type help; for a help.\n\n"); white(env); } void sqlrsh::interactWithUser(sqlrconnection *sqlrcon, sqlrcursor *sqlrcur, environment *env) { // init some variables stringbuffer command; stringbuffer prmpt; int exitprogram=0; uint32_t promptcount; while (!exitprogram) { // prompt the user promptcount=0; // get the command bool done=false; while (!done) { #ifdef HAVE_READLINE prmpt.append(promptcount); prmpt.append("> "); char *cmd=readline(const_cast( prmpt.getString())); prmpt.clear(); if (cmd && cmd[0]) { charstring::rightTrim(cmd); add_history(cmd); } else { printf("\n"); } #else prompt(promptcount); char cmd[1024]; ssize_t bytes=standardin.read(cmd,1024); cmd[bytes-1]=(char)NULL; #endif size_t len=charstring::length(cmd); done=false; for (size_t i=0; idelimiter) { done=true; } else { command.append(cmd[i]); } } else if (cmd[i]>=32 || cmd[i]==' ') { command.append(cmd[i]); } } if (!done) { promptcount++; command.append(" "); } #ifdef HAVE_READLINE delete[] cmd; #endif } // run the command if (!runCommand(sqlrcon,sqlrcur,env,command.getString())) { exitprogram=1; } command.clear(); } } void sqlrsh::prompt(unsigned long promptcount) { printf("%ld> ",promptcount); fflush(stdout); } void sqlrsh::error(const char *errstring) { // print the error printf("%s\n\n",errstring); } void sqlrsh::execute(int argc, const char **argv) { commandline cmdline(argc,argv); sqlrconfigfile cfgfile; usercontainer *currentnode=NULL; const char *host; uint16_t port; const char *socket; const char *user; const char *password; const char *script=NULL; const char *config=cmdline.getValue("-config"); if (!(config && config[0])) { config=DEFAULT_CONFIG_FILE; } const char *id=cmdline.getValue("-id"); if (!(id && id[0])) { if (argc<6) { printf("usage: sqlrsh host port socket " "user password [script]\n" " or sqlrsh [-config configfile] " "-id id [script]\n"); exit(1); } host=argv[1]; port=charstring::toInteger(argv[2]); socket=argv[3]; user=argv[4]; password=argv[5]; if (argv[6]) { script=argv[6]; } } else { if (cfgfile.parse(config,id)) { // get the host/port/socket/username/password host="localhost"; port=cfgfile.getPort(); socket=cfgfile.getUnixPort(); // FIXME: this can return 0 cfgfile.getUserList()->getDataByIndex(0,¤tnode); user=currentnode->getUser(); password=currentnode->getPassword(); } else { return; } // find the script if there is one for (int i=1; icolor) { printf("\033[0;%dm",value); } } void sqlrsh::black(environment *env) { setColor(env,30); } void sqlrsh::red(environment *env) { setColor(env,31); } void sqlrsh::green(environment *env) { setColor(env,32); } void sqlrsh::yellow(environment *env) { setColor(env,33); } void sqlrsh::blue(environment *env) { setColor(env,34); } void sqlrsh::magenta(environment *env) { setColor(env,35); } void sqlrsh::cyan(environment *env) { setColor(env,36); } void sqlrsh::white(environment *env) { setColor(env,37); } int main(int argc, const char **argv) { #include sqlrsh s; s.execute(argc,argv); exit(0); }