/mordor/pq/result.cpp
C++ | 246 lines | 214 code | 25 blank | 7 comment | 37 complexity | 7413e76570cb605cb555ee4782cd6201 MD5 | raw file
1// Copyright (c) 2010 - Mozy, Inc. 2 3#include "result.h" 4 5#include <boost/date_time/posix_time/posix_time_types.hpp> 6 7#include "mordor/assert.h" 8#include "mordor/endian.h" 9#include "mordor/socket.h" 10 11#define BOOLOID 16 12#define CHAROID 18 13#define INT8OID 20 14#define INT2OID 21 15#define INT4OID 23 16#define CIDROID 650 17#define FLOAT4OID 700 18#define FLOAT8OID 701 19#define INETOID 869 20#define TIMESTAMPOID 1114 21#define TIMESTAMPTZOID 1184 22#define INT4ARRAYOID 1007 23 24namespace Mordor { 25namespace PQ { 26 27size_t 28Result::rows() const 29{ 30 return (size_t)PQntuples(m_result.get()); 31} 32 33size_t 34Result::columns() const 35{ 36 return (size_t)PQnfields(m_result.get()); 37} 38 39size_t 40Result::column(const char *name) const 41{ 42 return (size_t)PQfnumber(m_result.get(), name); 43} 44 45Oid 46Result::getType(size_t column) const 47{ 48 return PQftype(m_result.get(), (int)column); 49} 50 51bool Result::getIsNull(size_t row, size_t column) const { 52 return PQgetisnull(m_result.get(), (int)row, (int)column) == 1; 53} 54 55template <> 56std::string 57Result::get<std::string>(size_t row, size_t column) const 58{ 59 return std::string(PQgetvalue(m_result.get(), (int)row, (int)column), 60 PQgetlength(m_result.get(), (int)row, (int)column)); 61} 62 63template <> 64const char * 65Result::get<const char *>(size_t row, size_t column) const 66{ 67 return PQgetvalue(m_result.get(), (int)row, (int)column); 68} 69 70template <> 71bool 72Result::get<bool>(size_t row, size_t column) const 73{ 74 MORDOR_ASSERT(getType(column) == BOOLOID); 75 MORDOR_ASSERT(PQgetlength(m_result.get(), (int)row, (int)column) == 1); 76 return !!*PQgetvalue(m_result.get(), (int)row, (int)column); 77} 78 79template <> 80char 81Result::get<char>(size_t row, size_t column) const 82{ 83 MORDOR_ASSERT(getType(column) == CHAROID); 84 MORDOR_ASSERT(PQgetlength(m_result.get(), (int)row, (int)column) == 1); 85 return *PQgetvalue(m_result.get(), (int)row, (int)column); 86} 87 88template <> 89long long 90Result::get<long long>(size_t row, size_t column) const 91{ 92 switch (getType(column)) { 93 case INT8OID: 94 MORDOR_ASSERT(PQgetlength(m_result.get(), (int)row, (int)column) == 8); 95 return byteswapOnLittleEndian(*(long long *)PQgetvalue(m_result.get(), (int)row, (int)column)); 96 case INT2OID: 97 MORDOR_ASSERT(PQgetlength(m_result.get(), (int)row, (int)column) == 2); 98 return byteswapOnLittleEndian(*(short *)PQgetvalue(m_result.get(), (int)row, (int)column)); 99 case INT4OID: 100 MORDOR_ASSERT(PQgetlength(m_result.get(), (int)row, (int)column) == 4); 101 return byteswapOnLittleEndian(*(int *)PQgetvalue(m_result.get(), (int)row, (int)column)); 102 default: 103 MORDOR_NOTREACHED(); 104 } 105} 106 107template <> 108short 109Result::get<short>(size_t row, size_t column) const 110{ 111 MORDOR_ASSERT(getType(column) == INT2OID); 112 MORDOR_ASSERT(PQgetlength(m_result.get(), (int)row, (int)column) == 2); 113 return byteswapOnLittleEndian(*(short *)PQgetvalue(m_result.get(), (int)row, (int)column)); 114} 115 116template <> 117int 118Result::get<int>(size_t row, size_t column) const 119{ 120 switch (getType(column)) { 121 case INT2OID: 122 MORDOR_ASSERT(PQgetlength(m_result.get(), (int)row, (int)column) == 2); 123 return byteswapOnLittleEndian(*(short *)PQgetvalue(m_result.get(), (int)row, (int)column)); 124 case INT4OID: 125 MORDOR_ASSERT(PQgetlength(m_result.get(), (int)row, (int)column) == 4); 126 return byteswapOnLittleEndian(*(int *)PQgetvalue(m_result.get(), (int)row, (int)column)); 127 default: 128 MORDOR_NOTREACHED(); 129 } 130} 131 132template <> 133float 134Result::get<float>(size_t row, size_t column) const 135{ 136 MORDOR_ASSERT(getType(column) == FLOAT4OID); 137 MORDOR_ASSERT(PQgetlength(m_result.get(), (int)row, (int)column) == 4); 138 int temp = byteswapOnLittleEndian(*(int *)PQgetvalue(m_result.get(), (int)row, (int)column)); 139 return *(float *)&temp; 140} 141 142template <> 143double 144Result::get<double>(size_t row, size_t column) const 145{ 146 int templ; 147 long long templl; 148 switch (getType(column)) { 149 case FLOAT4OID: 150 MORDOR_ASSERT(PQgetlength(m_result.get(), (int)row, (int)column) == 4); 151 templ = byteswapOnLittleEndian(*(int *)PQgetvalue(m_result.get(), (int)row, (int)column)); 152 return *(float *)&templ; 153 case FLOAT8OID: 154 MORDOR_ASSERT(PQgetlength(m_result.get(), (int)row, (int)column) == 8); 155 templl = byteswapOnLittleEndian(*(long long *)PQgetvalue(m_result.get(), (int)row, (int)column)); 156 return *(double *)&templl; 157 default: 158 MORDOR_NOTREACHED(); 159 } 160} 161 162static const boost::posix_time::ptime postgres_epoch(boost::gregorian::date(2000, 1, 1)); 163 164template<> 165boost::posix_time::ptime 166Result::get<boost::posix_time::ptime>(size_t row, size_t column) const 167{ 168 MORDOR_ASSERT(getType(column) == TIMESTAMPOID || 169 getType(column) == TIMESTAMPTZOID); 170 if (PQgetlength(m_result.get(), (int)row, (int)column) == 0) 171 return boost::posix_time::ptime(); 172 MORDOR_ASSERT(PQgetlength(m_result.get(), (int)row, (int)column) == 8); 173 long long microseconds = byteswapOnLittleEndian(*(long long *)PQgetvalue(m_result.get(), (int)row, (int)column)); 174 return postgres_epoch + 175 boost::posix_time::seconds((long)(microseconds / 1000000)) + 176 boost::posix_time::microseconds(microseconds % 1000000); 177} 178 179template<> 180std::vector<int> 181Result::get<std::vector<int> >(size_t row, size_t column) const 182{ 183 std::vector<int> result; 184 MORDOR_ASSERT(getType(column) == INT4ARRAYOID); 185 MORDOR_ASSERT(PQgetlength(m_result.get(), (int)row, (int)column) >= 12); 186 const int *array = (const int *)PQgetvalue(m_result.get(), (int)row, (int)column); 187 // No embedded NULLs 188 MORDOR_ASSERT(array[1] == 0); 189 // Correct element type 190 MORDOR_ASSERT(byteswapOnLittleEndian(array[2]) == INT4OID); 191 // Number of dimensions 192 switch (byteswapOnLittleEndian(array[0])) { 193 case 0: 194 return result; 195 case 1: 196 MORDOR_ASSERT(PQgetlength(m_result.get(), (int)row, (int)column) >= 20); 197 break; 198 default: 199 MORDOR_NOTREACHED(); 200 } 201 int numberOfElements = byteswapOnLittleEndian(array[3]); 202 // Ignore starting index 203 array = &array[5]; 204 // Now verify we have the entire array, as described 205 MORDOR_ASSERT(PQgetlength(m_result.get(), (int)row, (int)column) == 20 + numberOfElements * 8); 206 result.resize(numberOfElements); 207 for (int i = 0; i < numberOfElements; ++i) { 208 // Correct element size 209 MORDOR_ASSERT(byteswapOnLittleEndian(array[i * 2]) == 4); 210 result[i] = byteswapOnLittleEndian(array[i * 2 + 1]); 211 } 212 return result; 213} 214 215template<> 216std::pair<IPAddress::ptr, unsigned int> 217Result::get<std::pair<IPAddress::ptr, unsigned int> >(size_t row, size_t column) const 218{ 219 MORDOR_ASSERT(getType(column) == INETOID || getType(column) == CIDROID); 220 MORDOR_ASSERT(PQgetlength(m_result.get(), (int)row, (int)column) >= 2); 221 const char *bytes = PQgetvalue(m_result.get(), (int)row, (int)column); 222 std::pair<IPAddress::ptr, unsigned int> result; 223 switch (bytes[0]) { 224 case AF_INET: 225 MORDOR_ASSERT(PQgetlength(m_result.get(), (int)row, (int)column) == 8); 226 result.second = bytes[1]; 227 result.first.reset(new IPv4Address(byteswapOnLittleEndian(*(unsigned int*)&bytes[4]))); 228 return result; 229 case AF_INET + 1: 230 MORDOR_ASSERT(PQgetlength(m_result.get(), (int)row, (int)column) == 20); 231 result.second = bytes[1]; 232 result.first.reset(new IPv6Address((const unsigned char *)&bytes[4])); 233 return result; 234 default: 235 MORDOR_NOTREACHED(); 236 } 237} 238 239template<> 240IPAddress::ptr 241Result::get<IPAddress::ptr>(size_t row, size_t column) const 242{ 243 return get<std::pair<IPAddress::ptr, unsigned int> >(row, column).first; 244} 245 246}}