KDb

PostgresqlCursor.cpp
1 /* This file is part of the KDE project
2  Copyright (C) 2003 Adam Pigg <[email protected]>
3  Copyright (C) 2016 JarosÅ‚aw Staniek <[email protected]>
4 
5  This program is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Library General Public
7  License as published by the Free Software Foundation; either
8  version 2 of the License, or (at your option) any later version.
9 
10  This program 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 GNU
13  Library General Public License for more details.
14 
15  You should have received a copy of the GNU Library General Public License
16  along with this program; see the file COPYING. If not, write to
17  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19 */
20 
21 #include "PostgresqlCursor.h"
22 #include "PostgresqlConnection.h"
23 #include "PostgresqlConnection_p.h"
24 #include "PostgresqlDriver.h"
25 #include "postgresql_debug.h"
26 
27 #include "KDbError.h"
28 #include "KDbGlobal.h"
29 #include "KDbRecordData.h"
30 
31 // Constructor based on query statement
32 PostgresqlCursor::PostgresqlCursor(KDbConnection* conn, const KDbEscapedString& sql,
33  KDbCursor::Options options)
34  : KDbCursor(conn, sql, options | KDbCursor::Option::Buffered)
35  , m_numRows(0)
36  , d(new PostgresqlCursorData(conn))
37 {
38 }
39 
40 //==================================================================================
41 //Constructor base on query object
42 PostgresqlCursor::PostgresqlCursor(KDbConnection* conn, KDbQuerySchema* query,
43  KDbCursor::Options options)
44  : KDbCursor(conn, query, options | KDbCursor::Option::Buffered)
45  , m_numRows(0)
46  , d(new PostgresqlCursorData(conn))
47 {
48 }
49 
50 //==================================================================================
51 //Destructor
52 PostgresqlCursor::~PostgresqlCursor()
53 {
54  close();
55  delete d;
56 }
57 
58 //==================================================================================
59 //Create a cursor result set
60 bool PostgresqlCursor::drv_open(const KDbEscapedString& sql)
61 {
62  d->res = d->executeSql(sql);
63  d->resultStatus = PQresultStatus(d->res);
64  if (d->resultStatus != PGRES_TUPLES_OK && d->resultStatus != PGRES_COMMAND_OK) {
65  storeResultAndClear(&d->res, d->resultStatus);
66  return false;
67  }
68  m_fieldsToStoreInRecord = PQnfields(d->res);
69  m_fieldCount = m_fieldsToStoreInRecord - (containsRecordIdInfo() ? 1 : 0);
70  m_numRows = PQntuples(d->res);
71  m_records_in_buf = m_numRows;
72  m_buffering_completed = true;
73 
74  // get real types for all fields
75  PostgresqlDriver* drv = static_cast<PostgresqlDriver*>(connection()->driver());
76 
77  m_realTypes.resize(m_fieldsToStoreInRecord);
78  m_realLengths.resize(m_fieldsToStoreInRecord);
79  for (int i = 0; i < int(m_fieldsToStoreInRecord); i++) {
80  const int pqtype = PQftype(d->res, i);
81  const int pqfmod = PQfmod(d->res, i);
82  m_realTypes[i] = drv->pgsqlToKDbType(pqtype, pqfmod, &m_realLengths[i]);
83  }
84  return true;
85 }
86 
87 //==================================================================================
88 //Delete objects
89 bool PostgresqlCursor::drv_close()
90 {
91  PQclear(d->res);
92  return true;
93 }
94 
95 //==================================================================================
96 //Gets the next record...does not need to do much, just return fetchend if at end of result set
97 void PostgresqlCursor::drv_getNextRecord()
98 {
99  if (at() >= qint64(m_numRows)) {
100  m_fetchResult = FetchResult::End;
101  }
102  else if (at() < 0) {
103  // control will reach here only when at() < 0 ( which is usually -1 )
104  // -1 is same as "1 beyond the End"
105  m_fetchResult = FetchResult::End;
106  }
107  else { // 0 <= at() < m_numRows
108  m_fetchResult = FetchResult::Ok;
109  }
110 }
111 
112 //==================================================================================
113 //Check the current position is within boundaries
114 #if 0
115 void PostgresqlCursor::drv_getPrevRecord()
116 {
117  if (at() < m_res->size() && at() >= 0) {
118  m_fetchResult = FetchResult::Ok;
119  } else if (at() >= m_res->size()) {
120  m_fetchResult = FetchResult::End;
121  } else {
122  m_fetchResult = FetchResult::Error;
123  }
124 }
125 #endif
126 
127 //==================================================================================
128 //Return the value for a given column for the current record
129 QVariant PostgresqlCursor::value(int pos)
130 {
131  if (pos < m_fieldCount)
132  return pValue(pos);
133  else
134  return QVariant();
135 }
136 
137 #if 0
138 inline QVariant pgsqlCStrToVariant(const pqxx::result::field& r)
139 {
140  switch (r.type()) {
141  case BOOLOID:
142  return QString::fromLatin1(r.c_str(), r.size()) == "true"; //!< @todo check formatting
143  case INT2OID:
144  case INT4OID:
145  case INT8OID:
146  return r.as(int());
147  case FLOAT4OID:
148  case FLOAT8OID:
149  case NUMERICOID:
150  return r.as(double());
151  case DATEOID:
152  return QString::fromUtf8(r.c_str(), r.size()); //!< @todo check formatting
153  case TIMEOID:
154  return QString::fromUtf8(r.c_str(), r.size()); //!< @todo check formatting
155  case TIMESTAMPOID:
156  return QString::fromUtf8(r.c_str(), r.size()); //!< @todo check formatting
157  case BYTEAOID:
158  return KDb::pgsqlByteaToByteArray(r.c_str(), r.size());
159  case BPCHAROID:
160  case VARCHAROID:
161  case TEXTOID:
162  return QString::fromUtf8(r.c_str(), r.size()); //utf8?
163  default:
164  return QString::fromUtf8(r.c_str(), r.size()); //utf8?
165  }
166 }
167 #endif
168 
169 static inline bool hasTimeZone(const QString& s)
170 {
171  return s.at(s.length() - 3) == QLatin1Char('+') || s.at(s.length() - 3) == QLatin1Char('-');
172 }
173 
174 static inline QVariant convertToKDbType(bool convert, const QVariant &value, KDbField::Type kdbType)
175 {
176  return (convert && kdbType != KDbField::InvalidType)
177  ? KDbField::convertToType(value, kdbType) : value;
178 }
179 
180 static inline QTime timeFromData(const char *data, int len)
181 {
182  if (len == 0) {
183  return QTime();
184  }
185  QString s(QString::fromLatin1(data, len));
186  if (hasTimeZone(s)) {
187  s.chop(3); // skip timezone
188  }
189  return KDbUtils::timeFromISODateStringWithMs(s);
190 }
191 
192 static inline QDateTime dateTimeFromData(const char *data, int len)
193 {
194  if (len < 10 /*ISO Date*/) {
195  return QDateTime();
196  }
197  QString s(QString::fromLatin1(data, len));
198  if (hasTimeZone(s)) {
199  s.chop(3); // skip timezone
200  if (s.isEmpty()) {
201  return QDateTime();
202  }
203  }
204  if (s.at(s.length() - 3).isPunct()) { // fix ms, should be three digits
205  s += QLatin1Char('0');
206  }
207  return KDbUtils::dateTimeFromISODateStringWithMs(s);
208 }
209 
210 static inline QByteArray byteArrayFromData(const char *data)
211 {
212  size_t unescapedLen;
213  unsigned char *unescapedData = PQunescapeBytea((const unsigned char*)data, &unescapedLen);
214  const QByteArray result((const char*)unescapedData, unescapedLen);
215  //! @todo avoid deep copy; QByteArray does not allow passing ownership of data; maybe copy PQunescapeBytea code?
216  PQfreemem(unescapedData);
217  return result;
218 }
219 
220 //==================================================================================
221 //Return the value for a given column for the current record - Private const version
222 QVariant PostgresqlCursor::pValue(int pos) const
223 {
224 // postgresqlWarning() << "PostgresqlCursor::value - ERROR: requested position is greater than the number of fields";
225  const qint64 row = at();
226 
227  KDbField *f = (m_visibleFieldsExpanded && pos < qMin(m_visibleFieldsExpanded->count(), m_fieldCount))
228  ? m_visibleFieldsExpanded->at(pos)->field() : nullptr;
229 // postgresqlDebug() << "pos:" << pos;
230 
231  const KDbField::Type type = m_realTypes[pos];
232  const KDbField::Type kdbType = f ? f->type() : KDbField::InvalidType; // cache: evaluating type of expressions can be expensive
233  if (PQgetisnull(d->res, row, pos) || kdbType == KDbField::Null) {
234  return QVariant();
235  }
236  const char *data = PQgetvalue(d->res, row, pos);
237  int len = PQgetlength(d->res, row, pos);
238 
239  switch (type) { // from most to least frequently used types:
240  case KDbField::Text:
241  case KDbField::LongText: {
242  const int maxLength = m_realLengths[pos];
243  if (maxLength > 0) {
244  len = qMin(len, maxLength);
245  }
246  return convertToKDbType(!KDbField::isTextType(kdbType),
247  d->unicode ? QString::fromUtf8(data, len) : QString::fromLatin1(data, len),
248  kdbType);
249  }
250  case KDbField::Integer:
251  return convertToKDbType(!KDbField::isIntegerType(kdbType),
252  atoi(data), // the fastest way
253  kdbType);
254  case KDbField::Boolean:
255  return convertToKDbType(kdbType != KDbField::Boolean,
256  bool(data[0] == 't'),
257  kdbType);
259  return convertToKDbType(kdbType != KDbField::BigInteger,
260  (data[0] == '-') ? QByteArray::fromRawData(data, len).toLongLong()
261  : QByteArray::fromRawData(data, len).toULongLong(),
262  kdbType);
263  case KDbField::Double:
264 //! @todo support equivalent of QSql::NumericalPrecisionPolicy, especially for NUMERICOID
265  return convertToKDbType(!KDbField::isFPNumericType(kdbType),
266  QByteArray::fromRawData(data, len).toDouble(),
267  kdbType);
268  case KDbField::Date:
269  return convertToKDbType(kdbType != KDbField::Date,
270  (len == 0) ? QVariant(QDate())
272  kdbType);
273  case KDbField::Time:
274  return convertToKDbType(kdbType != KDbField::Time,
275  timeFromData(data, len),
276  kdbType);
277  case KDbField::DateTime:
278  return convertToKDbType(kdbType != KDbField::DateTime,
279  dateTimeFromData(data, len),
280  kdbType);
281  case KDbField::BLOB:
282  return convertToKDbType(kdbType != KDbField::BLOB,
283  byteArrayFromData(data),
284  kdbType);
285  default:
286  postgresqlWarning() << "PostgresqlCursor::pValue() data type?";
287  }
288  return QVariant();
289 }
290 
291 //==================================================================================
292 //Return the current record as a char**
293 const char** PostgresqlCursor::recordData() const
294 {
295  //! @todo
296  return nullptr;
297 }
298 
299 //==================================================================================
300 //Store the current record in [data]
301 bool PostgresqlCursor::drv_storeCurrentRecord(KDbRecordData* data) const
302 {
303 // postgresqlDebug() << "POSITION IS" << (long)m_at;
304  for (int i = 0; i < m_fieldsToStoreInRecord; i++)
305  (*data)[i] = pValue(i);
306  return true;
307 }
308 
309 //==================================================================================
310 //
311 /*void PostgresqlCursor::drv_clearServerResult()
312 {
313 //! @todo PostgresqlCursor: stuff with server results
314 }*/
315 
316 //==================================================================================
317 //Add the current record to the internal buffer
318 //Implementation required but no need in this driver
319 //Result set is a buffer so do not need another
320 void PostgresqlCursor::drv_appendCurrentRecordToBuffer()
321 {
322 
323 }
324 
325 //==================================================================================
326 //Move internal pointer to internal buffer +1
327 //Implementation required but no need in this driver
328 void PostgresqlCursor::drv_bufferMovePointerNext()
329 {
330 
331 }
332 
333 //==================================================================================
334 //Move internal pointer to internal buffer -1
335 //Implementation required but no need in this driver
336 void PostgresqlCursor::drv_bufferMovePointerPrev()
337 {
338 
339 }
340 
341 //==================================================================================
342 //Move internal pointer to internal buffer to N
343 //Implementation required but no need in this driver
344 void PostgresqlCursor::drv_bufferMovePointerTo(qint64 to)
345 {
346  Q_UNUSED(to);
347 }
348 
349 void PostgresqlCursor::storeResultAndClear(PGresult **pgResult, ExecStatusType execStatus)
350 {
351  d->storeResultAndClear(&m_result, pgResult, execStatus);
352 }
Provides database cursor functionality.
Definition: KDbCursor.h:68
QString fromUtf8(const char *str, int size)
QByteArray fromRawData(const char *data, int size)
@ InvalidType
Definition: KDbField.h:86
Type type(const QSqlDatabase &db)
@ Text
Definition: KDbField.h:98
Specialized string for escaping.
void chop(int n)
const QList< QKeySequence > & close()
bool isFPNumericType() const
Definition: KDbField.h:335
static QVariant convertToType(const QVariant &value, Type type)
Converts value value to variant corresponding to type type.
Definition: KDbField.cpp:426
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
bool isTextType() const
Definition: KDbField.h:353
bool isIntegerType() const
Definition: KDbField.h:326
@ Integer
Definition: KDbField.h:90
bool isEmpty() const const
int length() const const
bool isPunct() const const
@ BigInteger
Definition: KDbField.h:91
Type type() const
Definition: KDbField.cpp:379
@ Double
Definition: KDbField.h:97
@ LongText
Definition: KDbField.h:99
KDbField::Type pgsqlToKDbType(int pqtype, int pqfmod, int *maxTextLength) const
QDate fromString(const QString &string, Qt::DateFormat format)
@ Boolean
Definition: KDbField.h:92
Structure for storing single record with type information.
Definition: KDbRecordData.h:36
KDB_EXPORT QByteArray pgsqlByteaToByteArray(const char *data, int length=-1)
Definition: KDb.cpp:1661
KDbQuerySchema provides information about database query.
QString fromLatin1(const char *str, int size)
const QChar at(int position) const const
Meta-data for a field.
Definition: KDbField.h:71
Provides database connection, allowing queries and data modification.
Definition: KDbConnection.h:51
PostgreSQL database driver.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon May 8 2023 04:07:51 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.