src/terralib/postgis/PreparedQuery.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2008 National Institute For Space Research (INPE) - Brazil.
2 
3  This file is part of the TerraLib - a Framework for building GIS enabled applications.
4 
5  TerraLib is free software: you can redistribute it and/or modify
6  it under the terms of the GNU Lesser General Public License as published by
7  the Free Software Foundation, either version 3 of the License,
8  or (at your option) any later version.
9 
10  TerraLib is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU Lesser General Public License for more details.
14 
15  You should have received a copy of the GNU Lesser General Public License
16  along with TerraLib. See COPYING. If not, write to
17  TerraLib Team at <terralib-team@terralib.org>.
18  */
19 
20 /*!
21  \file terralib/postgis/PreparedQuery.cpp
22 
23  \brief A class that implements a prepared query for PostgreSQL data access driver.
24 */
25 
26 // TerraLib
27 #include "../Defines.h"
28 #include "../common/StringUtils.h"
29 #include "../core/translator/Translator.h"
30 #include "../dataaccess/dataset/DataSet.h"
31 #include "../dataaccess/dataset/DataSetType.h"
32 #include "../dataaccess/query/Query.h"
33 #include "../datatype/ByteArray.h"
34 #include "../datatype/DateTime.h"
35 #include "../geometry/Geometry.h"
36 #include "Connection.h"
37 #include "DataSet.h"
38 #include "DataSource.h"
39 #include "Exception.h"
40 #include "EWKBWriter.h"
41 #include "PreparedQuery.h"
42 #include "ScopedConnection.h"
43 #include "SQLVisitor.h"
44 #include "Transactor.h"
45 #include "Utils.h"
46 
47 // STL
48 #include <cassert>
49 #include <cstring>
50 
51 // Boost
52 #include <boost/cstdint.hpp>
53 #include <boost/format.hpp>
54 #include <boost/lexical_cast.hpp>
55 
56 // libpq
57 #include <libpq-fe.h>
58 
59 namespace te
60 {
61  namespace pgis
62  {
63  inline void BindValue(te::pgis::PreparedQuery* pq, te::da::DataSet* d, std::size_t i, std::size_t propertyPos)
64  {
65  if(d->isNull(propertyPos))
66  return;
67 
68  const int propertyDataType = d->getPropertyDataType(propertyPos);
69 
70  switch(propertyDataType)
71  {
72  case te::dt::CHAR_TYPE :
73  pq->bind((int)i, d->getChar(propertyPos));
74  break;
75 
76  case te::dt::INT16_TYPE :
77  pq->bind((int)i, d->getInt16(propertyPos));
78  break;
79 
80  case te::dt::INT32_TYPE :
81  pq->bind((int)i, d->getInt32(propertyPos));
82  break;
83 
84  case te::dt::INT64_TYPE :
85  pq->bind((int)i, d->getInt64(propertyPos));
86  break;
87 
89  pq->bind((int)i, d->getBool(propertyPos));
90  break;
91 
92  case te::dt::FLOAT_TYPE :
93  pq->bind((int)i, d->getFloat(propertyPos));
94  break;
95 
96  case te::dt::DOUBLE_TYPE :
97  pq->bind((int)i, d->getDouble(propertyPos));
98  break;
99 
100  case te::dt::NUMERIC_TYPE :
101  pq->bindNumeric((int)i, d->getNumeric(propertyPos));
102  break;
103 
104  case te::dt::STRING_TYPE :
105  pq->bind((int)i, d->getString(propertyPos));
106  break;
107 
109  {
110  std::unique_ptr<te::dt::ByteArray> ba(d->getByteArray(propertyPos));
111  pq->bind((int)i, *ba);
112  }
113  break;
114 
115  case te::dt::GEOMETRY_TYPE :
116  {
117  std::unique_ptr<te::gm::Geometry> geom(d->getGeometry(propertyPos));
118  pq->bind((int)i, *geom);
119  }
120  break;
121 
122  case te::dt::DATETIME_TYPE :
123  {
124  std::unique_ptr<te::dt::DateTime> dt(d->getDateTime(propertyPos));
125  pq->bind((int)i, *dt);
126  }
127  break;
128 
129  default :
130  throw Exception(TE_TR("The TerraLib data type is not supported by the PostgreSQL driver!"));
131  }
132  }
133 
134  } // end namespace pgis
135 } // end namespace te
136 
138  : m_t(t),
139  m_result(nullptr),
140  m_paramValues(nullptr),
141  m_paramLengths(nullptr),
142  m_paramFormats(nullptr),
143  m_nparams(0),
144  m_qname(pqname)
145 {
147 }
148 
150 {
151  try
152  {
153  clear();
154  }
155  catch(...)
156  {
157  }
158 }
159 
161 {
162  return m_qname;
163 }
164 
165 void te::pgis::PreparedQuery::prepare(const te::da::Query& query, const std::vector<te::dt::Property*>& paramTypes)
166 {
168  assert(m_t && pgds && pgds->getDialect());
169  std::string sql;
170 
171  SQLVisitor visitor(*(pgds->getDialect()), sql, m_t->getConnection()->getConn());
172  query.accept(visitor);
173 
174  prepare(sql, paramTypes);
175 }
176 
178  const std::string& /*query*/,
179  const std::vector<te::dt::Property*>& /*queryParams*/)
180 {
181  throw Exception(TE_TR("Not implemented yet!"));
182 }
183 
185 {
186  PQclear(m_result);
187 
189  ScopedConnection scopedConnection(ds->getConnPool(), m_t->getConnectionID());
190 
191  m_result = PQexecPrepared(scopedConnection->getConn(), m_qname.c_str(), (int)m_nparams, m_paramValues, m_paramLengths, m_paramFormats, 1);
192 
193 // release param values data and set it to a null value
194  for(std::size_t i = 0; i < m_nparams; ++i)
195  {
196  delete [] (m_paramValues[i]);
197  m_paramValues[i] = nullptr;
198  m_paramLengths[i] = 0;
199  }
200 
201 // check result
202  if((PQresultStatus(m_result) != PGRES_COMMAND_OK) &&
203  (PQresultStatus(m_result) != PGRES_TUPLES_OK))
204  {
205  boost::format errmsg(TE_TR("Could not execute the prepared query due to the following error: %1%."));
206 
207  errmsg = errmsg % PQerrorMessage(scopedConnection->getConn());
208 
209  throw Exception(errmsg.str());
210  }
211 }
212 
214  te::common::AccessPolicy /*rwRole*/)
215 {
216  execute();
217 
219 
220  std::vector<int> ptypes;
222 
223  DataSet* dataset = new DataSet(m_result, std::vector<int>(), ds->isTimeAnInteger());
224 
225  m_result = nullptr;
226 
227  return dataset;
228 }
229 
230 void te::pgis::PreparedQuery::bind(int i, char value)
231 {
232  m_paramLengths[i] = sizeof(char);
233  m_paramFormats[i] = 1;
234 
235  if(m_paramValues[i] == nullptr)
236  m_paramValues[i] = new char[sizeof(char)];
237 
238  memcpy(m_paramValues[i], &value, sizeof(char));
239 }
240 
241 void te::pgis::PreparedQuery::bind(int /*i*/, unsigned char /*value*/)
242 {
243  throw Exception(TE_TR("The TerraLib unsigned char data type is not supported by PostgreSQL data access driver!"));
244 }
245 
246 void te::pgis::PreparedQuery::bind(int i, boost::int16_t value)
247 {
248  m_paramLengths[i] = sizeof(boost::int16_t);
249  m_paramFormats[i] = 1;
250 
251  if(m_paramValues[i] == nullptr)
252  m_paramValues[i] = new char[sizeof(boost::int16_t)];
253 
254  memcpy(m_paramValues[i], &value, sizeof(boost::int16_t));
255 
256 #if TE_MACHINE_BYTE_ORDER == TE_NDR
258 #endif
259 }
260 
261 void te::pgis::PreparedQuery::bind(int i, boost::int32_t value)
262 {
263  m_paramLengths[i] = sizeof(boost::int32_t);
264  m_paramFormats[i] = 1;
265 
266  if(m_paramValues[i] == nullptr)
267  m_paramValues[i] = new char[sizeof(boost::int32_t)];
268 
269  memcpy(m_paramValues[i], &value, sizeof(boost::int32_t));
270 
271 #if TE_MACHINE_BYTE_ORDER == TE_NDR
273 #endif
274 }
275 
276 void te::pgis::PreparedQuery::bind(int i, boost::int64_t value)
277 {
278  m_paramLengths[i] = sizeof(boost::int64_t);
279  m_paramFormats[i] = 1;
280 
281  if(m_paramValues[i] == nullptr)
282  m_paramValues[i] = new char[sizeof(boost::int64_t)];
283 
284  memcpy(m_paramValues[i], &value, sizeof(boost::int64_t));
285 
286 #if TE_MACHINE_BYTE_ORDER == TE_NDR
288 #endif
289 }
290 
291 void te::pgis::PreparedQuery::bind(int i, bool value)
292 {
293  m_paramLengths[i] = sizeof(char);
294  m_paramFormats[i] = 1;
295 
296  if(m_paramValues[i] == nullptr)
297  m_paramValues[i] = new char[sizeof(char)];
298 
299  char bvalue = value ? 1 : 0;
300 
301  memcpy(m_paramValues[i], &bvalue, sizeof(char));
302 }
303 
304 void te::pgis::PreparedQuery::bind(int i, float value)
305 {
306  m_paramLengths[i] = sizeof(float);
307  m_paramFormats[i] = 1;
308 
309  if(m_paramValues[i] == nullptr)
310  m_paramValues[i] = new char[sizeof(float)];
311 
312  memcpy(m_paramValues[i], &value, sizeof(float));
313 
314 #if TE_MACHINE_BYTE_ORDER == TE_NDR
316 #endif
317 }
318 
319 void te::pgis::PreparedQuery::bind(int i, double value)
320 {
321  m_paramLengths[i] = sizeof(double);
322  m_paramFormats[i] = 1;
323 
324  if(m_paramValues[i] == nullptr)
325  m_paramValues[i] = new char[sizeof(double)];
326 
327  memcpy(m_paramValues[i], &value, sizeof(double));
328 
329 #if TE_MACHINE_BYTE_ORDER == TE_NDR
331 #endif
332 }
333 
334 void te::pgis::PreparedQuery::bindNumeric(int i, const std::string& value)
335 {
336  delete [] (m_paramValues[i]);
337 
338  m_paramValues[i] = new char[value.length() + 1];
339 
340  memcpy(m_paramValues[i], value.c_str(), value.length() + 1);
341 
342  //m_paramLengths[i] = static_cast<int>(value.length()); // we don't need to inform the length
343  m_paramFormats[i] = 0;
344 }
345 
346 void te::pgis::PreparedQuery::bind(int i, const std::string& value)
347 {
348  delete [] (m_paramValues[i]);
349 
350  m_paramValues[i] = new char[value.length() + 1];
351 
352  memcpy(m_paramValues[i], value.c_str(), value.length() + 1);
353 
354  //m_paramLengths[i] = static_cast<int>(value.length()); // we don't need to inform the length
355  m_paramFormats[i] = 0;
356 }
357 
359 {
360  delete [] (m_paramValues[i]);
361 
362  m_paramValues[i] = new char[value.bytesUsed()];
363 
364  memcpy(m_paramValues[i], value.getData(), value.bytesUsed());
365 
366  m_paramLengths[i] = (int)value.bytesUsed();
367  m_paramFormats[i] = 1;
368 }
369 
371 {
372  delete [] (m_paramValues[i]);
373 
374  m_paramValues[i] = nullptr;
375 
376  m_paramFormats[i] = 1;
377 
378  std::size_t ewkbsize = value.getWkbSize() + 4;
379 
380  m_paramValues[i] = new char[ewkbsize];
381 
382  EWKBWriter::write(&value, m_paramValues[i]);
383 
384  m_paramLengths[i] = static_cast<int>(ewkbsize);
385 }
386 
387 void te::pgis::PreparedQuery::bind(int /*i*/, const te::rst::Raster& /*value*/)
388 {
389  throw Exception(TE_TR("Not implemented yet!"));
390 }
391 
393 {
394  //if(dynamic_cast<const te::dt::Date*>(&value) != 0)
395  //{
396  // m_paramTypes[i] = PG_DATE_TYPE;
397  //}
398  //else if(dynamic_cast<const te::dt::TimeDuration*>(&value) != 0)
399  //{
400  // m_paramTypes[i] = PG_TIME_TYPE;
401  //}
402  //else if(dynamic_cast<const te::dt::TimeInstant*>(&value) != 0)
403  //{
404  // m_paramTypes[i] = PG_TIMESTAMP_TYPE;
405  //}
406  //else
407  //{
408  // m_paramTypes[i] = PG_TIMESTAMPTZ_TYPE;
409  //}
410 
411  delete [] (m_paramValues[i]);
412 
413  std::string dvalue = value.toString();
414 
415  m_paramValues[i] = new char[dvalue.length() + 1];
416 
417  memcpy(m_paramValues[i], dvalue.c_str(), dvalue.length() + 1);
418 
419  m_paramLengths[i] = (int)dvalue.length() + 1;
420 }
421 
422 void te::pgis::PreparedQuery::bind(int /*i*/, const te::da::DataSet& /*value*/)
423 {
424  throw Exception(TE_TR("Not implemented yet!"));
425 }
426 
428 {
429  throw Exception(TE_TR("Not implemented yet!"));
430 }
431 
433 {
434  return m_t;
435 }
436 
437 void te::pgis::PreparedQuery::prepare(const std::string& query, const std::vector<int>& paramTypes)
438 {
439 // clear any previous prepared query
440  clear();
441 
442 // create parameters of prepared query
443  m_nparams = paramTypes.size();
444 
445  m_paramValues = new char*[m_nparams];
446 
447  memset(m_paramValues, 0, m_nparams * sizeof(char*));
448 
449  m_paramLengths = new int[m_nparams];
450 
451  memset(m_paramLengths, 0, m_nparams * sizeof(int));
452 
453  m_paramFormats = new int[m_nparams];
454 
455  memset(m_paramFormats, 0, m_nparams * sizeof(int));
456 
458  ScopedConnection scopedConnection(ds->getConnPool(), m_t->getConnectionID());
459 
460 // make prepared query
461  m_result = PQprepare(scopedConnection->getConn(), m_qname.c_str(), query.c_str(), (int)m_nparams, nullptr);
462 
463 // check result
464  if((PQresultStatus(m_result) != PGRES_COMMAND_OK) &&
465  (PQresultStatus(m_result) != PGRES_TUPLES_OK))
466  {
467  boost::format errmsg(TE_TR("Could not create the prepared query due to the following error: %1%."));
468 
469  errmsg = errmsg % PQerrorMessage(scopedConnection->getConn());
470 
471  throw Exception(errmsg.str());
472  }
473 }
474 
475 void te::pgis::PreparedQuery::bind(const std::vector<std::size_t>& propertiesPos, std::size_t offset, te::da::DataSet* d)
476 {
477  const std::size_t nparams = propertiesPos.size();
478 
479  for(std::size_t i = 0; i < nparams; ++i)
480  BindValue(this, d, i + offset, propertiesPos[i]);
481 }
482 
483 void te::pgis::PreparedQuery::bind(const std::vector<std::size_t>& propertiesPos, te::da::DataSet* d)
484 {
485  const std::size_t nparams = propertiesPos.size();
486 
487  for(std::size_t i = 0; i < nparams; ++i)
488  BindValue(this, d, i, propertiesPos[i]);
489 }
490 
492 {
493  for(std::size_t i = 0; i < m_nparams; ++i)
494  BindValue(this, d, i, i);
495 }
496 
498 {
499  if(m_nparams == 0)
500  return;
501 
502 // release prepared statement
503  m_t->execute("DEALLOCATE PREPARE " + m_qname);
504 
505 // release any pending result
506  PQclear(m_result);
507 
508  m_result = nullptr;
509 
510 // release param values
511  for(std::size_t i = 0; i < m_nparams; ++i)
512  delete [] (m_paramValues[i]);
513 
514  delete [] m_paramValues;
515 
516  m_paramValues = nullptr;
517 
518 // release param lengths
519  delete [] m_paramLengths;
520 
521  m_paramLengths = nullptr;
522 
523 // release param formats
524  delete [] m_paramFormats;
525 
526  m_paramFormats = nullptr;
527 
528 // don't forget to reset number of params!
529  m_nparams = 0;
530 }
531 
532 
virtual std::unique_ptr< te::gm::Geometry > getGeometry(std::size_t i) const =0
Method for retrieving a geometric attribute value.
virtual double getDouble(std::size_t i) const =0
Method for retrieving a double attribute value.
A visitor for building an SQL statement using PostGIS dialect.
virtual std::unique_ptr< te::dt::ByteArray > getByteArray(std::size_t i) const =0
Method for retrieving a byte array.
A class that control the use of connection to a PostgreSQL database.
std::size_t bytesUsed() const
It returns the number of used bytes in the internal buffer.
Definition: ByteArray.cpp:169
std::string Convert2LCase(const std::string &value)
It converts a string to lower case.
Definition: StringUtils.h:202
Base exception class for plugin module.
void bindNumeric(int i, const std::string &value)
Binds the i-th parameter of the query to a value.
virtual std::string getNumeric(std::size_t i) const =0
Method for retrieving a numeric attribute value.
virtual float getFloat(std::size_t i) const =0
Method for retrieving a float attribute value.
void write(const te::gm::Geometry *geom)
It serializes the geometry to an EWKB representation into the specified buffer.
void Swap8Bytes(char *v)
It swaps an array of eight bytes in local.
An utility class for writing a PostGIS EWKB.
Utility functions for PostgreSQL.
unsigned int getRasterTypeId() const
It returns the type id associated to the PostGIS Raster type.
A visitor for building an SQL statement using PostGIS dialect.
void bind(int i, char value)
Binds the i-th parameter of the query to a value.
te::da::DataSourceTransactor * getTransactor() const
It returns a pointer to the underlying data source transactor.
virtual ReturnType accept(VisitorType &guest) const =0
It call the visit method from the guest object.
static te::dt::Date ds(2010, 01, 01)
A Transactor can be viewed as a connection to the data source for reading/writing things into it...
#define TE_TR(message)
It marks a string in order to get translated.
Definition: Translator.h:242
std::size_t getWkbSize() const _NOEXCEPT_OP(true)
It returns the size required by a WKB representation for this geometric object.
te::da::DataSet * query(te::common::TraverseType travType=te::common::FORWARDONLY, te::common::AccessPolicy rwRole=te::common::RAccess)
void Swap2Bytes(T &v)
It swaps two bytes in local.
Definition: ByteSwapUtils.h:51
te::da::DataSource * getDataSource() const
It returns the parent data source of the transactor.
The transactor class for the PostGIS driver.
AccessPolicy
Supported data access policies (can be used as bitfield).
TraverseType
A dataset can be traversed in two ways:
virtual std::string toString() const =0
It returns the data value in a string notation.
An exception class for the PostGIS driver.
virtual boost::int16_t getInt16(std::size_t i) const =0
Method for retrieving a 16-bit integer attribute value (2 bytes long).
PGconn * getConn() const
It gets the underlying PGconn object.
virtual int getPropertyDataType(std::size_t i) const =0
It returns the underlying data type of the property at position pos.
std::string getName() const
It returns the prepared query name.
void Swap4Bytes(T &v)
It swaps four bytes in local.
Definition: ByteSwapUtils.h:82
Implementation of a dataset for the PostGIS driver.
An abstract class for raster data strucutures.
char * getData() const
It returns the data array.
Definition: ByteArray.cpp:105
URI C++ Library.
Definition: Attributes.h:37
virtual boost::int32_t getInt32(std::size_t i) const =0
Method for retrieving a 32-bit integer attribute value (4 bytes long).
void DataSet()
static te::dt::DateTime d(2010, 8, 9, 15, 58, 39)
static te::dt::TimeDuration dt(20, 30, 50, 11)
A base class for values that can be retrieved from the data access module.
Definition: AbstractData.h:57
const te::da::SQLDialect * getDialect() const
It returns the data source SQL dialect, if there is one.
A DataSourceTransactor can be viewed as a connection to the data source for reading/writing things in...
Geometry is the root class of the geometries hierarchy, it follows OGC and ISO standards.
A class that implements a connection to a PostgreSQL database.
virtual std::unique_ptr< te::dt::DateTime > getDateTime(std::size_t i) const =0
Method for retrieving a date and time attribute value.
A dataset is the unit of information manipulated by the data access module of TerraLib.
virtual char getChar(std::size_t i) const =0
Method for retrieving a signed character attribute value (1 byte long).
Connection * getConnection(const int &id=-1) const
It returns the underlying connection.
A class that control the use of the connection to a PostgreSQL database.
ConnectionPool * getConnPool() const
It returns a pointer to the internal connection pool.
int getConnectionID() const
It returns the underlying connection ID.
void prepare(const te::da::Query &query, const std::vector< te::dt::Property * > &paramTypes)
It prepares the query that may be used for commands that are used mutiple times (select, insert, update and delete).
te::dt::Property * Convert2TerraLib(unsigned int attNum, const char *attName, unsigned int attType, bool attNotNull, const char *fmt, bool attHasDefault, const char *attDefValue, unsigned int pgisGeomTypeOid, unsigned int pgisRasterTypeOid)
It creates a PropertyType from a PostgreSQL attribute description.
void execute(const te::da::Query &command)
It executes the specified command using a generic query representation.
virtual bool getBool(std::size_t i) const =0
Method for retrieving a boolean attribute value.
virtual bool isNull(std::size_t i) const =0
It checks if the attribute value is NULL.
virtual boost::int64_t getInt64(std::size_t i) const =0
Method for retrieving a 64-bit integer attribute value (8 bytes long).
void BindValue(te::pgis::PreparedQuery *pq, te::da::DataSet *d, std::size_t i, std::size_t propertyPos)
A dataset is the unit of information manipulated by the data access module of TerraLib.
A class that implements a prepared query for PostgreSQL data access driver.
Implementation of the data source for the PostGIS driver.
A class that implements a prepared query for PostgreSQL data access driver.
A Query is independent from the data source language/dialect.
Definition: Query.h:46
virtual std::string getString(std::size_t i) const =0
Method for retrieving a string value attribute.
A class for representing binary data.
Definition: ByteArray.h:51
unsigned int getGeomTypeId() const
It returns the type id associated to the PostGIS Geometry type.