PageRenderTime 125ms CodeModel.GetById 40ms app.highlight 58ms RepoModel.GetById 22ms app.codeStats 0ms

/mordor/pq/result.cpp

http://github.com/mozy/mordor
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}}