📄 isql++.cpp.svn-base
字号:
/* This file is part of libodbc++. Copyright (C) 1999-2000 Manush Dodunekov <manush@stendahls.net> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/#include "isql++.h"#include <cstdlib>#include <cstdio>#include <iostream>extern "C" {#if defined(ODBCXX_DISABLE_READLINE_HACK)#include <readline/readline.h>#include <readline/history.h>#else /* readline.h doesn't contain proper function prototypes, which makes newer gcc versions (>=2.95) barf with certain flags. This could help the situation. */ extern char* rl_readline_name; typedef char** (*CPPFunction)(char*,char*); extern CPPFunction rl_completion_entry_function; extern int rl_initialize(void); extern char* readline(const char*); extern void add_history(const char*);#ifdef SPACE#undef SPACE#endif#define SPACE ' '#endif}using namespace odbc;using namespace std;const char* SQLPROMPT1="SQL> ";const char* SQLPROMPT2=" +> ";const char* BLOB_FIELD="<BLOB>";const char* NULL_FIELD="<NULL>";const char* INNER_SEPARATOR=" ";const char* OUTER_SEPARATOR=" ";const char SPACE_CHAR=' ';const char LINE_CHAR='=';const char END_OF_STATEMENT=';';const char* WS=" \r\n\t";const int LONGVARCHAR_WIDTH=20;const int MIN_COL_WIDTH_ON_SCREEN=4;// this effectively disables filename completion// with readlinestatic char **noCompletion (char *,char*){ // no completion of filenames return (char**)NULL;}static const char* getTypeName(int sqlType) { static struct { int id; const char* name; } sqlTypes[] = { { Types::BIGINT, "BIGINT" }, { Types::BINARY, "BINARY" }, { Types::BIT, "BIT" }, { Types::CHAR, "CHAR" }, { Types::DATE, "DATE" }, { Types::DECIMAL, "DECIMAL" }, { Types::DOUBLE, "DOUBLE" }, { Types::FLOAT, "FLOAT" }, { Types::INTEGER, "INTEGER" }, { Types::LONGVARBINARY, "LONGVARBINARY" }, { Types::LONGVARCHAR, "LONGVARCHAR" }, { Types::NUMERIC, "NUMERIC" }, { Types::REAL, "REAL" }, { Types::SMALLINT, "SMALLINT" }, { Types::TIME, "TIME" }, { Types::TIMESTAMP, "TIMESTAMP" }, { Types::TINYINT, "TINYINT" }, { Types::VARBINARY, "VARBINARY" }, { Types::VARCHAR, "VARCHAR" }, {0, NULL } }; for(unsigned int i=0; sqlTypes[i].name!=NULL; i++) { if(sqlTypes[i].id==sqlType) { return sqlTypes[i].name; } } return "UNKNOWN";}// split string on any of scharsinline vector<string> splitString(const string& str, const char* schars){ vector<string> res; if(str.length()==0) { return res; } if(strlen(schars)==0) { res.push_back(str); return res; } string::size_type e=0; string::size_type s=str.find_first_not_of(schars); while(s!=string::npos) { e=str.find_first_of(schars,s); if(e==string::npos) { res.push_back(str.substr(s)); } else { res.push_back(str.substr(s,e-s)); } s=str.find_first_not_of(schars,e); } for(vector<string>::iterator i=res.begin(); i!=res.end(); i++) { } return res;}// lowercase a stringinline string toLowerCase(const string& str) { string ret; if(str.length()>0) { ret.resize(str.length()); for(int i=0; i<str.length(); i++) { ret[i]=tolower(str[i]); } } return ret;}inline string toUpperCase(const string& str) { string ret; if(str.length()>0) { ret.resize(str.length()); for(int i=0; i<str.length(); i++) { ret[i]=toupper(str[i]); } } return ret;}// split a fully qualified sql table or procedure// identifier into catalog,schema and name// returns a vector of all three of themvoid Isql::splitIdentifier(const string& id, string& catalog, string& schema, string& name){ vector<string> ret; DatabaseMetaData* md=con_->getMetaData(); string str=id; //local copy, we are going to modify it if(supportsCatalogs_) { string catSep=md->getCatalogSeparator(); string::size_type catStart, catEnd; if(md->isCatalogAtStart()) { catStart=0; catEnd=str.find(catSep); if(catEnd!=string::npos) { catalog=str.substr(0,catEnd); str=str.substr(catEnd+catSep.length()); } } else { catStart=str.rfind(catSep); if(catStart!=string::npos) { catalog=str.substr(catStart+1); str=str.substr(0,catStart); } } } else { catalog=""; } // check for schemas if(supportsSchemas_) { string schemaSep="."; string::size_type schemaEnd=str.find(schemaSep); if(schemaEnd!=string::npos) { schema=str.substr(0,schemaEnd); str=str.substr(schemaEnd+schemaSep.length()); } } else { schema=""; } name=str; //now, check if we are to perform some case-transforms on this //since some drivers can't refer to table test using TEST //when metadata information is requested if(md->storesLowerCaseIdentifiers()) { name=toLowerCase(name); schema=toLowerCase(schema); catalog=toLowerCase(catalog); } else if(md->storesUpperCaseIdentifiers()) { name=toUpperCase(name); schema=toUpperCase(schema); catalog=toUpperCase(catalog); } //otherwise, we don't touch them}string Isql::makeIdentifier(const string& cat, const string& schema, const string& name){ string id=name; DatabaseMetaData* md=con_->getMetaData(); if(supportsSchemas_ && schema.length()>0) { string schemaSep="."; id=schema+schemaSep+id; } if(supportsCatalogs_ && cat.length()>0) { string catSep=md->getCatalogSeparator(); if(md->isCatalogAtStart()) { id=cat+catSep+id; } else { id+=(catSep+cat); } } return id;}Isql::Isql(Connection* con) :con_(con),termWidth_(80), maxRows_(0){ rl_readline_name="isqlxx"; rl_completion_entry_function=(CPPFunction)noCompletion; rl_initialize(); commands_["set"]=&Isql::setCmd; commands_["show"]=&Isql::showCmd; commands_["commit"]=&Isql::commitCmd; commands_["rollback"]=&Isql::rollbackCmd; commands_["describe"]=&Isql::describeCmd; // a cheap way to check if this supports schemas try { ResultSet* rs=con_->getMetaData()->getSchemas(); Deleter<ResultSet> _rs(rs); supportsSchemas_=rs->next(); } catch(SQLException& e) { supportsSchemas_=true; } // another cheap trick try { ResultSet* rs=con_->getMetaData()->getCatalogs(); Deleter<ResultSet> _rs(rs); supportsCatalogs_=rs->next(); } catch(SQLException& e) { supportsCatalogs_=true; }}Isql::~Isql(){ //safety if(con_->getMetaData()->supportsTransactions()) { con_->rollback(); }}bool Isql::readInput(string& out){ const char* prompt=(buffer_.length()>0?SQLPROMPT2:SQLPROMPT1); char* s=readline(prompt); if(s!=NULL) { if(s[0]!=0) { add_history(s); out=s; } else { out = ""; } //free it (it is malloced) free(s); return true; } else { return false; }}void Isql::run(){ string input; while(this->readInput(input)) { buffer_=buffer_+(buffer_.length()>0?"\n"+input:input); string s; StatementType st=this->extractStatement(s); switch(st) { case STATEMENT_SQL: try { if(s.length()>1 && s[0]=='"' && s[s.length()-1]=='"') { //the whole thing is quoted, strip the quotes s=s.substr(1,s.length()-2); } PreparedStatement* pstmt=con_->prepareStatement(s); Deleter<PreparedStatement> _pstmt(pstmt); //apply maxrows if needed if(maxRows_>0) { pstmt->setMaxRows(maxRows_); } this->execute(pstmt); } catch(SQLException& e) { cout << e.getMessage() << endl; } break; case STATEMENT_COMMAND: try { this->executeCommand(s); } catch(exception& e) { cout << e.what() << endl; } break; case STATEMENT_NONE: break; } }}void Isql::execute(PreparedStatement* pstmt){ if(pstmt->execute()) { ResultSet* rs=pstmt->getResultSet(); Deleter<ResultSet> _rs(rs); this->printResultSet(rs); } try { int ar=pstmt->getUpdateCount(); cout << OUTER_SEPARATOR << ar << " rows affected" << flush; } catch(SQLException& e) {} //ignore it cout << endl;}void Isql::printResultSet(ResultSet* rs){ int cnt=0; ResultSetMetaData* md=rs->getMetaData(); int totalWidth=0, shrinkableWidth=0; vector<int> widths; for(int i=1; i<=md->getColumnCount(); i++) { int l; switch(md->getColumnType(i)) { case Types::LONGVARCHAR: l=LONGVARCHAR_WIDTH; break; case Types::LONGVARBINARY: l=strlen(BLOB_FIELD); break; default: l=md->getColumnDisplaySize(i); break; } widths.push_back(l); totalWidth+=l; if ( i > 1 ) totalWidth += strlen(INNER_SEPARATOR); } totalWidth += 2 * strlen(OUTER_SEPARATOR); if(totalWidth>(int)termWidth_) { //we'll need to shrink some column widths //we only do that on char and varchar columns for(int i=1; i<=md->getColumnCount(); i++) { switch(md->getColumnType(i)) { case Types::CHAR: case Types::VARCHAR: shrinkableWidth+=md->getColumnDisplaySize(i); break; default: break; } } if(shrinkableWidth>0) { //ok, now try shrinking float shrinkFactor = (1.0 - ((float)(totalWidth - (int)termWidth_)) / (float)shrinkableWidth);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -