Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

result.hxx

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  *   FILE
00004  *      pqxx/result.hxx
00005  *
00006  *   DESCRIPTION
00007  *      definitions for the pqxx::result class and support classes.
00008  *   pqxx::result represents the set of result tuples from a database query
00009  *   DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/result instead.
00010  *
00011  * Copyright (c) 2001-2004, Jeroen T. Vermeulen <jtv@xs4all.nl>
00012  *
00013  * See COPYING for copyright license.  If you did not receive a file called
00014  * COPYING with this source code, please notify the distributor of this mistake,
00015  * or contact the author.
00016  *
00017  *-------------------------------------------------------------------------
00018  */
00019 #include <ios>
00020 #include <stdexcept>
00021 
00022 #include "pqxx/util"
00023 
00024 /* Methods tested in eg. self-test program test001 are marked with "//[t1]"
00025  */
00026 
00027 
00028 // TODO: Support SQL arrays
00029 // TODO: value_type, reference, const_reference, difference_type
00030 // TODO: container comparisons
00031 
00032 namespace pqxx
00033 {
00034 
00036 
00043 class PQXX_LIBEXPORT result
00044 {
00045 public:
00046   result() throw () : m_Result(0), m_Refcount(0) {}                     //[t3]
00047   result(const result &rhs) throw () :                                  //[t1]
00048           m_Result(0), m_Refcount(0) { MakeRef(rhs); }
00049   ~result() { LoseRef(); }                                              //[t1]
00050   
00051   result &operator=(const result &) throw ();                           //[t10]
00052 
00053   typedef result_size_type size_type;
00054   class field;
00055 
00056   // TODO: Field iterators
00057  
00059 
00067   class PQXX_LIBEXPORT tuple
00068   {
00069   public:
00070     typedef tuple_size_type size_type;
00071     tuple(const result *r, result::size_type i) throw () : 
00072       m_Home(r), m_Index(i) {}
00073     ~tuple() throw () {} // Yes Scott Meyers, you're absolutely right[1]
00074 
00075     inline field operator[](size_type) const throw ();                  //[t1]
00076     field operator[](const char[]) const;                               //[t11]
00077     field operator[](const PGSTD::string &s) const                      //[t11]
00078         { return operator[](s.c_str()); }
00079     field at(size_type) const throw (PGSTD::out_of_range);              //[t10]
00080     field at(const char[]) const;                                       //[t11]
00081     field at(const PGSTD::string &s) const { return at(s.c_str()); }    //[t11]
00082 
00083     inline size_type size() const throw ();                             //[t11]
00084 
00085     result::size_type rownumber() const throw () { return m_Index; }    //[t11]
00086 
00088     size_type column_number(const PGSTD::string &ColName) const         //[t30]
00089         { return m_Home->column_number(ColName); }
00090 
00092     size_type column_number(const char ColName[]) const                 //[t30]
00093         { return m_Home->column_number(ColName); }
00094 
00096     oid column_type(size_type ColNum) const                             //[t7]
00097         { return m_Home->column_type(ColNum); }
00098 
00100     oid column_type(const PGSTD::string &ColName) const                 //[t7]
00101         { return column_type(column_number(ColName)); }
00102 
00104     oid column_type(const char ColName[]) const                         //[t7]
00105         { return column_type(column_number(ColName)); }
00106 
00107 #ifdef PQXX_HAVE_PQFTABLE
00108     oid column_table(size_type ColNum) const                            //[t2]
00109         { return m_Home->column_table(ColNum); }
00110     oid column_table(const PGSTD::string &ColName) const                //[t2]
00111         { return column_table(column_number(ColName)); }
00112 #endif
00113 
00114 
00115 #ifdef PQXX_DEPRECATED_HEADERS
00116 
00117     result::size_type Row() const { return rownumber(); }
00118 
00120     size_type ColumnNumber(const PGSTD::string &ColName) const 
00121         { return m_Home->ColumnNumber(ColName); }
00122 
00124     size_type ColumnNumber(const char ColName[]) const 
00125         { return m_Home->ColumnNumber(ColName); }
00126 #endif
00127 
00128 
00129   protected:
00130     const result *m_Home;
00131     result::size_type m_Index;
00132 
00133     // Not allowed:
00134     tuple();
00135   };
00136 
00138 
00141   class PQXX_LIBEXPORT field : private tuple
00142   {
00143   public:
00144     typedef size_t size_type;
00145 
00147 
00151     field(const tuple &R, tuple::size_type C) throw () :                //[t1]
00152         tuple(R), m_Col(C) {}
00153 
00155 
00160     const char *c_str() const {return m_Home->GetValue(m_Index,m_Col);} //[t2]
00161 
00163     inline const char *name() const;                                    //[t11]
00164 
00166     oid type() const                                                    //[t7]
00167         { return m_Home->column_type(m_Col); }
00168 
00169 #ifdef PQXX_HAVE_PQFTABLE
00170 
00171 
00173     oid table() const { return m_Home->column_table(m_Col); }           //[t2]
00174 #endif
00175 
00177 
00186     template<typename T> bool to(T &Obj) const                          //[t3]
00187     {
00188       if (is_null())
00189         return false;
00190 
00191       try
00192       {
00193         from_string(c_str(), Obj);
00194       }
00195       catch (const PGSTD::exception &e)
00196       {
00197         throw PGSTD::domain_error("Error reading field " + 
00198                                   PGSTD::string(name()) +
00199                                   ": " +
00200                                   e.what());
00201       }
00202       return true;
00203     }
00204 
00205 
00206 #ifdef PQXX_NO_PARTIAL_CLASS_TEMPLATE_SPECIALISATION
00207 
00208     template<> bool to<PGSTD::string>(PGSTD::string &Obj) const;
00209 
00211 
00214     template<> bool to<const char *>(const char *&Obj) const;
00215 #endif
00216 
00217 
00219     template<typename T> bool to(T &Obj, const T &Default) const        //[t12]
00220     {
00221       const bool NotNull = to(Obj);
00222       if (!NotNull)
00223         Obj = Default;
00224       return NotNull;
00225     }
00226 
00228 
00231     template<typename T> T as(const T &Default) const                   //[t1]
00232     {
00233       T Obj;
00234       to(Obj, Default);
00235       return Obj;
00236     }
00237 
00239     template<typename T> T as() const                                   //[t45]
00240     {
00241       T Obj;
00242       const bool NotNull = to(Obj);
00243       if (!NotNull) throw PGSTD::domain_error("Attempt to read null field");
00244       return Obj;
00245     }
00246 
00247     bool is_null() const { return m_Home->GetIsNull(m_Index,m_Col); }   //[t12]
00248 
00249     size_type size() const { return m_Home->GetLength(m_Index,m_Col); } //[t11]
00250 
00251 #ifdef PQXX_DEPRECATED_HEADERS
00252 
00253     const char *Name() const {return name();}
00254 #endif
00255 
00256   private:
00257     tuple::size_type m_Col;
00258   };
00259 
00261 
00265   class PQXX_LIBEXPORT const_iterator : 
00266     public PGSTD::iterator<PGSTD::random_access_iterator_tag, 
00267                          const tuple,
00268                          result::size_type>, 
00269     public tuple
00270   {
00271   public:
00272     const_iterator() : tuple(0,0) {}
00273 
00280     pointer operator->()  const { return this; }                        //[t12]
00281     reference operator*() const { return *operator->(); }               //[t12]
00282 
00283     const_iterator operator++(int);                                     //[t12]
00284     const_iterator &operator++() { ++m_Index; return *this; }           //[t1]
00285     const_iterator operator--(int);                                     //[t12]
00286     const_iterator &operator--() { --m_Index; return *this; }           //[t12]
00287 
00288     const_iterator &operator+=(difference_type i)                       //[t12]
00289         { m_Index+=i; return *this; }
00290     const_iterator &operator-=(difference_type i)                       //[t12]
00291         { m_Index-=i; return *this; }
00292 
00293     bool operator==(const const_iterator &i) const                      //[t12]
00294         {return m_Index==i.m_Index;}
00295     bool operator!=(const const_iterator &i) const                      //[t12]
00296         {return m_Index!=i.m_Index;}
00297     bool operator<(const const_iterator &i) const                       //[t12]
00298          {return m_Index<i.m_Index;}
00299     bool operator<=(const const_iterator &i) const                      //[t12]
00300         {return m_Index<=i.m_Index;}
00301     bool operator>(const const_iterator &i) const                       //[t12]
00302         {return m_Index>i.m_Index;}
00303     bool operator>=(const const_iterator &i) const                      //[t12]
00304         {return m_Index>=i.m_Index;}
00305 
00306     inline const_iterator operator+(difference_type o) const;           //[t12]
00307 
00308     friend const_iterator operator+(difference_type o, 
00309                                     const_iterator i);                  //[t12]
00310 
00311     inline const_iterator operator-(difference_type o) const;           //[t12]
00312 
00313     inline difference_type operator-(const_iterator i) const;           //[t12]
00314 
00315     result::size_type num() const { return rownumber(); }               //[t1]
00316 
00317   private:
00318     friend class result;
00319     const_iterator(const result *r, result::size_type i) : tuple(r, i) {}
00320   };
00321 
00322 #ifdef PQXX_HAVE_REVERSE_ITERATOR
00323   typedef PGSTD::reverse_iterator<const_iterator> const_reverse_iterator;
00324   const_reverse_iterator rbegin() const                                 //[t75]
00325         { return const_reverse_iterator(end()); }
00326   const_reverse_iterator rend() const                                   //[t75]
00327         { return const_reverse_iterator(begin()); }
00328 #endif
00329 
00330   const_iterator begin() const { return const_iterator(this, 0); }      //[t1]
00331   inline const_iterator end() const;                                    //[t1]
00332 
00333   size_type size() const { return m_Result ? PQntuples(m_Result) : 0; } //[t2]
00334   bool empty() const { return !m_Result || !PQntuples(m_Result); }      //[t11]
00335   size_type capacity() const { return size(); }                         //[t20]
00336 
00337   void swap(result &other) throw ();                                    //[t77]
00338 
00339   const tuple operator[](size_type i) const throw ()                    //[t2]
00340         { return tuple(this, i); }
00341   const tuple at(size_type) const throw (PGSTD::out_of_range);          //[t10]
00342 
00343   void clear() throw () { LoseRef(); }                                  //[t20]
00344 
00346   tuple::size_type columns() const throw ()                             //[t11]
00347         { return PQnfields(m_Result); }
00348 
00350   tuple::size_type column_number(const char ColName[]) const;           //[t11]
00351 
00353   tuple::size_type column_number(const PGSTD::string &Name) const       //[t11]
00354         {return column_number(Name.c_str());}
00355 
00357   const char *column_name(tuple::size_type Number) const;               //[t11]
00358 
00360   inline oid column_type(tuple::size_type ColNum) const;                //[t7]
00361 
00363   oid column_type(const PGSTD::string &ColName) const                   //[t7]
00364         { return column_type(column_number(ColName)); }
00365 
00367   oid column_type(const char ColName[]) const                           //[t7]
00368         { return column_type(column_number(ColName)); }
00369 
00370 #ifdef PQXX_HAVE_PQFTABLE
00371 
00372   oid column_table(tuple::size_type ColNum) const;                      //[t2]
00373 
00375   oid column_table(const PGSTD::string &ColName) const                  //[t2]
00376         { return column_table(column_number(ColName)); }
00377 #endif
00378 
00379 
00381 
00383   oid inserted_oid() const { return PQoidValue(m_Result); }             //[t13]
00384 
00385 
00387   /*** Returns zero for all other commands. */
00388   size_type affected_rows() const;                                      //[t7]
00389 
00390 
00391 #ifdef PQXX_DEPRECATED_HEADERS
00392 
00393   typedef tuple Tuple;
00395   typedef field Field;
00397   oid InsertedOid() const { return inserted_oid(); }
00399   size_type AffectedRows() const { return affected_rows(); }
00401   tuple::size_type Columns() const { return columns(); }
00403   tuple::size_type ColumnNumber(const char Name[]) const
00404         {return PQfnumber(m_Result,Name);}
00406   tuple::size_type ColumnNumber(const PGSTD::string &Name) const
00407         {return ColumnNumber(Name.c_str());}
00409   const char *ColumnName(tuple::size_type Number) const
00410         {return PQfname(m_Result,Number);}
00411 #endif
00412 
00413 
00414 private:
00415   PGresult *m_Result;
00416   mutable int *m_Refcount;
00417 
00418   friend class result::field;
00419   const char *GetValue(size_type Row, tuple::size_type Col) const;
00420   bool GetIsNull(size_type Row, tuple::size_type Col) const;
00421   field::size_type GetLength(size_type Row, tuple::size_type Col) const;
00422 
00423   friend class connection_base;
00424   explicit result(PGresult *rhs) : m_Result(rhs), m_Refcount(0) {MakeRef(rhs);}
00425   result &operator=(PGresult *);
00426   bool operator!() const throw () { return !m_Result; }
00427   operator bool() const throw () { return m_Result != 0; }
00428   friend class pipeline;
00429   void CheckStatus(const PGSTD::string &Query) const;
00430   void CheckStatus(const char Query[]) const;
00431   PGSTD::string StatusError() const;
00432 
00433   friend class Cursor;
00434   const char *CmdStatus() const throw () { return PQcmdStatus(m_Result); }
00435 
00436 
00437   void MakeRef(PGresult *);
00438   void MakeRef(const result &) throw ();
00439   void LoseRef() throw ();
00440 };
00441 
00442 
00444 
00461 template<typename STREAM>
00462 inline STREAM &operator<<(STREAM &S, const pqxx::result::field &F)      //[t46]
00463 {
00464   S.write(F.c_str(), F.size());
00465   return S;
00466 }
00467 
00468 
00470 template<typename T>
00471 inline void from_string(const result::field &F, T &Obj)                 //[t46]
00472         { from_string(F.c_str(), Obj); }
00473 
00475 template<>
00476 inline PGSTD::string to_string(const result::field &Obj)                //[t74]
00477         { return to_string(Obj.c_str()); }
00478 
00479 inline result::field 
00480 result::tuple::operator[](result::tuple::size_type i) const  throw ()
00481 { 
00482   return field(*this, i); 
00483 }
00484 
00485 inline result::tuple::size_type result::tuple::size() const throw ()
00486 { 
00487   return m_Home->columns(); 
00488 }
00489 
00490 inline const char *result::field::name() const 
00491 { 
00492   return m_Home->column_name(m_Col); 
00493 }
00494 
00496 template<> 
00497 inline bool result::field::to<PGSTD::string>(PGSTD::string &Obj) const
00498 {
00499   if (is_null()) return false;
00500   Obj = c_str();
00501   return true;
00502 }
00503 
00505 
00508 template<> 
00509 inline bool result::field::to<const char *>(const char *&Obj) const
00510 {
00511   if (is_null()) return false;
00512   Obj = c_str();
00513   return true;
00514 }
00515 
00516 
00517 inline result::const_iterator 
00518 result::const_iterator::operator+(difference_type o) const
00519 {
00520   return const_iterator(m_Home, m_Index + o);
00521 }
00522 
00523 inline result::const_iterator 
00524 operator+(result::const_iterator::difference_type o, 
00525           result::const_iterator i)
00526 {
00527   return i + o;
00528 }
00529 
00530 inline result::const_iterator 
00531 result::const_iterator::operator-(difference_type o) const
00532 {
00533   return const_iterator(m_Home, m_Index - o);
00534 }
00535 
00536 inline result::const_iterator::difference_type 
00537 result::const_iterator::operator-(const_iterator i) const
00538 { 
00539   return num()-i.num(); 
00540 }
00541 
00542 inline result::const_iterator result::end() const 
00543 { 
00544   return const_iterator(this, size()); 
00545 }
00546 
00547 inline oid result::column_type(tuple::size_type ColNum) const
00548 {
00549   const oid T = PQftype(m_Result, ColNum);
00550   if (T == oid_none)
00551     throw PGSTD::invalid_argument(
00552                 "Attempt to retrieve type of nonexistant column " +
00553                 to_string(ColNum) + " "
00554                 "of query result");
00555   return T;
00556 }
00557 
00558 
00559 #ifdef PQXX_HAVE_PQFTABLE
00560 inline oid result::column_table(tuple::size_type ColNum) const
00561 {
00562   const oid T = PQftable(m_Result, ColNum);
00563 
00564   /* If we get InvalidOid, it may be because the column is computed, or because
00565    * we got an invalid row number.
00566    */
00567   // TODO: Skip this if we first computed the column name ourselves
00568   if ((T == InvalidOid) &&
00569       ((ColNum < 0) || (ColNum >= columns())))
00570     throw PGSTD::invalid_argument("Attempt to retrieve table ID for column " +
00571                                   to_string(ColNum) + " "
00572                                   "out of " + to_string(columns()));
00573   return T;
00574 }
00575 #endif
00576 
00577 
00578 template<typename CHAR=char, typename TRAITS=PGSTD::char_traits<CHAR> >
00579   class field_streambuf :
00580 #ifdef PQXX_HAVE_STREAMBUF
00581   public PGSTD::basic_streambuf<CHAR, TRAITS>
00582 #else
00583   public PGSTD::streambuf
00584 #endif
00585 {
00586   typedef long size_type;
00587 public:
00588   typedef CHAR char_type;
00589   typedef TRAITS traits_type;
00590   typedef typename traits_type::int_type int_type;
00591 #ifdef PQXX_HAVE_STREAMBUF
00592   typedef typename traits_type::pos_type pos_type;
00593   typedef typename traits_type::off_type off_type;
00594 #else
00595   typedef streamoff off_type;
00596   typedef streampos pos_type;
00597 #endif
00598   typedef PGSTD::ios::openmode openmode;
00599   typedef PGSTD::ios::seekdir seekdir;
00600 
00601   explicit field_streambuf(const result::field &F) :                    //[t74]
00602     m_Field(F)
00603   {
00604     initialize();
00605   }
00606 
00607 #ifdef PQXX_HAVE_STREAMBUF
00608 protected:
00609 #endif
00610   virtual int sync() { return traits_type::eof(); }
00611 
00612 protected:
00613   virtual pos_type seekoff(off_type, seekdir, openmode)
00614   {
00615     return traits_type::eof();
00616   }
00617 
00618   virtual pos_type seekpos(pos_type, openmode) {return traits_type::eof();}
00619 
00620   virtual int_type overflow(int_type) { return traits_type::eof(); }
00621 
00622   virtual int_type underflow() { return traits_type::eof(); }
00623 
00624 private:
00625   const result::field &m_Field;
00626 
00627   int_type initialize() throw ()
00628   {
00629     char_type *G = 
00630       reinterpret_cast<char_type *>(const_cast<char *>(m_Field.c_str()));
00631     setg(G, G, G + m_Field.size());
00632     return m_Field.size();
00633   }
00634 };
00635 
00636 
00638 
00652 template<typename CHAR=char, typename TRAITS=PGSTD::char_traits<CHAR> >
00653   class basic_fieldstream :
00654 #ifdef PQXX_HAVE_STREAMBUF
00655     public PGSTD::basic_istream<CHAR, TRAITS>
00656 #else
00657     public PGSTD::istream
00658 #endif
00659 {
00660 #ifdef PQXX_HAVE_STREAMBUF
00661   typedef PGSTD::basic_istream<CHAR, TRAITS> super;
00662 #else
00663   typedef PGSTD::istream super;
00664 #endif
00665 
00666 public:
00667   typedef CHAR char_type;
00668   typedef TRAITS traits_type;
00669   typedef typename traits_type::int_type int_type;
00670   typedef typename traits_type::pos_type pos_type;
00671   typedef typename traits_type::off_type off_type;
00672 
00673   basic_fieldstream(const result::field &F) : super(&m_Buf), m_Buf(F) { }
00674 
00675 private:
00676   field_streambuf<CHAR, TRAITS> m_Buf;
00677 };
00678 
00679 typedef basic_fieldstream<char> fieldstream;
00680 
00681 } // namespace pqxx
00682 
00683 
00684 
00685 /* 
00686 [1] Scott Meyers, in one of his essential books, "Effective C++" and "More 
00687 Effective C++", points out that it is good style to have any class containing 
00688 a member of pointer type define its own destructor--just to show that it knows
00689 what it is doing.  This helps prevent nasty memory leak / double deletion bugs
00690 typically resulting from programmers' omission to deal with such issues in
00691 their destructors.
00692 
00693 The -Weffc++ option in gcc generates warnings for noncompliance with Scott's
00694 style guidelines, and hence necessitates the definition of this destructor,\
00695 trivial as it may be.
00696 
00697 [2] IIRC Alex Stepanov, the inventor of the STL, once remarked that having
00698 this as standard behaviour for pointers would be useful in some algorithms.
00699 So even if this makes me look foolish, I would seem to be in distinguished 
00700 company.
00701 */
00702 
00703 

Generated on Mon May 10 13:19:09 2004 for libpqxx by doxygen 1.3.6-20040222