00001 // $Id: sqldb.h,v 1.16 2003/02/13 19:36:09 dvermeir Exp $ $Name: $ 00002 00003 #ifndef DV_SQLDB_H 00004 #define DV_SQLDB_H 00005 00006 #include <string> 00007 #include <sstream> 00008 #include <iostream> 00009 #include <vector> 00010 #include <stdexcept> 00011 #include <iterator> 00012 00013 #include <dvutil/date.h> 00014 #include <dvutil/props.h> 00015 #include <dvmysql/vector_string_input_iterator.h> 00016 00017 /*! \mainpage 00018 \author dvermeir@vub.ac.be 00019 00020 <a href="../download/">Download</a> 00021 00022 \section installation Installation 00023 00024 Standard. See the INSTALL file in the top directory. 00025 00026 Dependencies: 00027 <ul> 00028 <li> <a href="http://tinf2.vub.ac.be/~dvermeir/software/dv/index.html">dvutil</a> 00029 <li> <a href="http://mysql.com/">mysql </a> 00030 </ul> 00031 */ 00032 00033 /*! \file 00034 This file describes the dbms-independent classes: 00035 00036 <ul> 00037 <li> Dv::Sql::Exception: thrown upon failure of some operations. 00038 <li> Dv::Sql::Field: describes a field in a table or query result. 00039 <li> Dv::Sql::Db: an open database connection. 00040 <li> Dv::Sql::Command: an sql command (query, update). 00041 <li> Dv::Sql::Command::iterator: an iterator for the result of a 00042 query. 00043 <li> Dv::Sql::Command_: an implementation class that must be subclassed 00044 for each particular DBMS. 00045 </ul> 00046 */ 00047 00048 //! The whole package lives in this namespace. 00049 namespace Dv { 00050 00051 //! Namespace for dbms-independent classes. 00052 namespace Sql { 00053 00054 //! Exception class used throughout the package. 00055 /*! 00056 Derived from runtime_error, i.e. the only useful member function 00057 is Dv::Sql::Exception::what() which returns constant C string 00058 describing the error. 00059 00060 Most exceptions thrown are really logical errors (i.e. errors 00061 in the application or package source code). 00062 */ 00063 class Exception: public std::runtime_error { 00064 public: 00065 //! Name of class, is prepended to each message argument of the constructor. 00066 static const std::string NAME; 00067 //! Constructor, prepends NAME to message to obtain runtime_error::what(). 00068 Exception(const std::string& message): std::runtime_error(NAME+": "+message) {} 00069 }; 00070 00071 //! A field/column in a table or a query result. 00072 class Field { 00073 public: 00074 //! Field constructor. Users will never use this. 00075 /*! What are valid arguments is dbms-dependent. 00076 */ 00077 Field(const std::string& name,const std::string& table, const std::string& type, 00078 const std::string& defaultvalue, 00079 bool isint,bool isflt, bool isstr, bool isbin, unsigned int sz); 00080 00081 //! The name of the field. 00082 std::string name() const { return name_; } 00083 //! The name of the table containing this column. 00084 /*! For a query result (i.e. a Dv::Sql::Command), the table 00085 can be defined by the ``as'' clause in a ``select'' statement. 00086 */ 00087 std::string table() const { return table_; } 00088 //! The type of this field. The return value is dbms-dependent. 00089 std::string type() const { return type_; } 00090 //! The default value (see the constructor). 00091 std::string defaultvalue() const { return default_; } 00092 00093 //! Does the field have a numeric (int or float) type? 00094 bool numeric() const { return int_ || float_ ; } 00095 //! Does the field have an integer type? 00096 bool integer() const { return int_; } 00097 //! Does the field have floating point type? 00098 bool real() const { return float_; } 00099 //! Does the field have a binary type? 00100 bool binary() const { return binary_; } 00101 //! Does the field have a string type? 00102 bool text() const { return string_; } 00103 00104 //! Convert mysql timestamp(14) value, e.g. 20010724110830 to Date. 00105 static Dv::Util::Date timestamp2date(const std::string& timestamp); 00106 //! Size of the field (in bytes). 00107 unsigned int size() const { return size_; } 00108 00109 //! Print readable representation on ostream. 00110 /*! Example: 00111 \code 00112 LONG[11] test.number 00113 STRING[32] test.word 00114 DATETIME[19] structuur2.wanneer 00115 VAR_STRING[100] structuur2.info 00116 TIMESTAMP[14] structuur2.time_stamp 00117 \endcode 00118 */ 00119 friend std::ostream& operator<<(std::ostream&,const Field&); 00120 private: 00121 std::string name_; 00122 std::string table_; 00123 std::string type_; 00124 std::string default_; 00125 00126 bool int_; 00127 bool float_; 00128 bool string_; 00129 bool binary_; 00130 unsigned int size_; 00131 00132 }; 00133 00134 class Command_; 00135 class Command; 00136 00137 //! An connection to an SQL database. 00138 class Db { 00139 00140 //! Friend. 00141 friend class Command; 00142 00143 public: 00144 //! Since this class will be subclassed, its destructor must be virtual. 00145 /*! The destructor will close the connection. 00146 */ 00147 virtual ~Db() {} 00148 00149 //! Return true iff status is ok; e.g. db was succesfully opened. 00150 bool ok() const { return ok_; } 00151 //! Same as Dv::Sql::Db::ok(). 00152 operator bool() const { return ok_; } 00153 //! Same as !Dv::Sql::Db::ok(). 00154 bool operator!() const { return !ok_; } 00155 00156 //! Return string representation of last error. 00157 std::string err() const { return err_; } 00158 //! Name of host machine on which database resides. 00159 std::string host() const { return host_; } 00160 //! Name of current database of connection. 00161 std::string database() const { return name_; } 00162 00163 //! Logging is turned on if log()!=0. 00164 /*! The amount of logging is decided by the DBMS-dependent classes. 00165 E.g. Dv::MySql::Db logs the execution of every command as 00166 well as indication of the result. 00167 */ 00168 std::ostream* log() const { return log_; } 00169 //! Turn logging on/off, see Db::log(). 00170 void log(std::ostream* os) { log_ = os; } 00171 00172 //! Switch connection to database, return true iff succeeded. 00173 virtual bool database(const std::string& database) = 0; 00174 //! Return names of available databases. 00175 virtual std::vector<std::string> databases() = 0; 00176 //! Return names of tables in databases. 00177 virtual std::vector<std::string> tables() = 0; 00178 //! Return fields in table. 00179 virtual std::vector<Field> fields(const std::string& table) = 0; 00180 00181 protected: 00182 //! Set status. 00183 bool ok(bool b) { return ok_ = b; } 00184 //! Set error string returned by Dv::Sql::err(). 00185 void err(const std::string& s) { err_ = s; } 00186 //! Set database name. 00187 void name(const std::string& dbname) { name_ = dbname; } 00188 00189 00190 //! Return dbms-specific command. 00191 /*! Factory method; a concrete subclass X of Db implements this 00192 to return (a pointer to) an instance of the subclass of Command_ 00193 corresponding to X's Command_. 00194 */ 00195 virtual Command_* command_(Command&) = 0; 00196 00197 //! Constructor. 00198 /*! The constructor takes the name of the database 00199 and the name of the host where the database resides. 00200 If name == 0, there is no database yet, if host == 0, 00201 we use "localhost" 00202 00203 Copy ctor and assignment are forbidden. 00204 */ 00205 Db(const char* name,const char* host); 00206 00207 private: 00208 std::string host_; 00209 bool ok_; 00210 std::string err_; 00211 std::ostream* log_; 00212 00213 //! Name of database, may be "". 00214 std::string name_; 00215 00216 //! Cctor is forbidden. 00217 Db(const Db&); 00218 //! Assignment is forbidden. 00219 Db& operator=(const Db&); 00220 00221 }; 00222 00223 00224 //! A Command represents a command, e.g. a query, for a Dv::Sql::Db. 00225 /*! Command is a dbms-independent proxy class for the dbms-dependent 00226 Command_. A Command object has a pointer to a companion Command_ 00227 object, which has been created using Dv::Sql::Db::command_(). 00228 00229 This is an alternative for inheritance: 00230 instead of subclassing Command, the ``subclass part'' is 00231 available in *cmd_. 00232 00233 This has the advantage that only the definition of the 00234 database object ``db'' derived from Dv::Sql::Db depends on the actual 00235 DBMS used. All other database manipulatsions can be done 00236 through normal Dv::Sql::Command objects, which are initialized 00237 using a referrence to a Dv::Sql::Db. Consequently, applications 00238 are more independent of the actual underlying DBMS. 00239 00240 Example: 00241 \code 00242 MySql::Db db(...); 00243 00244 std::string name; 00245 Sql::Command query("select * from person where name = "); 00246 query << name; 00247 if (q.exec()) 00248 for (Sql::Command::iterator i=q.begin(); i!=q.end(); ++i) 00249 cout << i(0) << "\t" << i(1) << endl; 00250 \endcode 00251 Commands contain a text. This text is built using 00252 Command::operator<<(..). 00253 */ 00254 class Command { 00255 00256 public: 00257 00258 class iterator; 00259 //! Friend. 00260 friend class Db; 00261 //! Friend. 00262 friend class iterator; 00263 //! Friend. 00264 friend class Command_; 00265 00266 00267 //! Constructor, text is initial text of command. 00268 /*! Example 00269 \code 00270 Sql::Command q("insert into person values ("); 00271 // rest of command is inserted using operator<< 00272 \endcode 00273 */ 00274 Command(Db& db,const char* text=0); 00275 //! Destructor also destroys any result data. 00276 ~Command(); 00277 00278 //! Status of command. 00279 bool ok() const { return ok_; } 00280 //! Same as Dv::Sql::Command::ok(). 00281 operator bool() const { return ok_; } 00282 //! Error message. 00283 std::string err() const { return err_; } 00284 //! Return true iff the command has already been executed. 00285 /*! Calling Dv::Sql::Command::exec() twice is a noop. 00286 */ 00287 bool ran() const { return ran_; } 00288 //! Return database connection with which this command is associated. 00289 Db& db(); // see below, for inline definition 00290 //! Return text of command. 00291 /*! Example 00292 \code 00293 std::string name("jane"); 00294 Sql::Command query("select * from person where name = "); 00295 query << name; 00296 cout << query.text(); 00297 \endcode 00298 will print 00299 \code 00300 select * from person where name = 'jane' 00301 \endcode 00302 */ 00303 std::string text() { frozen_ = true; return text_.str();} 00304 00305 //! Return field information for query result. 00306 const std::vector<Field>& fields(); 00307 //! Return number of rows in query result. 00308 unsigned int nrows() { return nrows_; } 00309 //! Return number of columns in query result. 00310 unsigned int ncols() { return ncols_; } 00311 //! Return logical pointer to first row of the result. 00312 iterator begin(); 00313 //! Return logical pointer to past the end of the query result. 00314 iterator end(); 00315 00316 //! Check whether a rowno r is valid, i.e. the r'th row is available 00317 bool valid(int r) const; 00318 00319 //! Append a string to the command text; the string will be escaped and quoted. 00320 /*! 00321 E.g. after 00322 \code 00323 Db::Sql::Db& db; 00324 00325 Dv::Sql::Command q(db,"select * from person"); 00326 q << "where name =" << std::string("joe's nightamere"); 00327 \endcode 00328 q.text_ will contain 00329 \code 00330 select * from person where name = 'joe\'s nightmare' 00331 \endcode 00332 00333 Note that Dv::Sql::Command::operator<<(const char*) does not escape or quote. 00334 */ 00335 Command& operator<<(const std::string&) throw (Exception); 00336 //! Append a string to the command text. 00337 Command& operator<<(const char*) throw (Exception); 00338 //! Append an int to the command text. 00339 Command& operator<<(int) throw (Exception); 00340 //! Append an unsigned int to the command text. 00341 Command& operator<<(unsigned int) throw (Exception); 00342 //! Append a short to the command text. 00343 Command& operator<<(short) throw (Exception); 00344 //! Append an unsigned short to the command text. 00345 Command& operator<<(unsigned short) throw (Exception); 00346 //! Append a long to the command text. 00347 Command& operator<<(long) throw (Exception); 00348 //! Append an unsigned long to the command text. 00349 Command& operator<<(unsigned long) throw (Exception); 00350 //! Append a float to the command text. 00351 Command& operator<<(float) throw (Exception); 00352 //! Append a double to the command text. 00353 Command& operator<<(double) throw (Exception); 00354 00355 //! Execute the query, status is determined by underlying DBMS. 00356 bool exec(); // inline definition at the end of this file 00357 /** Return auto_increment id of last insert. */ 00358 unsigned int insertid() const; 00359 private: 00360 bool ran_; 00361 std::string err_; 00362 bool ok_; 00363 std::ostringstream text_; 00364 bool frozen_; // after text_.str() has been used 00365 00366 00367 //! Nrows_ is # of rows in query answer, only valid if ran() && ok() 00368 unsigned long nrows_; 00369 //! Ncols_ is # of columns in query answer, only valid if ran() && ok() 00370 unsigned long ncols_; 00371 00372 00373 //! Associated dbms-dependent object. 00374 /*! Many functions such as Dv::Sql::Command::exec() are delegated 00375 to cmd_->exec(). 00376 */ 00377 Command_ *cmd_; 00378 00379 //! Retrieve the r'th row of the result ( 0<= r < nrows_ ). 00380 /*! Delegated to cmd_. */ 00381 void fetch(std::vector<std::string>& v,unsigned int r) throw (Exception); 00382 //! Retrieve the r'th row and c'th column of the result ( 0<= r < //nrows_ ). 00383 /*! Delegated to cmd_. */ 00384 void fetch(std::string& s,unsigned int r,unsigned int c) throw (Exception); 00385 00386 00387 //! Cctor is forbidden. 00388 Command(const Command&); 00389 //! Assignment is forbidden. 00390 Command& operator=(const Command&); 00391 00392 void nrows(unsigned int n) { nrows_ = n; } 00393 void ncols(unsigned int n) { ncols_ = n; } 00394 void run() { ran_ = true; } 00395 void err(const char*s) { err_ = std::string(s); } 00396 void ok(bool b) { ok_ = b; } 00397 00398 }; 00399 00400 //! A logical pointer (input iterator) inside the query result of a Dv::Sql::Command. 00401 /*! A valid iterator in the range [begin,end[ points to 00402 a row of the query result. 00403 00404 Its value type is the type of a row in the query result, i.e. 00405 a vector of strings (classes like Dv::Int can be used to 00406 convert to numeric types, see dvutil). 00407 */ 00408 class Command::iterator: public vector_string_input_iterator { 00409 00410 //! Friend. 00411 friend class Command; 00412 00413 public: 00414 //! Make the iterator point to the next row in the result: prefix version. 00415 iterator& operator++() throw (Exception); 00416 //! Make the iterator point to the next row in the result: postfix version. 00417 iterator operator++(int) { iterator tmp=*this; ++*this; return tmp; } 00418 00419 //! Dereference to the current row. 00420 /*! NULL is represented by the std::string ``NULL''. 00421 */ 00422 std::vector<std::string> operator*() const throw (Exception); 00423 00424 //! Compare two iterators. 00425 bool operator==(const iterator&j) const { 00426 return row_ == j.row_ && &cmd_ == &j.cmd_; } 00427 //! Compare two iterators. 00428 bool operator!=(const iterator&j) const { 00429 return row_ != j.row_ || &cmd_ != &j.cmd_; } 00430 00431 //! Non standard operator: get c'th column of current row. 00432 /*! Example 00433 \code 00434 MySql::Db db(...); 00435 00436 std::string name; 00437 Sql::Command query("select * from person where name = "); 00438 query << name; 00439 if (q.exec()) 00440 for (Sql::Command::iterator i=q.begin(); i!=q.end(); ++i) 00441 cout << i(0) << "\t" << i(1) << endl; 00442 \endcode 00443 */ 00444 std::string operator()(unsigned int c) const throw (Exception); 00445 00446 //! Return command associated with iterator. 00447 Command& command() const { return cmd_; } 00448 private: 00449 enum { END = -1, BEGIN = 0 }; 00450 00451 Command& cmd_; 00452 int row_; // may be BEGIN || END || 0<= row_ < cmd_.nrows_; 00453 00454 iterator(Command& cmd,int r): cmd_(cmd), row_(r) {} 00455 }; 00456 00457 //! Proxy class for Sql::Db::Command. Not used by end users. 00458 /*! This class needs to be subclassed for a particular DBMS. 00459 A Dv::Sql::Command_ object forms a pair with a Dv::Sql::Command 00460 object: each contains a reference (or pointer) to the other. 00461 The Dv::Sql::Command_ object associated with a Dv::Sql::Command 00462 object cmd is created using the Dv::Sql::Db virtual member 00463 function Sql::Db::command_(cmd). 00464 00465 There is delegation in both ways: Dv::Sql::Command_ uses 00466 Dv::Sql::Command to perform some DBMS-independent operations and 00467 Dv::Sql::Command delegates all DBMS-dependent 00468 operations to Dv::Sql::Command_. 00469 */ 00470 class Command_ { 00471 //! Friend. 00472 friend class Command; 00473 protected: 00474 //! Return number of rows in the query result. 00475 unsigned int nrows() { return cmd_.nrows(); } 00476 //! Set number of rows in the query result. 00477 void nrows(unsigned int n) { cmd_.nrows(n); } 00478 //! Set number of columns in the query result. 00479 void ncols(unsigned int n) { cmd_.ncols(n); } 00480 //! Mark the command (and associated Dv::Sql::Command) as having been executed. 00481 void run() { cmd_.run(); } 00482 //! Return status string of this command. 00483 std::string err() { return cmd_.err(); } 00484 //! Set status string of this command. 00485 void err(const char*s) { cmd_.err(s); } 00486 //! Set status of this command. 00487 void ok(bool b) { cmd_.ok(b); } 00488 //! Retrieve text of the command as a string stream. 00489 std::ostringstream& textstream() { return cmd_.text_; } 00490 00491 //! Associated DBMS-independent Dv::Sql::Command object. 00492 Command& cmd_; 00493 00494 //! Constructor: the associated Dv::Sql::Command object must already exist. 00495 Command_(Command& cmd): cmd_(cmd) {} 00496 //! Destructor, virtual because it will be subclassed. 00497 virtual ~Command_() {} 00498 //! Actually execute the command, DBMS-dependent. 00499 virtual bool exec() = 0; 00500 //! Return database connection associated with this command. 00501 virtual Db& db() = 0; 00502 00503 //! Write s in properly quoted and escaped form to this->textstream() 00504 virtual void escape(const std::string& s); 00505 //! Low level retrieval: put row # r from query result into the parameter. 00506 virtual void fetch(std::vector<std::string>&,unsigned int r) throw (Exception) = 0; 00507 //! Low level retrieval: get column c from row # r from query result. 00508 /*! An exception is thrown if any of r or c is out of range. 00509 */ 00510 virtual void fetch(std::string&,unsigned int r,unsigned int c) throw (Exception); 00511 //! Return field information for the query result. 00512 virtual const std::vector<Field>& fields() = 0; 00513 /** Return id of auto_increment field of last insertion */ 00514 virtual unsigned int insertid() const; 00515 00516 private: 00517 Command_(const Command_&); 00518 Command_& operator=(const Command_&); 00519 00520 }; 00521 00522 inline void 00523 Command::fetch(std::vector<std::string>& v,unsigned int r) throw (Exception) { 00524 cmd_->fetch(v,r); 00525 } 00526 00527 inline void 00528 Command::fetch(std::string& s,unsigned int r,unsigned int c) throw (Exception) { 00529 cmd_->fetch(s,r,c); 00530 } 00531 00532 inline bool Command::exec() { return cmd_->exec(); } 00533 inline const std::vector<Field>& Command::fields() { return cmd_->fields(); } 00534 inline Db& Command::db() { return cmd_->db(); } 00535 inline Command::iterator Command::end() { return iterator(*this,iterator::END); } 00536 inline unsigned int Command::insertid() const { return cmd_->insertid(); } 00537 00538 } 00539 } 00540 00541 //! Update props to reflect query result row represented by iterator. 00542 /*! Updat props such that props[fieldname] = result for each fieldname 00543 of the command associated with the iterator. 00544 */ 00545 Dv::Util::Props& operator<<(Dv::Util::Props&, const Dv::Sql::Command::iterator&); 00546 00547 #endif