// $Id: query.h,v 1.1.1.1 2004/09/05 22:08:21 redi Exp $ /* MyMySQL -- Copyright (C) 2002,2003,2004 Jonathan Wakely Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MYMYSQL_QUERY_H #define MYMYSQL_QUERY_H /** @file query.h * @brief Declares Query and related types. */ #include #include #include #include #include #include namespace mymysql { /** * @class Query query.h mymysql/query.h * @brief Wrapper class for running MySQL queries. * * This class provides a wrapper around the process of running a query, * storing the result set and iterating over it, using the functions * @c mysql_real_query(), @c mysql_store_result() and @c mysql_fetch_row(). * Resources are managed internally and an %iterator class provides access * to the result set in a similar fashion to STL containers. * * Some functions, such as @c fieldLengths() return data that relates to * the "current row". This is the row most recently returned by either * getRow() or by dereferencing an iterator. * * @par Implementation Notes: * There is no interface to the @c mysql_list_tables() function, but * this is not a problem since it is equivalent to running the query * "SHOW TABLES LIKE '...'". */ class Query { /// Constant representing an invalid row number. static const unsigned long nrow = ULONG_MAX; public: /** @brief Type representing a row in the result set of a query. * * It is an array of fields represented as counted byte strings, * accessed with @c operator[]. * The length of the array is given by @c numCols() and the lengths of each * field by fieldLengths(). @c NULL fields are represented by a null pointer. * The byte strings cannot in general be assumed to be null-terminated, * since they may contain binary data. */ typedef MYSQL_ROW value_type; /** * @class iterator query.h mymysql/query.h * @brief Random access %iterator for traversing the results of a Query. * * This is the same type as Query::const_iterator, i.e. it is read-only. * * An %iterator @c p is @e nonsingular iff * p.base() != 0. * * An %iterator @c p is @e dereferenceable iff * 0 <= p.offset() < p.base()->numRows(). * * An %iterator @c p is @e past-the-end iff * p.offset() == p.base()->numRows(). * * @par Refinement Of: * @e DefaultContructible, @e Assignable, @e EqualityComparable, * @e StrictWeaklyComparable, @e RandomAccessIterator. * * @par Implementation Notes: * When an %iterator is dereferenced it caches the value that is returned, * so that subsequent dereferences do not need to fetch the value again. * Incrementing or decrementing the %iterator clears the cache so that * the next dereference will fetch the new value that is referred to. * This optimisation prevents the associated Query object from seeking * unnecessarily within the result set whenever iterators at different * offsets are dereferenced. This means that for an iterator @c i * the operations *i; *(i+1); *i are more efficient than * *i; *++i; *--i because in the first example the result * of the third dereference is still cached so there is no need to seek * back to that row and fetch it again. */ class iterator { public: /// %Iterator category. typedef std::random_access_iterator_tag iterator_category; /// Type pointed to by %iterator. typedef Query::value_type value_type; /// Type used to represent the distance between two iterators. typedef ptrdiff_t difference_type; /// Pointer type. typedef value_type const* pointer; /// Reference type. typedef value_type const& reference; /// An %iterator that refers to row @p n in the results of %Query @p q iterator(Query& q, unsigned long n) : m_res(&q), m_rownum(n), m_row(0) {} /// A singular %iterator. iterator() : m_res(0), m_rownum(Query::nrow), m_row(0) {} iterator& operator++(); ///< Prefix increment operator. iterator operator++(int); ///< Postfix increment operator. iterator& operator--(); ///< Prefix decrement operator. iterator operator--(int); ///< Postfix decrement operator. iterator& operator+=(int i); ///< Addition assignment operator. iterator& operator-=(int i); ///< Subtraction assignment operator. reference operator*() const; ///< Dereference operator. pointer operator->() const;///< Member access operator. reference operator[](difference_type n) const; ///< Element access operator. /// The offset of the row that the %iterator refers to. unsigned long offset() const { return m_rownum; } /// The Query object that the %iterator refers to. Query const* base() const { return m_res; } /// The field lengths in the row that the %iterator refers to. std::vector lengths() const; private: Query* m_res; unsigned long m_rownum; mutable value_type m_row; }; friend class iterator; /** @brief Immutable %iterator type. * @see iterator */ typedef iterator const_iterator; /** * @class Field query.h mymysql/query.h * @brief An object containing information about a field in a result set. * * This is a wrapper around the @c MYSQL_FIELD type in the MySQL C API, * see the documentation for that type for details of members of %Field. * This wrapper is used instead to avoid unsafe @c char* members. * * Note that this structure does not have any member corresponding to * the @c MYSQL_FIELD::def member. * * @par Refinement Of: * @e Assignable. */ struct Field { /// Constructor. Field(MYSQL_FIELD const& f); // compiler-generated copy ctor, op= and dtor are safe. /// Test whether the field is a numeric type. bool isNumeric() const { return IS_NUM(type); } /// Name of the field. std::string name; /// Name of the table containing the field, or empty for calculated fields. std::string table; /// Type of the field, as defined for @c MYSQL_FIELD. enum_field_types type; /// Width of the field, as specified in the table definition. unsigned int length; /// Maximum width of the field for the result set. unsigned int max_length; /// Different bit-flags for the field, as defined for @c MYSQL_FIELD. unsigned int flags; /// Number of decimals for numeric fields. unsigned int decimals; }; typedef std::vector field_info_type; /// Name for type returned by fieldLengths(); typedef std::vector field_lengths_type; /// Create a new %Query object. explicit Query(Connection const& db) : m_db(db), m_result(0), m_num_rows(0), m_num_cols(0), m_affected(0), m_insert_id(0), m_row(0), m_rownum(nrow), m_sql(), m_errno(0), m_error() { } /// Create a new %Query object and run the given query. Query(Connection const& db, std::string const& sql) : m_db(db), m_result(0), m_num_rows(0), m_num_cols(0), m_affected(0), m_insert_id(0), m_row(0), m_rownum(nrow), m_sql(), m_errno(0), m_error() { this->run(sql); } /// Destroy the %Query object, freeing its resources. ~Query() { if (m_result) ::mysql_free_result(m_result); } /// Execute the supplied SQL statement. bool run(std::string const& sql); /// Return an %iterator pointing to the first row in the result set iterator begin() { return iterator(*this, 0); } /// Return an %iterator pointing just after the last row in the result set. iterator end() { return iterator(*this, numRows()); } /// Return the first row in the result set. value_type front() { return *begin(); } /// Return the number of rows in the result set. unsigned long numRows() const { return m_num_rows; } /// Return the number of columns for the most recent query. unsigned long numCols() const { return m_num_cols; } /// Return the number of rows affected by the most recent query. unsigned long affectedRows() const { return m_affected; } /// Return the AUTO_INCREMENT ID generated by the previous query. unsigned long insertId() const { return m_insert_id; } /// Return the lengths of the fields in the current row. std::vector fieldLengths() const; /// Return the field info for the result set. field_info_type fieldInfo() const; /// Return the row at the current offset. value_type& getRow(); /// Adjust the current offset and return the new row. value_type& getRow(unsigned long); /// Return the current offset. unsigned long offset() const { return m_rownum; } /// Adjust the current offset. unsigned long offset(unsigned long rownum); /// Return the last SQL statement that was run (successfully or not). std::string const& sql() const { return m_sql; } /// Test whether an error occurred. bool operator !() const { return errnum(); } /// Return the error message for the most recent operation that could fail. std::string const& error() const { return m_error; } /// Return the error code for the most recent operation that could fail. unsigned int errnum() const { return m_errno; } /// Discard the last results and reset all state information. void reset(); /// Obtain the database connection. Connection const& connection() const { return m_db; } private: /// Set the error number and string according to the current error status. void setError() { m_error = (m_errno = mysql_errno(m_db)) ? mysql_error(m_db) : ""; } /// Private copy constructor to prevent copying. Query(Query const&); /// Private assignment operator to prevent copying. Query const& operator=(Query const&); Connection const& m_db; ///< Database connection. MYSQL_RES* m_result; ///< Results of last successful query. unsigned long m_num_rows; ///< Size of result set from last query. unsigned int m_num_cols; ///< Number of columns in the result set. unsigned long m_affected; ///< Number of rows affected by last query. unsigned long m_insert_id; ///< AUTO_INCREMENT ID of last query. value_type m_row; ///< Cache of current row. unsigned long m_rownum; ///< Offset of current row. std::string m_sql; ///< Most recently excuted SQL statement. unsigned int m_errno; ///< Error number from last operation. std::string m_error; ///< Error message from last operation. }; // Utility functions for Query::iterators ... /** @brief Add an arbitrary number to an %iterator. * @param p An %iterator * @param n The distance to increment the %iterator by. * @return A new %iterator * @pre @p [p,p+n) is a valid range. * @relates Query::iterator */ inline Query::iterator operator+(Query::iterator p, int n) { return (p += n); } /** @brief Add an arbitrary number to an %iterator. * @param n The distance to increment the %iterator by. * @param p An %iterator * @return A new %iterator * @pre @p [p,p+n) is a valid range. * @relates Query::iterator */ inline Query::iterator operator+(int n, Query::iterator p) { return (p += n); } /** @brief Subtract an arbitrary number from an %iterator. * @param p An %iterator * @param n The distance to decrement the %iterator by. * @return A new %iterator * @pre @p [p-n,p) is a valid range. * * @relates Query::iterator */ inline Query::iterator operator-(Query::iterator p, int n) { return (p -= n); } /** @brief Subtract an arbitrary number from an %iterator. * @param n The distance to decrement the %iterator by. * @param p An %iterator * @return A new %iterator * @pre @p [p-n,p) is a valid range. * * @relates Query::iterator */ inline Query::iterator operator-(int n, Query::iterator p) { return (p -= n); } /** * @brief Find the distance between two iterators. * * @param left A valid iterator. * @param right A valid iterator. * @return The distance between the two iterators. * @pre @p left and @p right are valid iterators that refer to the same * Query object. * * @relates Query::iterator */ inline Query::iterator::difference_type operator-(Query::iterator const& left, Query::iterator const& right) { return (left.offset() - right.offset()); } /** * @brief Equality operator. * * Compares two iterators for equality. * * @param left A valid iterator. * @param right A valid iterator. * @return @c true if the two iterators refer to the same row, * @c false otherwise. * * @pre @p left and @p right are valid iterators that refer to the same * Query object. * * @relates Query::iterator */ inline bool operator==(Query::iterator const& left, Query::iterator const& right) { return left.base() == right.base() && left.offset() == right.offset(); } /** * @brief Inequality operator. * * Compares two iterators for inequality. * @return !( @p left == @p right ) * @pre @p left and @p right are valid iterators that refer to the same * Query object. * * @relates Query::iterator */ inline bool operator!=(Query::iterator const& left, Query::iterator const& right) { return !(left == right); } /** * @brief Comparison operator. * * Compares two iterators. This operator satisfies the requirements * of a @e StrictWeakOrdering since for any two iterators for which * !(x < y) && !(y < x) * is @c true, x == y is @c true. * * @param left A valid %iterator. * @param right A valid %iterator. * @return @c true if @p left refers to a row with a lower offset than * @p right, @c false otherwise. * @pre @p left and @p right are valid iterators that refer to the same * Query object. * * @relates Query::iterator */ inline bool operator<(Query::iterator const& left, Query::iterator const& right) { // assert( left.base() == right.base() ); return left.offset() < right.offset(); } /** * @brief Comparison operator. * @param left A valid %iterator. * @param right A valid %iterator. * @return @p right < @p left * @pre @p left and @p right are valid iterators that refer to the same * Query object. * * @see operator<(Query::iterator const&, Query::iterator const&) * @relates Query::iterator */ inline bool operator>(Query::iterator const& left, Query::iterator const& right) { return right < left; } /** * @brief Comparison operator. * @param left A valid %iterator. * @param right A valid %iterator. * @return !( @p left < @p right ) * @pre @p left and @p right are valid iterators that refer to the same * Query object. * * @see operator<(Query::iterator const&, Query::iterator const&) * @relates Query::iterator */ inline bool operator<=(Query::iterator const& left, Query::iterator const& right) { return !(left < right); } /** * @brief Comparison operator. * @param left A valid %iterator. * @param right A valid %iterator. * @return !( @p right < @p left ) * @pre @p left and @p right are valid iterators that refer to the same * Query object. * * @see operator<(Query::iterator const&, Query::iterator const&) * @relates Query::iterator */ inline bool operator>=(Query::iterator const& left, Query::iterator const& right) { return !(right < left); } // Function definitions for Query class members ... /** * Runs the supplied query and if it returns results then the current * row is set to the point to the start of the results. * The results of any previous query are lost and the associated * resources are returned to the system. * * @param sql A string containing an SQL query. * @return @c true if the query executed successfuly, @c false otherwise. */ inline bool Query::run(std::string const& sql) { bool status = false; this->reset(); m_sql = sql; if (::mysql_real_query(m_db, m_sql.data(), m_sql.size())) // query failed { setError(); } else // query succeeded { m_result = ::mysql_store_result(m_db); m_num_cols = ::mysql_field_count(m_db); if (m_result) // query returned data (e.g. SELECT) { m_affected = m_num_rows = ::mysql_num_rows(m_result); m_rownum = 0; status = true; } else if (!m_num_cols) // query returned no data (e.g. INSERT, DELETE) { m_affected = ::mysql_affected_rows(m_db); m_insert_id = ::mysql_insert_id(m_db); status = true; } else // an error occurred { setError(); } } return status; } /** * Calling reset() discards the results of last query, freeing the * resources used by the result set. This invalidates any iterators * that refer to the previously held results and clears all state * information, including the current row and error status. */ inline void Query::reset() { if (m_result) { ::mysql_free_result(m_result); m_result = 0; m_affected = m_num_rows = m_num_cols = 0; m_insert_id = 0; m_rownum = nrow; m_row = 0; } m_sql.erase(); m_errno = 0; m_error.erase(); } /** * @return A container holding the lengths of each field in the current row. * * @warning "current row" might not be what you think until you dereference an iterator. */ inline std::vector Query::fieldLengths() const { std::vector lengths; if (m_row) { if (unsigned long* lp = mysql_fetch_lengths(m_result)) { lengths.assign(lp, lp+numCols()); } } return lengths; } /** * @return A container of objects representing the fields in the result set. */ inline Query::field_info_type Query::fieldInfo() const { field_info_type fields; if (m_result) { MYSQL_FIELD* fp = mysql_fetch_fields(m_result); fields.assign(fp, fp+numCols()); } return fields; } /** * The returned value will be zero if no query has been run, if the last * query returned no results, or if the current offset is outside the * valid range for the results. * * @return The row at the current offset. */ inline Query::value_type& Query::getRow() { if (!m_row && m_result && m_rownum != nrow) { m_row = ::mysql_fetch_row(m_result); setError(); } return m_row; } /** * This function adjusts the current offset to @p rownum and returns * the corresponding row. * * @param rownum An integer offset into the result set. * @return The row at the specified offset. * @see getRow() */ inline Query::value_type& Query::getRow(unsigned long rownum) { this->offset(rownum); return this->getRow(); } /** * If @p rownum is a valid offset into the result set (that is, it is * between @c 0 and numRows() - 1 inclusive) then the * current offset is set to @p rownum and any cached data is cleared. * Otherwise the cached data is cleared and the current row is set to * * @c Query::nrow. * @param rownum An integer offset into the result set. * @return The new offset. */ inline unsigned long Query::offset(unsigned long rownum) { if (m_rownum != rownum) { if (rownum == m_rownum+1 && m_row) // can optimise this case { // The requested offset is immediately after the currently cached row, // so we can just fetch the next row and cache it right away. m_row = ::mysql_fetch_row(m_result); ++m_rownum; } else { m_row = 0; // clear cache so new row must be fetched if (rownum < m_num_rows) { ::mysql_data_seek(m_result, rownum); // seek to offset m_rownum = rownum; // set current offset } else m_rownum = nrow; // no current row } } return m_rownum; } // Function definitions for members of iterator class ... /** * Increments the %iterator to refer to the next row of the result set. * * @return @c *this, after incrementing. */ inline Query::iterator& Query::iterator::operator++() { m_row = 0; ++m_rownum; return *this; } /** * Increments the %iterator to refer to the next row of the result set. * * @return A copy of @c *this, before incrementing. */ inline Query::iterator Query::iterator::operator++(int) { iterator tmp = *this; ++*this; return tmp; } /** * Decrements the %iterator to refer to the previous row of the result set. * * @return @c *this, after decrementing. */ inline Query::iterator& Query::iterator::operator--() { m_row = 0; --m_rownum; return *this; } /** * Decrements the %iterator to refer to the previous row of the result set. * * @return A copy of @c *this, before decrementing. */ inline Query::iterator Query::iterator::operator--(int) { iterator tmp = *this; --*this; return tmp; } /** * Moves the %iterator forward @p i rows. If @p i is negative the %iterator * moves backwards @p |i| rows. * * @param i The distance to move the %iterator. * @return @c *this after moving. */ inline Query::iterator& Query::iterator::operator+=(int i) { m_row = 0; m_rownum += i; return *this; } /** * Moves the %iterator backward @p i rows. If @p i is negative the %iterator * moves forwards @p |i| rows. * * @param i The distance to move the %iterator. * @return @c *this after moving. */ inline Query::iterator& Query::iterator::operator-=(int i) { m_row = 0; m_rownum -= i; return *this; } /** * @return The row the %iterator refers to. * @pre The %iterator is dereferenceable. */ inline Query::iterator::reference Query::iterator::operator*() const { if (!m_row) m_row = m_res->getRow(m_rownum); return m_row; } /** * @return The row the %iterator refers to. * @pre The %iterator is dereferenceable. */ inline Query::iterator::pointer Query::iterator::operator->() const { return &**this; } /** @param n Offset of element to access, relative to @c *this. * @return *(*this + n) * @pre (*this + n) exists and is dereferenceable. */ inline Query::iterator::reference Query::iterator::operator[](Query::iterator::difference_type n) const { return *(*this + n); } /** @return A container holding the lengths of each field in the row. * @pre %Iterator is dereferencable. */ inline std::vector Query::iterator::lengths() const { **this; // set current offset return m_res->fieldLengths(); } // Function definitions for members of Query::Field class ... /** @brief Construct a %Field object as a copy of a @c MYSQL_FIELD structure. * @param f The @c MYSQL_FIELD to copy. */ inline Query::Field::Field(MYSQL_FIELD const& f) : name(f.name), table(f.table), type(f.type), length(f.length) , max_length(f.max_length), flags(f.flags), decimals(f.decimals) { } } // namespace mymysql #endif // MYMYSQL_QUERY_H // vim: ts=8 sw=2 expandtab