KDb

PostgresqlCursor.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2003 Adam Pigg <adam@piggz.co.uk>
3 Copyright (C) 2016 Jarosław Staniek <staniek@kde.org>
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
32PostgresqlCursor::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
42PostgresqlCursor::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
52PostgresqlCursor::~PostgresqlCursor()
53{
54 close();
55 delete d;
56}
57
58//==================================================================================
59//Create a cursor result set
60bool 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);
70 m_numRows = PQntuples(d->res);
71 m_records_in_buf = m_numRows;
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
89bool 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
97void PostgresqlCursor::drv_getNextRecord()
98{
99 if (at() >= qint64(m_numRows)) {
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"
106 }
107 else { // 0 <= at() < m_numRows
109 }
110}
111
112//==================================================================================
113//Check the current position is within boundaries
114#if 0
115void PostgresqlCursor::drv_getPrevRecord()
116{
117 if (at() < m_res->size() && at() >= 0) {
119 } else if (at() >= m_res->size()) {
121 } else {
123 }
124}
125#endif
126
127//==================================================================================
128//Return the value for a given column for the current record
129QVariant PostgresqlCursor::value(int pos)
130{
131 if (pos < m_fieldCount)
132 return pValue(pos);
133 else
134 return QVariant();
135}
136
137#if 0
138inline 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
169static inline bool hasTimeZone(const QString& s)
170{
171 return s.at(s.length() - 3) == QLatin1Char('+') || s.at(s.length() - 3) == QLatin1Char('-');
172}
173
174static 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
180static 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
192static 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
210static 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
222QVariant 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
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 }
251 return convertToKDbType(!KDbField::isIntegerType(kdbType),
252 atoi(data), // the fastest way
253 kdbType);
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())
271 : QVariant(QDate::fromString(QLatin1String(QByteArray::fromRawData(data, len)), Qt::ISODate)),
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**
293const char** PostgresqlCursor::recordData() const
294{
295 //! @todo
296 return nullptr;
297}
298
299//==================================================================================
300//Store the current record in [data]
301bool 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
320void 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
328void 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
336void 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
344void PostgresqlCursor::drv_bufferMovePointerTo(qint64 to)
345{
346 Q_UNUSED(to);
347}
348
349void PostgresqlCursor::storeResultAndClear(PGresult **pgResult, ExecStatusType execStatus)
350{
351 d->storeResultAndClear(&m_result, pgResult, execStatus);
352}
Provides database connection, allowing queries and data modification.
KDbDriver * driver() const
Provides database cursor functionality.
Definition KDbCursor.h:69
@ End
at the end of data
@ Error
error of fetching
@ Ok
the data is fetched
int m_fieldsToStoreInRecord
Used by storeCurrentRecord(), reimplement if needed.
Definition KDbCursor.h:302
int m_fieldCount
cached field count information
Definition KDbCursor.h:301
int m_records_in_buf
number of records currently stored in the buffer
Definition KDbCursor.h:319
bool containsRecordIdInfo() const
bool m_buffering_completed
true if we already have all records stored in the buffer
Definition KDbCursor.h:320
KDbQueryColumnInfo::Vector * m_visibleFieldsExpanded
Useful e.g. for value(int) method to obtain access to schema definition.
Definition KDbCursor.h:324
qint64 at() const
Definition KDbCursor.h:161
FetchResult m_fetchResult
result of a record fetching
Definition KDbCursor.h:316
KDbConnection * connection()
virtual bool close()
Specialized string for escaping.
Meta-data for a field.
Definition KDbField.h:72
bool isTextType() const
Definition KDbField.h:353
static QVariant convertToType(const QVariant &value, Type type)
Converts value value to variant corresponding to type type.
Definition KDbField.cpp:426
@ Integer
Definition KDbField.h:90
@ Boolean
Definition KDbField.h:92
@ InvalidType
Definition KDbField.h:86
@ BigInteger
Definition KDbField.h:91
@ LongText
Definition KDbField.h:99
bool isFPNumericType() const
Definition KDbField.h:335
Type type() const
Definition KDbField.cpp:379
bool isIntegerType() const
Definition KDbField.h:326
KDbQuerySchema provides information about database query.
Structure for storing single record with type information.
PostgreSQL database driver.
KDbField::Type pgsqlToKDbType(int pqtype, int pqfmod, int *maxTextLength) const
Type type(const QSqlDatabase &db)
std::optional< QSqlQuery > query(const QString &queryStatement)
KDB_EXPORT QByteArray pgsqlByteaToByteArray(const char *data, int length=-1)
QByteArray fromRawData(const char *data, qsizetype size)
bool isPunct(char32_t ucs4)
const_reference at(qsizetype i) const const
qsizetype count() const const
const QChar at(qsizetype position) const const
void chop(qsizetype n)
QString fromLatin1(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
qsizetype length() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 19 2024 12:00:07 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.