2016-02-05 21:01:51 +01:00
/* Copyright 2015, 2016, Michele Santullo
2015-11-11 02:01:37 +00:00
* This file is part of " dindexer " .
*
* " dindexer " is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* " dindexer " 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 General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with " dindexer " . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "pq/resultset.hpp"
2015-12-04 12:58:13 +00:00
# include "resultinfo.hpp"
2015-11-11 02:01:37 +00:00
# include "pq/databaseexception.hpp"
# include <libpq-fe.h>
# include <map>
# include <cassert>
# include <ciso646>
# include <boost/lexical_cast.hpp>
# include <algorithm>
# include <boost/bind.hpp>
2015-12-31 00:38:44 +00:00
# include "libpqtypes.h"
2015-11-11 02:01:37 +00:00
namespace pq {
2015-12-31 00:38:44 +00:00
namespace {
int cast_col_index_or_throw ( std : : size_t parIndex , std : : size_t parCount , const char * parFile , int parLine ) {
if ( parIndex > = parCount ) {
std : : ostringstream oss ;
oss < < " Requested column at index " < < parIndex < < " is out of range (column count is " < < parCount < < ' ) ' ;
throw DatabaseException ( " Error retrieving column " , oss . str ( ) , parFile , parLine ) ;
}
return static_cast < int > ( parIndex ) ;
}
int conv_col_index_or_throw ( const std : : string & parIndex , const std : : map < std : : string , int > & parCols , const char * parFile , int parLine ) {
const auto num_index_it = parCols . find ( parIndex ) ;
if ( parCols . end ( ) = = num_index_it ) {
throw DatabaseException ( " Error retrieving column " , " Requested column \" " + parIndex + " \" doesn't exist " , parFile , parLine ) ;
}
return num_index_it - > second ;
}
std : : string binary_to_string ( const char * parValue , Oid parType , int parLength ) {
using boost : : lexical_cast ;
2016-01-07 15:12:21 +00:00
static_cast < void > ( parLength ) ;
2015-12-31 00:38:44 +00:00
assert ( parValue ) ;
assert ( parLength > 0 ) ;
//TODO: use libpqtypes
switch ( parType ) {
case 21 :
return lexical_cast < std : : string > ( * reinterpret_cast < const uint16_t * > ( parValue ) ) ;
default :
//not implemented
assert ( false ) ;
return std : : string ( ) ;
}
}
} //unnamed namespace
2015-11-11 02:01:37 +00:00
struct Row : : LocalData {
LocalData ( const PGresult * parResult , std : : size_t parRow , const std : : map < std : : string , int > * parCols , std : : size_t parColCount ) :
result ( parResult ) ,
columns ( parCols ) ,
row_index ( parRow ) ,
col_count ( parColCount )
{
assert ( ( result and columns ) or ( not ( result or columns ) ) ) ;
}
const PGresult * const result ;
const std : : map < std : : string , int > * const columns ;
const std : : size_t row_index ;
const std : : size_t col_count ;
} ;
struct ResultSet : : LocalData {
2015-12-30 10:15:36 +00:00
typedef ResultInfo : : PGResultPtr PGResultPtr ;
explicit LocalData ( PGResultPtr & & parResult ) :
info ( std : : move ( parResult ) , & columns )
2015-11-11 02:01:37 +00:00
{
}
2015-12-30 10:15:36 +00:00
ResultInfo info ;
2015-11-11 02:01:37 +00:00
std : : map < std : : string , int > columns ;
} ;
2015-12-30 10:15:36 +00:00
Row : : Row ( const ResultInfo * parResult , std : : size_t parRow ) :
m_localData ( new LocalData ( parResult - > result . get ( ) , parRow , parResult - > column_mappings , ( parResult - > column_mappings ? parResult - > column_mappings - > size ( ) : 0 ) ) )
2015-11-11 02:01:37 +00:00
{
assert ( not m_localData - > result or static_cast < std : : size_t > ( std : : max ( 0 , PQnfields ( m_localData - > result ) ) ) = = m_localData - > col_count ) ;
}
Row : : Row ( Row & & parOther ) :
m_localData ( std : : move ( parOther . m_localData ) )
{
}
Row : : ~ Row ( ) noexcept {
}
std : : string Row : : operator [ ] ( const std : : string & parIndex ) const {
2015-12-31 00:38:44 +00:00
return get_as_str_assume_valid ( conv_col_index_or_throw ( parIndex , * m_localData - > columns , __FILE__ , __LINE__ ) ) ;
2015-11-11 02:01:37 +00:00
}
std : : size_t Row : : size ( ) const {
return m_localData - > col_count ;
}
bool Row : : empty ( ) const {
return size ( ) = = 0 ;
}
std : : string Row : : operator [ ] ( std : : size_t parIndex ) const {
2015-12-31 00:38:44 +00:00
return get_as_str_assume_valid ( cast_col_index_or_throw ( parIndex , m_localData - > col_count , __FILE__ , __LINE__ ) ) ;
}
std : : string Row : : get_as_str_assume_valid ( int parIndex ) const {
const int num_index = parIndex ;
const auto row_index = static_cast < int > ( m_localData - > row_index ) ;
const auto format = PQfformat ( m_localData - > result , num_index ) ;
const int data_len = PQgetlength ( m_localData - > result , row_index , num_index ) ;
switch ( format ) {
case 0 :
//textual data
return std : : string ( PQgetvalue ( m_localData - > result , row_index , num_index ) , data_len ) ;
case 1 :
//binary data
if ( is_null ( num_index ) ) {
return std : : string ( ) ;
}
else {
return binary_to_string (
PQgetvalue ( m_localData - > result , row_index , num_index ) ,
PQftype ( m_localData - > result , num_index ) ,
data_len
) ;
}
default :
//unknown
assert ( false ) ;
return std : : string ( ) ;
2015-11-11 02:01:37 +00:00
}
2015-12-31 00:38:44 +00:00
}
bool Row : : is_null ( std : : size_t parIndex ) const {
const auto num_index = cast_col_index_or_throw ( parIndex , m_localData - > col_count , __FILE__ , __LINE__ ) ;
const auto row_index = static_cast < int > ( m_localData - > row_index ) ;
return ( 1 = = PQgetisnull ( m_localData - > result , row_index , num_index ) ? true : false ) ;
}
2015-11-11 02:01:37 +00:00
2015-12-31 00:38:44 +00:00
bool Row : : is_null ( const std : : string & parIndex ) const {
const auto num_index = conv_col_index_or_throw ( parIndex , * m_localData - > columns , __FILE__ , __LINE__ ) ;
const auto row_index = static_cast < int > ( m_localData - > row_index ) ;
return ( 1 = = PQgetisnull ( m_localData - > result , row_index , num_index ) ? true : false ) ;
2015-11-11 02:01:37 +00:00
}
Row : : const_iterator Row : : begin ( ) const {
return boost : : make_transform_iterator (
boost : : counting_iterator < std : : size_t > ( 0 ) ,
boost : : bind ( static_cast < std : : string ( Row : : * ) ( std : : size_t ) const > ( & Row : : operator [ ] ) , this , _1 )
) ;
}
Row : : const_iterator Row : : end ( ) const {
return boost : : make_transform_iterator (
boost : : counting_iterator < std : : size_t > ( m_localData - > col_count ) ,
boost : : bind ( static_cast < std : : string ( Row : : * ) ( std : : size_t ) const > ( & Row : : operator [ ] ) , this , _1 )
) ;
}
2015-12-30 10:15:36 +00:00
ResultSet : : ResultSet ( ResultInfo & & parResult ) :
m_localData ( new LocalData ( std : : move ( parResult . result ) ) )
2015-11-11 02:01:37 +00:00
{
2015-12-30 10:15:36 +00:00
const auto result = m_localData - > info . result . get ( ) ;
2015-11-11 02:01:37 +00:00
const int column_count = PQnfields ( result ) ;
std : : string col_name ;
for ( int z = 0 ; z < column_count ; + + z ) {
m_localData - > columns [ PQfname ( result , z ) ] = z ;
}
}
ResultSet : : ResultSet ( ResultSet & & parOther ) :
m_localData ( std : : move ( parOther . m_localData ) )
{
}
ResultSet : : ~ ResultSet ( ) noexcept {
}
Row ResultSet : : operator [ ] ( std : : size_t parIndex ) const {
if ( parIndex > = size ( ) )
throw DatabaseException ( " Out of range " , " Column index " + boost : : lexical_cast < std : : string > ( parIndex ) + " is out of range (column count is " + boost : : lexical_cast < std : : string > ( size ( ) ) + ' ) ' , __FILE__ , __LINE__ ) ;
2015-12-30 10:15:36 +00:00
return Row ( & m_localData - > info , parIndex ) ;
2015-11-11 02:01:37 +00:00
}
std : : size_t ResultSet : : size ( ) const {
2015-12-30 10:15:36 +00:00
assert ( m_localData - > info . result ) ;
const auto retval = PQntuples ( m_localData - > info . result . get ( ) ) ;
2015-11-11 02:01:37 +00:00
assert ( retval > = 0 ) ;
return static_cast < std : : size_t > ( std : : max ( 0 , retval ) ) ;
}
bool ResultSet : : empty ( ) const {
return size ( ) = = 0 ;
}
ResultSet : : const_iterator ResultSet : : begin ( ) const {
return boost : : make_transform_iterator (
boost : : counting_iterator < std : : size_t > ( 0 ) ,
boost : : bind ( static_cast < Row ( ResultSet : : * ) ( std : : size_t ) const > ( & ResultSet : : operator [ ] ) , this , _1 )
) ;
}
ResultSet : : const_iterator ResultSet : : end ( ) const {
return boost : : make_transform_iterator (
boost : : counting_iterator < std : : size_t > ( size ( ) ) ,
boost : : bind ( static_cast < Row ( ResultSet : : * ) ( std : : size_t ) const > ( & ResultSet : : operator [ ] ) , this , _1 )
) ;
}
} //namespace pq