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 }