fastcgi++  3.1alpha
A C++ FastCGI/Web API
results.cpp
Go to the documentation of this file.
1 
10 /*******************************************************************************
11 * Copyright (C) 2020 Eddie Carle [eddie@isatec.ca] *
12 * *
13 * This file is part of fastcgi++. *
14 * *
15 * fastcgi++ is free software: you can redistribute it and/or modify it under *
16 * the terms of the GNU Lesser General Public License as published by the Free *
17 * Software Foundation, either version 3 of the License, or (at your option) *
18 * any later version. *
19 * *
20 * fastcgi++ is distributed in the hope that it will be useful, but WITHOUT ANY *
21 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS *
22 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for *
23 * more details. *
24 * *
25 * You should have received a copy of the GNU Lesser General Public License *
26 * along with fastcgi++. If not, see <http://www.gnu.org/licenses/>. *
27 *******************************************************************************/
28 
30 #include "fastcgi++/endian.hpp"
31 #include "fastcgi++/log.hpp"
32 #include "sqlTraits.hpp"
33 
34 #include <locale>
35 #include <codecvt>
36 #include <cstdio>
37 
38 // Column verification
39 
40 template<typename T>
42 {
43  return Traits<T>::verifyType(m_res, column);
44 }
45 template bool Fastcgipp::SQL::Results_base::verifyColumn<bool>(
46  int column) const;
47 template bool Fastcgipp::SQL::Results_base::verifyColumn<int16_t>(
48  int column) const;
49 template bool Fastcgipp::SQL::Results_base::verifyColumn<int32_t>(
50  int column) const;
51 template bool Fastcgipp::SQL::Results_base::verifyColumn<int64_t>(
52  int column) const;
53 template bool Fastcgipp::SQL::Results_base::verifyColumn<float>(
54  int column) const;
55 template bool Fastcgipp::SQL::Results_base::verifyColumn<double>(
56  int column) const;
57 template bool
58 Fastcgipp::SQL::Results_base::verifyColumn<std::chrono::time_point<std::chrono::system_clock>>(
59  int column) const;
60 template bool Fastcgipp::SQL::Results_base::verifyColumn<Fastcgipp::Address>(
61  int column) const;
62 template bool Fastcgipp::SQL::Results_base::verifyColumn<std::string>(
63  int column) const;
64 template bool Fastcgipp::SQL::Results_base::verifyColumn<std::wstring>(
65  int column) const;
66 template bool Fastcgipp::SQL::Results_base::verifyColumn<std::vector<char>>(
67  int column) const;
68 template bool Fastcgipp::SQL::Results_base::verifyColumn<std::vector<int16_t>>(
69  int column) const;
70 template bool Fastcgipp::SQL::Results_base::verifyColumn<std::vector<int32_t>>(
71  int column) const;
72 template bool Fastcgipp::SQL::Results_base::verifyColumn<std::vector<int64_t>>(
73  int column) const;
74 template bool Fastcgipp::SQL::Results_base::verifyColumn<std::vector<float>>(
75  int column) const;
76 template bool Fastcgipp::SQL::Results_base::verifyColumn<std::vector<double>>(
77  int column) const;
78 template bool Fastcgipp::SQL::Results_base::verifyColumn<std::vector<std::string>>(
79  int column) const;
80 template bool Fastcgipp::SQL::Results_base::verifyColumn<std::vector<std::wstring>>(
81  int column) const;
82 
83 // Non-array field return
84 
85 template<typename Numeric> void Fastcgipp::SQL::Results_base::field(
86  int row,
87  int column,
88  Numeric& value) const
89 {
90  static_assert(
91  std::is_integral<Numeric>::value ||
92  std::is_floating_point<Numeric>::value,
93  "Numeric must be a numeric type.");
95  PQgetvalue(reinterpret_cast<const PGresult*>(m_res), row, column));
96 }
97 template void Fastcgipp::SQL::Results_base::field<int16_t>(
98  int row,
99  int column,
100  int16_t& value) const;
101 template void Fastcgipp::SQL::Results_base::field<int32_t>(
102  int row,
103  int column,
104  int32_t& value) const;
105 template void Fastcgipp::SQL::Results_base::field<int64_t>(
106  int row,
107  int column,
108  int64_t& value) const;
109 template void Fastcgipp::SQL::Results_base::field<float>(
110  int row,
111  int column,
112  float& value) const;
113 template void Fastcgipp::SQL::Results_base::field<double>(
114  int row,
115  int column,
116  double& value) const;
117 
118 // Non-array field return specializations
119 
120 template<> void Fastcgipp::SQL::Results_base::field<bool>(
121  int row,
122  int column,
123  bool& value) const
124 {
125  value = static_cast<bool>(
126  *PQgetvalue(reinterpret_cast<const PGresult*>(m_res), row, column));
127 }
128 
129 template<> void Fastcgipp::SQL::Results_base::field<std::string>(
130  int row,
131  int column,
132  std::string& value) const
133 {
134  value.assign(
135  PQgetvalue(reinterpret_cast<const PGresult*>(m_res), row, column),
136  PQgetlength(reinterpret_cast<const PGresult*>(m_res), row, column));
137 }
138 
139 template<> void Fastcgipp::SQL::Results_base::field<std::wstring>(
140  int row,
141  int column,
142  std::wstring& value) const
143 {
144  std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
145  const char* const start = PQgetvalue(
146  reinterpret_cast<const PGresult*>(m_res),
147  row,
148  column);
149  const char* const end = start+PQgetlength(
150  reinterpret_cast<const PGresult*>(m_res),
151  row,
152  column);
153  try
154  {
155  value = converter.from_bytes(start, end);
156  }
157  catch(const std::range_error& e)
158  {
159  WARNING_LOG("Error in code conversion from utf8 in SQL result")
160  }
161 }
162 
164  std::chrono::time_point<std::chrono::system_clock>>(
165  int row,
166  int column,
167  std::chrono::time_point<std::chrono::system_clock>& value) const
168 {
169  const int64_t count = BigEndian<int64_t>::read(
170  PQgetvalue(reinterpret_cast<const PGresult*>(m_res), row, column));
171 
172  const std::chrono::duration<int64_t, std::micro> duration(count);
173 
174  value = std::chrono::time_point<std::chrono::system_clock>(
175  std::chrono::duration_cast<std::chrono::system_clock::duration>(
176  duration)+std::chrono::seconds(946684800));
177 }
178 
179 template<> void Fastcgipp::SQL::Results_base::field<Fastcgipp::Address>(
180  int row,
181  int column,
182  Address& value) const
183 {
184  char* address_p = reinterpret_cast<char*>(&value);
185  const char* const data = PQgetvalue(
186  reinterpret_cast<const PGresult*>(m_res),
187  row,
188  column);
189 
190  switch(PQgetlength(reinterpret_cast<const PGresult*>(m_res), row, column))
191  {
192  case 8:
193  {
194  address_p = std::fill_n(address_p, 10, char(0));
195  address_p = std::fill_n(address_p, 2, char(-1));
196  std::copy_n(
197  data+4,
198  4,
199  address_p);
200  break;
201  }
202  case 20:
203  {
204  std::copy_n(data+4, Address::size, address_p);
205  break;
206  }
207  }
208 }
209 
210 // Array field returns
211 
212 template<typename Numeric>
214  int row,
215  int column,
216  std::vector<Numeric>& value) const
217 {
218  static_assert(
219  std::is_integral<Numeric>::value ||
220  std::is_floating_point<Numeric>::value,
221  "Numeric must be a numeric type.");
222  const char* const start = PQgetvalue(
223  reinterpret_cast<const PGresult*>(m_res),
224  row,
225  column);
226 
227  const int32_t ndim(*reinterpret_cast<const BigEndian<int32_t>*>(
228  start+0*sizeof(int32_t)));
229  if(ndim != 1)
230  {
231  WARNING_LOG("SQL result array type for std::vector<Numeric> has "\
232  "ndim != 1");
233  return;
234  }
235 
236  const int32_t hasNull(*reinterpret_cast<const BigEndian<int32_t>*>(
237  start+1*sizeof(int32_t)));
238  if(hasNull != 0)
239  {
240  WARNING_LOG("SQL result array type for std::vector<Numeric> has "\
241  "ndim != 0");
242  return;
243  }
244 
245  const int32_t elementType(*reinterpret_cast<const BigEndian<int32_t>*>(
246  start+2*sizeof(int32_t)));
247  if(elementType != Traits<Numeric>::oid)
248  {
249  WARNING_LOG("SQL result array type for std::vector<Numeric> has "\
250  "the wrong element type");
251  return;
252  }
253 
254  const int32_t size(*reinterpret_cast<const BigEndian<int32_t>*>(
255  start+3*sizeof(int32_t)));
256 
257  value.clear();
258  value.reserve(size);
259  for(int i=0; i<size; ++i)
260  {
261  const int32_t length(
262  *reinterpret_cast<const BigEndian<int32_t>*>(
263  start + 5*sizeof(int32_t)
264  + i*(sizeof(int32_t) + sizeof(Numeric))));
265  if(length != sizeof(Numeric))
266  {
267  WARNING_LOG("SQL result array for Numeric has element of wrong size");
268  continue;
269  }
270 
271  value.push_back(*reinterpret_cast<const BigEndian<Numeric>*>(
272  start + 6*sizeof(int32_t)
273  + i*(sizeof(int32_t) + sizeof(Numeric))));
274  }
275 }
276 template void Fastcgipp::SQL::Results_base::field<int16_t>(
277  int row,
278  int column,
279  std::vector<int16_t>& value) const;
280 template void Fastcgipp::SQL::Results_base::field<int32_t>(
281  int row,
282  int column,
283  std::vector<int32_t>& value) const;
284 template void Fastcgipp::SQL::Results_base::field<int64_t>(
285  int row,
286  int column,
287  std::vector<int64_t>& value) const;
288 template void Fastcgipp::SQL::Results_base::field<float>(
289  int row,
290  int column,
291  std::vector<float>& value) const;
292 template void Fastcgipp::SQL::Results_base::field<double>(
293  int row,
294  int column,
295  std::vector<double>& value) const;
296 
297 template<> void Fastcgipp::SQL::Results_base::field<char>(
298  int row,
299  int column,
300  std::vector<char>& value) const
301 {
302  const unsigned size = PQgetlength(
303  reinterpret_cast<const PGresult*>(m_res),
304  row,
305  column);
306  const char* const start = PQgetvalue(
307  reinterpret_cast<const PGresult*>(m_res),
308  row,
309  column);
310  const char* const end = start+size;
311 
312  value.reserve(size);
313  value.assign(start, end);
314 }
315 
316 template<>
317 void Fastcgipp::SQL::Results_base::field<std::string>(
318  int row,
319  int column,
320  std::vector<std::string>& value) const
321 {
322  const char* ptr = PQgetvalue(
323  reinterpret_cast<const PGresult*>(m_res),
324  row,
325  column);
326 
327  const int32_t ndim(*reinterpret_cast<const BigEndian<int32_t>*>(ptr));
328  ptr += sizeof(int32_t);
329  if(ndim != 1)
330  {
331  WARNING_LOG("SQL result array type for std::vector<std::string> has "\
332  "ndim != 1");
333  return;
334  }
335 
336  const int32_t hasNull(*reinterpret_cast<const BigEndian<int32_t>*>(ptr));
337  ptr += sizeof(int32_t);
338  if(hasNull != 0)
339  {
340  WARNING_LOG("SQL result array type for std::vector<std::string> has "\
341  "ndim != 0");
342  return;
343  }
344 
345  const int32_t elementType(*reinterpret_cast<const BigEndian<int32_t>*>(ptr));
346  ptr += sizeof(int32_t);
347  if(elementType != Traits<std::string>::oid)
348  {
349  WARNING_LOG("SQL result array type for std::vector<std::string> has "\
350  "the wrong element type");
351  return;
352  }
353 
354  const int32_t size(*reinterpret_cast<const BigEndian<int32_t>*>(ptr));
355  ptr += 2*sizeof(int32_t);
356 
357  value.clear();
358  value.reserve(size);
359  for(int i=0; i<size; ++i)
360  {
361  const int32_t length(*reinterpret_cast<const BigEndian<int32_t>*>(ptr));
362  ptr += sizeof(int32_t);
363 
364  value.emplace_back(ptr, length);
365  ptr += length;
366  }
367 }
368 
369 template<>
370 void Fastcgipp::SQL::Results_base::field<std::wstring>(
371  int row,
372  int column,
373  std::vector<std::wstring>& value) const
374 {
375  const char* ptr = PQgetvalue(
376  reinterpret_cast<const PGresult*>(m_res),
377  row,
378  column);
379 
380  const int32_t ndim(*reinterpret_cast<const BigEndian<int32_t>*>(ptr));
381  ptr += sizeof(int32_t);
382  if(ndim != 1)
383  {
384  WARNING_LOG("SQL result array type for std::vector<std::string> has "\
385  "ndim != 1");
386  return;
387  }
388 
389  const int32_t hasNull(*reinterpret_cast<const BigEndian<int32_t>*>(ptr));
390  ptr += sizeof(int32_t);
391  if(hasNull != 0)
392  {
393  WARNING_LOG("SQL result array type for std::vector<std::string> has "\
394  "ndim != 0");
395  return;
396  }
397 
398  const int32_t elementType(*reinterpret_cast<const BigEndian<int32_t>*>(ptr));
399  ptr += sizeof(int32_t);
400  if(elementType != Traits<std::string>::oid)
401  {
402  WARNING_LOG("SQL result array type for std::vector<std::string> has "\
403  "the wrong element type");
404  return;
405  }
406 
407  const int32_t size(*reinterpret_cast<const BigEndian<int32_t>*>(ptr));
408  ptr += 2*sizeof(int32_t);
409 
410  value.clear();
411  value.reserve(size);
412  std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
413  try
414  {
415  for(int i=0; i<size; ++i)
416  {
417  const int32_t length(*reinterpret_cast<const BigEndian<int32_t>*>(ptr));
418  ptr += sizeof(int32_t);
419 
420  value.emplace_back(std::move(
421  converter.from_bytes(ptr, ptr+length)));
422  ptr += length;
423  }
424  }
425  catch(const std::range_error& e)
426  {
427  WARNING_LOG("Error in array code conversion to utf8 in SQL parameter")
428  }
429 }
430 
431 // Done result fields
432 
434 {
435  if(reinterpret_cast<const PGresult*>(m_res) == nullptr)
436  return Status::noResult;
437 
438  switch(PQresultStatus(reinterpret_cast<const PGresult*>(m_res)))
439  {
440  case PGRES_EMPTY_QUERY:
441  return Status::emptyQuery;
442  case PGRES_COMMAND_OK:
443  return Status::commandOk;
444  case PGRES_TUPLES_OK:
445  return Status::rowsOk;
446  case PGRES_COPY_OUT:
447  return Status::copyOut;
448  case PGRES_COPY_IN:
449  return Status::copyIn;
450  case PGRES_BAD_RESPONSE:
451  return Status::badResponse;
452  case PGRES_NONFATAL_ERROR:
453  return Status::nonfatalError;
454  case PGRES_COPY_BOTH:
455  return Status::copyBoth;
456  case PGRES_SINGLE_TUPLE:
457  return Status::singleTuple;
458  default:
459  return Status::fatalError;
460  };
461 }
462 
464 {
465  return std::atoi(PQcmdTuples(reinterpret_cast<PGresult*>(m_res)));
466 }
467 
469 {
470  if(m_res != nullptr)
471  PQclear(reinterpret_cast<PGresult*>(m_res));
472 }
473 
475 {
476  return PQresultErrorMessage(reinterpret_cast<const PGresult*>(m_res));
477 }
478 
480 {
481  return PQntuples(reinterpret_cast<const PGresult*>(m_res));
482 }
483 
484 bool Fastcgipp::SQL::Results_base::null(int row, int column) const
485 {
486  return static_cast<bool>(PQgetisnull(
487  reinterpret_cast<const PGresult*>(m_res),
488  row,
489  column));
490 }
491 
493 {
494  return PQnfields(reinterpret_cast<const PGresult*>(m_res));
495 }
496 
497 const char* Fastcgipp::SQL::statusString(const Status status)
498 {
499  switch(status)
500  {
501  case Status::noResult:
502  return "No Result";
503  case Status::emptyQuery:
504  return "Empty Query";
505  case Status::commandOk:
506  return "Command OK";
507  case Status::rowsOk:
508  return "Rows OK";
509  case Status::copyOut:
510  return "Copy Out";
511  case Status::copyIn:
512  return "Copy In";
513  case Status::badResponse:
514  return "Bad Response";
516  return "Non-fatal Error";
517  case Status::copyBoth:
518  return "Copy Both";
519  case Status::singleTuple:
520  return "Single Tuple";
521  case Status::fatalError:
522  default:
523  return "Fatal Error";
524  }
525 }
Fastcgipp::SQL::Status::commandOk
@ commandOk
Fastcgipp::BigEndian::read
static constexpr T read(const unsigned char *source) noexcept
Static function for reading the value out of a data array.
Definition: endian.hpp:195
Fastcgipp::SQL::Status::fatalError
@ fatalError
endian.hpp
Defines the BigEndian class.
Fastcgipp::Http::atoi
int atoi(const charT *start, const charT *end)
Convert a char string to an integer.
Definition: http.cpp:60
Fastcgipp::SQL::Status::singleTuple
@ singleTuple
Fastcgipp::SQL::Results_base::field
void field(int row, int column, std::vector< Numeric > &value) const
Extract typed array from specific row and column.
Definition: results.cpp:213
Fastcgipp::SQL::Results_base::null
bool null(int row, int column) const
Check for nullness of a specific row/column.
Definition: results.cpp:484
Fastcgipp::SQL::Results_base::columns
int columns() const
How many columns are associated with the underlying results.
Definition: results.cpp:492
Fastcgipp::SQL::Status
Status
Response type for SQL query results statuses.
Definition: results.hpp:80
Fastcgipp::Address::size
static constexpr size_t size
This is the data length of the IPv6 address.
Definition: address.hpp:87
Fastcgipp::SQL::Status::noResult
@ noResult
Fastcgipp::SQL::Status::nonfatalError
@ nonfatalError
results.hpp
Declares SQL Results types.
WARNING_LOG
#define WARNING_LOG(data)
Log any externally caused "errors".
Definition: log.hpp:124
Fastcgipp::SQL::Results_base::errorMessage
const char * errorMessage() const
Get error message associated with SQL query result.
Definition: results.cpp:474
Fastcgipp::SQL::Status::badResponse
@ badResponse
Fastcgipp::SQL::Status::rowsOk
@ rowsOk
Fastcgipp::SQL::Status::copyIn
@ copyIn
log.hpp
Declares the Fastcgipp debugging/logging facilities.
Fastcgipp::SQL::statusString
const char * statusString(const Status status)
Returns a text description of the specified SQL query result status.
Definition: results.cpp:497
Fastcgipp::SQL::Results_base::affectedRows
unsigned affectedRows() const
How many rows were affected from the SQL query.
Definition: results.cpp:463
Fastcgipp::SQL::Results_base::rows
unsigned rows() const
How many rows were returned with the SQL query.
Definition: results.cpp:479
Fastcgipp::SQL::Status::emptyQuery
@ emptyQuery
Fastcgipp::SQL::Status::copyBoth
@ copyBoth
Fastcgipp::SQL::Results_base::m_res
void * m_res
Pointer to underlying SQL results object.
Definition: results.hpp:130
Fastcgipp::SQL::Results_base::verifyColumn
bool verifyColumn(int column) const
Verify type and size consistency for the specified column.
Definition: results.cpp:41
Fastcgipp::BigEndian< int32_t >
Fastcgipp::SQL::Results_base::~Results_base
virtual ~Results_base()
Definition: results.cpp:468
Fastcgipp::SQL::Status::copyOut
@ copyOut
Fastcgipp::SQL::Results_base::status
Status status() const
Get status of SQL query result.
Definition: results.cpp:433