File indexing completed on 2024-04-28 15:58:50

0001 /* This file is part of the KDE project
0002    Copyright (C) 2004 Martin Ellis <martin.ellis@kdemail.net>
0003    Copyright (C) 2004-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_MYSQLCONNECTION_P_H
0022 #define KDB_MYSQLCONNECTION_P_H
0023 
0024 #include "KDbConnection_p.h"
0025 #include "MysqlConnection.h"
0026 #include "KDbResult.h"
0027 #include "KDbSqlField.h"
0028 #include "KDbSqlRecord.h"
0029 #include "KDbSqlResult.h"
0030 #include "KDbSqlString.h"
0031 
0032 #include <mysql.h>
0033 
0034 class KDbConnectionData;
0035 class KDbEscapedString;
0036 
0037 //! Internal MySQL connection data.
0038 /*! Provides a low-level API for accessing MySQL databases, that can
0039     be shared by any module that needs direct access to the underlying
0040     database.  Used by the KDb and migration drivers.
0041     @todo fix the above note about migration...
0042  */
0043 class MysqlConnectionInternal : public KDbConnectionInternal
0044 {
0045 public:
0046     explicit MysqlConnectionInternal(KDbConnection* connection);
0047     virtual ~MysqlConnectionInternal();
0048 
0049     //! Connects to a MySQL database
0050     /*! Connects to the MySQL server on host as the given user using the specified
0051         password.  If host is "localhost", then a socket on the local file system
0052         can be specified to connect to the server (several defaults will be tried if
0053         none is specified).  If the server is on a remote machine, then a port is
0054         the port that the remote server is listening on.
0055      */
0056     bool db_connect(const KDbConnectionData& data);
0057 
0058     //! Disconnects from the database
0059     bool db_disconnect();
0060 
0061     //! Selects a database that is about to be used
0062     bool useDatabase(const QString &dbName = QString());
0063 
0064     //! Executes query for a raw SQL statement @a sql using mysql_real_query()
0065     bool executeSql(const KDbEscapedString& sql);
0066 
0067     static QString serverResultName(MYSQL *mysql);
0068 
0069     void storeResult(KDbResult *result);
0070 
0071     MYSQL *mysql;
0072     bool mysql_owned; //!< true if mysql pointer should be freed on destruction
0073     int res; //!< result code of last operation on server
0074     //! Get lower_case_table_name variable value so we know if there's case sensitivity supported for table and database names
0075     bool lowerCaseTableNames;
0076     //! Server version known after successfull connection.
0077     //! Equal to major_version*10000 + release_level*100 + sub_version
0078     //! 0 if not known.
0079     //! See https://dev.mysql.com/doc/refman/5.7/en/mysql-get-server-version.html
0080     //! @todo store in Connection base class as a property or as public server info
0081     unsigned long serverVersion;
0082 private:
0083     Q_DISABLE_COPY(MysqlConnectionInternal)
0084 };
0085 
0086 //! Internal MySQL cursor data.
0087 /*! Provides a low-level abstraction for iterating over MySql result sets. */
0088 class MysqlCursorData : public MysqlConnectionInternal
0089 {
0090 public:
0091     explicit MysqlCursorData(KDbConnection* connection);
0092     ~MysqlCursorData() override;
0093 
0094     MYSQL_RES *mysqlres;
0095     MYSQL_ROW mysqlrow;
0096     unsigned long *lengths;
0097     qint64 numRows;
0098 private:
0099     Q_DISABLE_COPY(MysqlCursorData)
0100 };
0101 
0102 class MysqlSqlField : public KDbSqlField
0103 {
0104 public:
0105     inline MysqlSqlField(MYSQL_FIELD *f) : data(f) {
0106     }
0107     //! @return column name
0108     inline QString name() override {
0109         //! @todo UTF8?
0110         return QString::fromLatin1(data->name);
0111     }
0112     inline int type() override {
0113         return data->type;
0114     }
0115     inline int length() override {
0116         return data->length;
0117     }
0118     MYSQL_FIELD *data;
0119 private:
0120     Q_DISABLE_COPY(MysqlSqlField)
0121 };
0122 
0123 class MysqlSqlRecord : public KDbSqlRecord
0124 {
0125 public:
0126     inline MysqlSqlRecord(MYSQL_ROW r, unsigned long* len) : record(r), lengths(len) {
0127     }
0128     inline ~MysqlSqlRecord() override {
0129     }
0130     inline QString stringValue(int index) override {
0131         return QString::fromUtf8(record[index], lengths[index]);
0132     }
0133     inline KDbSqlString cstringValue(int index) override {
0134         return KDbSqlString(record[index], lengths[index]);
0135     }
0136     inline QByteArray toByteArray(int index) override {
0137         return QByteArray(record[index], lengths[index]);
0138     }
0139 
0140 private:
0141     MYSQL_ROW record;
0142     unsigned long* lengths;
0143     Q_DISABLE_COPY(MysqlSqlRecord)
0144 };
0145 
0146 class MysqlSqlResult : public KDbSqlResult
0147 {
0148 public:
0149     inline MysqlSqlResult(MysqlConnection *c, MYSQL_RES *d)
0150         : conn(c), data(d), fields(nullptr)
0151     {
0152         Q_ASSERT(c);
0153     }
0154 
0155     inline ~MysqlSqlResult() override {
0156         if (data) {
0157             mysql_free_result(data);
0158         }
0159     }
0160 
0161     inline KDbConnection *connection() const override {
0162         return conn;
0163     }
0164 
0165     inline int fieldsCount() override {
0166         return data ? mysql_num_fields(data) : 0;
0167     }
0168 
0169     Q_REQUIRED_RESULT inline KDbSqlField *field(int index) override {
0170         if (!fields) {
0171             if (!data) {
0172                 return nullptr;
0173             }
0174             fields = mysql_fetch_fields(data);
0175         }
0176         return new MysqlSqlField(fields + index);
0177     }
0178 
0179     Q_REQUIRED_RESULT KDbField *createField(const QString &tableName, int index) override;
0180 
0181     Q_REQUIRED_RESULT inline QSharedPointer<KDbSqlRecord> fetchRecord() override
0182     {
0183         QSharedPointer<KDbSqlRecord> record;
0184         MYSQL_ROW row = data ? mysql_fetch_row(data) : nullptr;
0185         if (!row) {
0186             return record;
0187         }
0188         unsigned long* lengths = mysql_fetch_lengths(data);
0189         record.reset(new MysqlSqlRecord(row, lengths));
0190         return record;
0191     }
0192 
0193     inline KDbResult lastResult() override {
0194         KDbResult res;
0195         const int err = mysql_errno(conn->d->mysql);
0196         if (err != 0) {
0197             res.setCode(ERR_OTHER);
0198             res.setServerErrorCode(err);
0199         }
0200         return res;
0201     }
0202 
0203     inline quint64 lastInsertRecordId() override {
0204         //! @todo
0205         return static_cast<quint64>(mysql_insert_id(conn->d->mysql));
0206     }
0207 
0208 private:
0209     //! @return a KDb type for MySQL type
0210     //! @todo prompt user if necessary?
0211     KDbField::Type type(const QString& tableName, MysqlSqlField *field);
0212 
0213     //! @return a KDb BLOB-related type for MySQL type
0214     /*! Distinguishes between a BLOB and a TEXT types.
0215         MySQL uses the same field type to identify BLOB and TEXT fields.
0216         This method queries the server to find out if a field is a binary
0217         field or a text field. It also considers the length of CHAR and VARCHAR
0218         fields to see whether Text or LongText is the appropriate Kexi field type.
0219         Assumes fld is a CHAR, VARCHAR, one of the BLOBs or TEXTs.
0220         Returns KDbField::Text, KDbField::LongText or KDbField::BLOB. */
0221     KDbField::Type blobType(const QString& tableName, MysqlSqlField *field);
0222 
0223     MysqlConnection * const conn;
0224     MYSQL_RES * const data;
0225     MYSQL_FIELD *fields;
0226     Q_DISABLE_COPY(MysqlSqlResult)
0227 };
0228 
0229 #endif