File indexing completed on 2024-05-05 16:47:14

0001 /* This file is part of the KDE project
0002    Copyright (C) 2003 Adam Pigg <adam@piggz.co.uk>
0003    Copyright (C) 2016 Jarosław Staniek <staniek@kde.org>
0004 
0005    This program is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU Library General Public
0007    License as published by the Free Software Foundation; either
0008    version 2 of the License, or (at your option) any later version.
0009 
0010    This program is distributed in the hope that it will be useful,
0011    but WITHOUT ANY WARRANTY; without even the implied warranty of
0012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013    Library General Public License for more details.
0014 
0015    You should have received a copy of the GNU Library General Public License
0016    along with this program; see the file COPYING.  If not, write to
0017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #include "PostgresqlCursor.h"
0022 #include "PostgresqlConnection.h"
0023 #include "PostgresqlConnection_p.h"
0024 #include "PostgresqlDriver.h"
0025 #include "postgresql_debug.h"
0026 
0027 #include "KDbError.h"
0028 #include "KDbGlobal.h"
0029 #include "KDbRecordData.h"
0030 
0031 // Constructor based on query statement
0032 PostgresqlCursor::PostgresqlCursor(KDbConnection* conn, const KDbEscapedString& sql,
0033                                    KDbCursor::Options options)
0034         : KDbCursor(conn, sql, options | KDbCursor::Option::Buffered)
0035         , m_numRows(0)
0036         , d(new PostgresqlCursorData(conn))
0037 {
0038 }
0039 
0040 //==================================================================================
0041 //Constructor base on query object
0042 PostgresqlCursor::PostgresqlCursor(KDbConnection* conn, KDbQuerySchema* query,
0043                                    KDbCursor::Options options)
0044         : KDbCursor(conn, query, options | KDbCursor::Option::Buffered)
0045         , m_numRows(0)
0046         , d(new PostgresqlCursorData(conn))
0047 {
0048 }
0049 
0050 //==================================================================================
0051 //Destructor
0052 PostgresqlCursor::~PostgresqlCursor()
0053 {
0054     close();
0055     delete d;
0056 }
0057 
0058 //==================================================================================
0059 //Create a cursor result set
0060 bool PostgresqlCursor::drv_open(const KDbEscapedString& sql)
0061 {
0062     d->res = d->executeSql(sql);
0063     d->resultStatus = PQresultStatus(d->res);
0064     if (d->resultStatus != PGRES_TUPLES_OK && d->resultStatus != PGRES_COMMAND_OK) {
0065         storeResultAndClear(&d->res, d->resultStatus);
0066         return false;
0067     }
0068     m_fieldsToStoreInRecord = PQnfields(d->res);
0069     m_fieldCount = m_fieldsToStoreInRecord - (containsRecordIdInfo() ? 1 : 0);
0070     m_numRows = PQntuples(d->res);
0071     m_records_in_buf = m_numRows;
0072     m_buffering_completed = true;
0073 
0074     // get real types for all fields
0075     PostgresqlDriver* drv = static_cast<PostgresqlDriver*>(connection()->driver());
0076 
0077     m_realTypes.resize(m_fieldsToStoreInRecord);
0078     m_realLengths.resize(m_fieldsToStoreInRecord);
0079     for (int i = 0; i < int(m_fieldsToStoreInRecord); i++) {
0080         const int pqtype = PQftype(d->res, i);
0081         const int pqfmod = PQfmod(d->res, i);
0082         m_realTypes[i] = drv->pgsqlToKDbType(pqtype, pqfmod, &m_realLengths[i]);
0083     }
0084     return true;
0085 }
0086 
0087 //==================================================================================
0088 //Delete objects
0089 bool PostgresqlCursor::drv_close()
0090 {
0091     PQclear(d->res);
0092     return true;
0093 }
0094 
0095 //==================================================================================
0096 //Gets the next record...does not need to do much, just return fetchend if at end of result set
0097 void PostgresqlCursor::drv_getNextRecord()
0098 {
0099     if (at() >= qint64(m_numRows)) {
0100         m_fetchResult = FetchResult::End;
0101     }
0102     else if (at() < 0) {
0103         // control will reach here only when at() < 0 ( which is usually -1 )
0104         // -1 is same as "1 beyond the End"
0105         m_fetchResult = FetchResult::End;
0106     }
0107     else { // 0 <= at() < m_numRows
0108         m_fetchResult = FetchResult::Ok;
0109     }
0110 }
0111 
0112 //==================================================================================
0113 //Check the current position is within boundaries
0114 #if 0
0115 void PostgresqlCursor::drv_getPrevRecord()
0116 {
0117     if (at() < m_res->size() && at() >= 0) {
0118         m_fetchResult = FetchResult::Ok;
0119     } else if (at() >= m_res->size()) {
0120         m_fetchResult = FetchResult::End;
0121     } else {
0122         m_fetchResult = FetchResult::Error;
0123     }
0124 }
0125 #endif
0126 
0127 //==================================================================================
0128 //Return the value for a given column for the current record
0129 QVariant PostgresqlCursor::value(int pos)
0130 {
0131     if (pos < m_fieldCount)
0132         return pValue(pos);
0133     else
0134         return QVariant();
0135 }
0136 
0137 #if 0
0138 inline QVariant pgsqlCStrToVariant(const pqxx::result::field& r)
0139 {
0140     switch (r.type()) {
0141     case BOOLOID:
0142         return QString::fromLatin1(r.c_str(), r.size()) == "true"; //!< @todo check formatting
0143     case INT2OID:
0144     case INT4OID:
0145     case INT8OID:
0146         return r.as(int());
0147     case FLOAT4OID:
0148     case FLOAT8OID:
0149     case NUMERICOID:
0150         return r.as(double());
0151     case DATEOID:
0152         return QString::fromUtf8(r.c_str(), r.size()); //!< @todo check formatting
0153     case TIMEOID:
0154         return QString::fromUtf8(r.c_str(), r.size()); //!< @todo check formatting
0155     case TIMESTAMPOID:
0156         return QString::fromUtf8(r.c_str(), r.size()); //!< @todo check formatting
0157     case BYTEAOID:
0158         return KDb::pgsqlByteaToByteArray(r.c_str(), r.size());
0159     case BPCHAROID:
0160     case VARCHAROID:
0161     case TEXTOID:
0162         return QString::fromUtf8(r.c_str(), r.size()); //utf8?
0163     default:
0164         return QString::fromUtf8(r.c_str(), r.size()); //utf8?
0165     }
0166 }
0167 #endif
0168 
0169 static inline bool hasTimeZone(const QString& s)
0170 {
0171     return s.at(s.length() - 3) == QLatin1Char('+') || s.at(s.length() - 3) == QLatin1Char('-');
0172 }
0173 
0174 static inline QVariant convertToKDbType(bool convert, const QVariant &value, KDbField::Type kdbType)
0175 {
0176     return (convert && kdbType != KDbField::InvalidType)
0177             ? KDbField::convertToType(value, kdbType) : value;
0178 }
0179 
0180 static inline QTime timeFromData(const char *data, int len)
0181 {
0182     if (len == 0) {
0183         return QTime();
0184     }
0185     QString s(QString::fromLatin1(data, len));
0186     if (hasTimeZone(s)) {
0187         s.chop(3); // skip timezone
0188     }
0189     return KDbUtils::timeFromISODateStringWithMs(s);
0190 }
0191 
0192 static inline QDateTime dateTimeFromData(const char *data, int len)
0193 {
0194     if (len < 10 /*ISO Date*/) {
0195         return QDateTime();
0196     }
0197     QString s(QString::fromLatin1(data, len));
0198     if (hasTimeZone(s)) {
0199         s.chop(3); // skip timezone
0200         if (s.isEmpty()) {
0201             return QDateTime();
0202         }
0203     }
0204     if (s.at(s.length() - 3).isPunct()) { // fix ms, should be three digits
0205         s += QLatin1Char('0');
0206     }
0207     return KDbUtils::dateTimeFromISODateStringWithMs(s);
0208 }
0209 
0210 static inline QByteArray byteArrayFromData(const char *data)
0211 {
0212     size_t unescapedLen;
0213     unsigned char *unescapedData = PQunescapeBytea((const unsigned char*)data, &unescapedLen);
0214     const QByteArray result((const char*)unescapedData, unescapedLen);
0215     //! @todo avoid deep copy; QByteArray does not allow passing ownership of data; maybe copy PQunescapeBytea code?
0216     PQfreemem(unescapedData);
0217     return result;
0218 }
0219 
0220 //==================================================================================
0221 //Return the value for a given column for the current record - Private const version
0222 QVariant PostgresqlCursor::pValue(int pos) const
0223 {
0224 //  postgresqlWarning() << "PostgresqlCursor::value - ERROR: requested position is greater than the number of fields";
0225     const qint64 row = at();
0226 
0227     KDbField *f = (m_visibleFieldsExpanded && pos < qMin(m_visibleFieldsExpanded->count(), m_fieldCount))
0228                        ? m_visibleFieldsExpanded->at(pos)->field() : nullptr;
0229 // postgresqlDebug() << "pos:" << pos;
0230 
0231     const KDbField::Type type = m_realTypes[pos];
0232     const KDbField::Type kdbType = f ? f->type() : KDbField::InvalidType; // cache: evaluating type of expressions can be expensive
0233     if (PQgetisnull(d->res, row, pos) || kdbType == KDbField::Null) {
0234         return QVariant();
0235     }
0236     const char *data = PQgetvalue(d->res, row, pos);
0237     int len = PQgetlength(d->res, row, pos);
0238 
0239     switch (type) { // from most to least frequently used types:
0240     case KDbField::Text:
0241     case KDbField::LongText: {
0242         const int maxLength = m_realLengths[pos];
0243         if (maxLength > 0) {
0244             len = qMin(len, maxLength);
0245         }
0246         return convertToKDbType(!KDbField::isTextType(kdbType),
0247                                 d->unicode ? QString::fromUtf8(data, len) : QString::fromLatin1(data, len),
0248                                 kdbType);
0249     }
0250     case KDbField::Integer:
0251         return convertToKDbType(!KDbField::isIntegerType(kdbType),
0252                                 atoi(data), // the fastest way
0253                                 kdbType);
0254     case KDbField::Boolean:
0255         return convertToKDbType(kdbType != KDbField::Boolean,
0256                                 bool(data[0] == 't'),
0257                                 kdbType);
0258     case KDbField::BigInteger:
0259         return convertToKDbType(kdbType != KDbField::BigInteger,
0260                                 (data[0] == '-') ? QByteArray::fromRawData(data, len).toLongLong()
0261                                                  : QByteArray::fromRawData(data, len).toULongLong(),
0262                                 kdbType);
0263     case KDbField::Double:
0264 //! @todo support equivalent of QSql::NumericalPrecisionPolicy, especially for NUMERICOID
0265         return convertToKDbType(!KDbField::isFPNumericType(kdbType),
0266                                 QByteArray::fromRawData(data, len).toDouble(),
0267                                 kdbType);
0268     case KDbField::Date:
0269         return convertToKDbType(kdbType != KDbField::Date,
0270                                 (len == 0) ? QVariant(QDate())
0271                                            : QVariant(QDate::fromString(QLatin1String(QByteArray::fromRawData(data, len)), Qt::ISODate)),
0272                                 kdbType);
0273     case KDbField::Time:
0274         return convertToKDbType(kdbType != KDbField::Time,
0275                                 timeFromData(data, len),
0276                                 kdbType);
0277     case KDbField::DateTime:
0278         return convertToKDbType(kdbType != KDbField::DateTime,
0279                                 dateTimeFromData(data, len),
0280                                 kdbType);
0281     case KDbField::BLOB:
0282         return convertToKDbType(kdbType != KDbField::BLOB,
0283                                 byteArrayFromData(data),
0284                                 kdbType);
0285     default:
0286         postgresqlWarning() << "PostgresqlCursor::pValue() data type?";
0287     }
0288     return QVariant();
0289 }
0290 
0291 //==================================================================================
0292 //Return the current record as a char**
0293 const char** PostgresqlCursor::recordData() const
0294 {
0295     //! @todo
0296     return nullptr;
0297 }
0298 
0299 //==================================================================================
0300 //Store the current record in [data]
0301 bool PostgresqlCursor::drv_storeCurrentRecord(KDbRecordData* data) const
0302 {
0303 // postgresqlDebug() << "POSITION IS" << (long)m_at;
0304     for (int i = 0; i < m_fieldsToStoreInRecord; i++)
0305         (*data)[i] = pValue(i);
0306     return true;
0307 }
0308 
0309 //==================================================================================
0310 //
0311 /*void PostgresqlCursor::drv_clearServerResult()
0312 {
0313 //! @todo PostgresqlCursor: stuff with server results
0314 }*/
0315 
0316 //==================================================================================
0317 //Add the current record to the internal buffer
0318 //Implementation required but no need in this driver
0319 //Result set is a buffer so do not need another
0320 void PostgresqlCursor::drv_appendCurrentRecordToBuffer()
0321 {
0322 
0323 }
0324 
0325 //==================================================================================
0326 //Move internal pointer to internal buffer +1
0327 //Implementation required but no need in this driver
0328 void PostgresqlCursor::drv_bufferMovePointerNext()
0329 {
0330 
0331 }
0332 
0333 //==================================================================================
0334 //Move internal pointer to internal buffer -1
0335 //Implementation required but no need in this driver
0336 void PostgresqlCursor::drv_bufferMovePointerPrev()
0337 {
0338 
0339 }
0340 
0341 //==================================================================================
0342 //Move internal pointer to internal buffer to N
0343 //Implementation required but no need in this driver
0344 void PostgresqlCursor::drv_bufferMovePointerTo(qint64 to)
0345 {
0346     Q_UNUSED(to);
0347 }
0348 
0349 void PostgresqlCursor::storeResultAndClear(PGresult **pgResult, ExecStatusType execStatus)
0350 {
0351     d->storeResultAndClear(&m_result, pgResult, execStatus);
0352 }