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

0001 /* This file is part of the KDE project
0002    Copyright (C) 2005 Adam Pigg <adam@piggz.co.uk>
0003    Copyright (C) 2010-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 #ifndef KDB_POSTGRESQLCONNECTION_P_H
0022 #define KDB_POSTGRESQLCONNECTION_P_H
0023 
0024 #include "KDbConnection_p.h"
0025 #include "PostgresqlConnection.h"
0026 #include "PostgresqlDriver.h"
0027 #include "KDbResult.h"
0028 #include "KDbSqlField.h"
0029 #include "KDbSqlRecord.h"
0030 #include "KDbSqlResult.h"
0031 #include "KDbSqlString.h"
0032 
0033 #include <QString>
0034 
0035 #include <libpq-fe.h>
0036 
0037 class KDbEscapedString;
0038 
0039 class PostgresqlConnectionInternal : public KDbConnectionInternal
0040 {
0041 public:
0042     explicit PostgresqlConnectionInternal(KDbConnection *connection);
0043 
0044     virtual ~PostgresqlConnectionInternal();
0045 
0046     //! Executes query for a raw SQL statement @a sql on the database
0047     PGresult* executeSql(const KDbEscapedString& sql);
0048 
0049     static QString serverResultName(int resultCode);
0050 
0051     void storeResultAndClear(KDbResult *result, PGresult **pgResult, ExecStatusType execStatus);
0052 
0053     void storeResult(KDbResult *result);
0054 
0055     //! @return true if status of connection is "OK".
0056     /*! From https://www.postgresql.org/docs/8.4/static/libpq-status.html:
0057         "Only two of these are seen outside of an asynchronous connection procedure:
0058          CONNECTION_OK and CONNECTION_BAD." */
0059     inline bool connectionOK() { return CONNECTION_OK == PQstatus(conn); }
0060 
0061     PGconn *conn;
0062     bool unicode;
0063     QByteArray escapingBuffer;
0064     bool fuzzystrmatchExtensionCreated = false;
0065 private:
0066     Q_DISABLE_COPY(PostgresqlConnectionInternal)
0067 };
0068 
0069 //! Internal PostgreSQL cursor data.
0070 /*! Provides a low-level abstraction for iterating over result sets. */
0071 class PostgresqlCursorData : public PostgresqlConnectionInternal
0072 {
0073 public:
0074     explicit PostgresqlCursorData(KDbConnection* connection);
0075     ~PostgresqlCursorData() override;
0076 
0077     PGresult* res;
0078     ExecStatusType resultStatus;
0079 private:
0080     Q_DISABLE_COPY(PostgresqlCursorData)
0081 };
0082 
0083 class PostgresqlSqlField : public KDbSqlField
0084 {
0085 public:
0086     inline PostgresqlSqlField(const PGresult *r, int n) : result(r), number(n) {
0087     }
0088     //! @return column name
0089     inline QString name() override {
0090         //! @todo UTF8?
0091         return QString::fromLatin1(PQfname(result, number));
0092     }
0093     inline int type() override {
0094         return static_cast<int>(PQftype(result, number));
0095     }
0096     inline int length() override {
0097         return PostgresqlDriver::pqfmodToLength(PQfmod(result, number));
0098     }
0099     const PGresult * const result;
0100     const int number;
0101 private:
0102     Q_DISABLE_COPY(PostgresqlSqlField)
0103 };
0104 
0105 class PostgresqlSqlRecord : public KDbSqlRecord
0106 {
0107 public:
0108     inline PostgresqlSqlRecord(const PGresult *res, int r) : result(res), record(r) {
0109     }
0110     inline ~PostgresqlSqlRecord() override {
0111     }
0112     inline QString stringValue(int index) override {
0113         return PQgetisnull(result, record, index)
0114                 ? QString()
0115                 : QString::fromUtf8(PQgetvalue(result, record, index),
0116                                     PQgetlength(result, record, index));
0117     }
0118     inline KDbSqlString cstringValue(int index) override {
0119         return PQgetisnull(result, record, index)
0120                 ? KDbSqlString()
0121                 : KDbSqlString(PQgetvalue(result, record, index),
0122                                PQgetlength(result, record, index));
0123     }
0124     inline QByteArray toByteArray(int index) override {
0125         return PQgetisnull(result, record, index)
0126                 ? QByteArray()
0127                 : QByteArray(PQgetvalue(result, record, index),
0128                              PQgetlength(result, record, index));
0129     }
0130 
0131 private:
0132     const PGresult * const result;
0133     const int record;
0134     Q_DISABLE_COPY(PostgresqlSqlRecord)
0135 };
0136 
0137 class PostgresqlSqlResult : public KDbSqlResult
0138 {
0139 public:
0140     inline PostgresqlSqlResult(PostgresqlConnection *c, PGresult* r, ExecStatusType status)
0141         : conn(c), result(r), resultStatus(status), recordToFetch(0), recordsCount(PQntuples(r))
0142     {
0143         Q_ASSERT(c);
0144     }
0145 
0146     inline ~PostgresqlSqlResult() override {
0147         PQclear(result);
0148     }
0149 
0150     inline KDbConnection *connection() const override {
0151         return conn;
0152     }
0153 
0154     inline int fieldsCount() override {
0155         return PQnfields(result);
0156     }
0157 
0158     inline Q_REQUIRED_RESULT KDbSqlField *field(int index) override
0159     {
0160         return new PostgresqlSqlField(result, index);
0161     }
0162 
0163     Q_REQUIRED_RESULT KDbField *createField(const QString &tableName, int index) override;
0164 
0165     inline Q_REQUIRED_RESULT QSharedPointer<KDbSqlRecord> fetchRecord() override
0166     {
0167         return QSharedPointer<KDbSqlRecord>(recordToFetch < recordsCount
0168                                                 ? new PostgresqlSqlRecord(result, recordToFetch++)
0169                                                 : nullptr);
0170     }
0171 
0172     inline KDbResult lastResult() override {
0173         KDbResult r;
0174         if (resultStatus == PGRES_TUPLES_OK || resultStatus == PGRES_COMMAND_OK) {
0175             return r;
0176         }
0177         QByteArray msg(PQresultErrorMessage(result));
0178         if (msg.endsWith('\n')) {
0179             msg.chop(1);
0180         }
0181         r.setServerMessage(QString::fromLatin1(msg));
0182         r.setServerErrorCode(resultStatus);
0183         return r;
0184     }
0185 
0186     //! @return the oid of the last insert - only works if there was insert of 1 row
0187     inline quint64 lastInsertRecordId() override {
0188         // InvalidOid == 0 means error
0189         const Oid oid = PQoidValue(result);
0190         return oid == 0 ? std::numeric_limits<quint64>::max() : static_cast<quint64>(oid);
0191     }
0192 
0193 private:
0194     //! @return a KDb type for PostgreSQL type
0195     //! @todo prompt user if necessary?
0196     KDbField::Type type(const QString& tableName, PostgresqlSqlField *field);
0197 
0198     PostgresqlConnection* const conn;
0199     PGresult* result;
0200     ExecStatusType resultStatus;
0201     int recordToFetch;
0202     int recordsCount;
0203     Q_DISABLE_COPY(PostgresqlSqlResult)
0204 };
0205 
0206 #endif