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

0001 /* This file is part of the KDE project
0002    Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
0003    Copyright (C) 2003 Joseph Wenninger<jowenn@kde.org>
0004    Copyright (C) 2004-2016 Jarosław Staniek <staniek@kde.org>
0005 
0006    This program is free software; you can redistribute it and/or
0007    modify it under the terms of the GNU Library General Public
0008    License as published by the Free Software Foundation; either
0009    version 2 of the License, or (at your option) any later version.
0010 
0011    This program is distributed in the hope that it will be useful,
0012    but WITHOUT ANY WARRANTY; without even the implied warranty of
0013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014    Library General Public License for more details.
0015 
0016    You should have received a copy of the GNU Library General Public License
0017    along with this program; see the file COPYING.  If not, write to
0018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019  * Boston, MA 02110-1301, USA.
0020 */
0021 
0022 #include "MysqlConnection.h"
0023 #include "MysqlDriver.h"
0024 #include "MysqlCursor.h"
0025 #include "MysqlPreparedStatement.h"
0026 #include "mysql_debug.h"
0027 #include "KDbConnectionData.h"
0028 #include "KDbVersionInfo.h"
0029 
0030 #include <QRegularExpression>
0031 
0032 MysqlConnection::MysqlConnection(KDbDriver *driver, const KDbConnectionData& connData,
0033                                  const KDbConnectionOptions &options)
0034         : KDbConnection(driver, connData, options)
0035         , d(new MysqlConnectionInternal(this))
0036 {
0037 }
0038 
0039 MysqlConnection::~MysqlConnection()
0040 {
0041     destroy();
0042     delete d;
0043 }
0044 
0045 bool MysqlConnection::drv_connect()
0046 {
0047     const bool ok = d->db_connect(data());
0048     if (!ok) {
0049         storeResult(); //store error msg, if any - can be destroyed after disconnect()
0050         d->db_disconnect();
0051         return false;
0052     }
0053 
0054     // Get lower_case_table_name value so we know if there's case sensitivity supported
0055     // See https://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html
0056     int intLowerCaseTableNames = 0;
0057     const tristate res = querySingleNumber(
0058         KDbEscapedString("SHOW VARIABLES LIKE 'lower_case_table_name'"), &intLowerCaseTableNames,
0059         0 /*col*/,
0060         QueryRecordOptions(QueryRecordOption::Default) & ~QueryRecordOptions(QueryRecordOption::AddLimitTo1));
0061     if (res == false) // sanity
0062         return false;
0063     d->lowerCaseTableNames = intLowerCaseTableNames > 0;
0064     return true;
0065 }
0066 
0067 bool MysqlConnection::drv_getServerVersion(KDbServerVersionInfo* version)
0068 {
0069     // https://dev.mysql.com/doc/refman/5.1/en/mysql-get-server-info.html
0070     version->setString(QLatin1String(mysql_get_server_info(d->mysql)));
0071 
0072     // get the version info using 'version' built-in variable:
0073 //! @todo this is hardcoded for now; define api for retrieving variables and use this API...
0074     // https://dev.mysql.com/doc/refman/5.1/en/mysql-get-server-version.html
0075     QString versionString;
0076     tristate res = querySingleString(KDbEscapedString("SELECT @@version"), &versionString,
0077         /*column*/ 0,
0078         QueryRecordOptions(QueryRecordOption::Default) & ~QueryRecordOptions(QueryRecordOption::AddLimitTo1));
0079 
0080     static const QRegularExpression versionRe(QLatin1String("^(\\d+)\\.(\\d+)\\.(\\d+)$"));
0081     QRegularExpressionMatch match  = versionRe.match(versionString);
0082     if (res == false) // sanity
0083         return false;
0084     if (match.hasMatch()) {
0085         // (if querySingleString failed, the version will be 0.0.0...
0086         version->setMajor(match.captured(1).toInt());
0087         version->setMinor(match.captured(2).toInt());
0088         version->setRelease(match.captured(3).toInt());
0089     }
0090     return true;
0091 }
0092 
0093 bool MysqlConnection::drv_disconnect()
0094 {
0095     return d->db_disconnect();
0096 }
0097 
0098 KDbCursor* MysqlConnection::prepareQuery(const KDbEscapedString& sql, KDbCursor::Options options)
0099 {
0100     return new MysqlCursor(this, sql, options);
0101 }
0102 
0103 KDbCursor* MysqlConnection::prepareQuery(KDbQuerySchema* query, KDbCursor::Options options)
0104 {
0105     return new MysqlCursor(this, query, options);
0106 }
0107 
0108 bool MysqlConnection::drv_getDatabasesList(QStringList* list)
0109 {
0110     mysqlDebug();
0111     list->clear();
0112     MYSQL_RES *res = mysql_list_dbs(d->mysql, nullptr);
0113     if (res != nullptr) {
0114         MYSQL_ROW row;
0115         while ((row = mysql_fetch_row(res)) != nullptr) {
0116             *list << QString::fromUtf8(row[0]);
0117         }
0118         mysql_free_result(res);
0119         return true;
0120     }
0121     storeResult();
0122     return false;
0123 }
0124 
0125 bool MysqlConnection::drv_databaseExists(const QString &dbName, bool ignoreErrors)
0126 {
0127     /* db names can be lower case in mysql */
0128     const QString storedDbName(d->lowerCaseTableNames ? dbName.toLower() : dbName);
0129     const tristate result = resultExists(
0130         KDbEscapedString("SHOW DATABASES LIKE %1").arg(escapeString(storedDbName)));
0131     if (result == true) {
0132         return true;
0133     }
0134     if (!ignoreErrors) {
0135         m_result = KDbResult(ERR_OBJECT_NOT_FOUND,
0136                              tr("The database \"%1\" does not exist.").arg(storedDbName));
0137     }
0138     return false;
0139 }
0140 
0141 bool MysqlConnection::drv_createDatabase(const QString &dbName)
0142 {
0143     const QString storedDbName(d->lowerCaseTableNames ? dbName.toLower() : dbName);
0144     mysqlDebug() << storedDbName;
0145     // mysql_create_db deprecated, use SQL here.
0146     // db names are lower case in mysql
0147     return drv_executeSql(KDbEscapedString("CREATE DATABASE %1").arg(escapeIdentifier(storedDbName)));
0148 }
0149 
0150 bool MysqlConnection::drv_useDatabase(const QString &dbName, bool *cancelled, KDbMessageHandler* msgHandler)
0151 {
0152     Q_UNUSED(cancelled);
0153     Q_UNUSED(msgHandler);
0154 //! @todo is here escaping needed?
0155     const QString storedDbName(d->lowerCaseTableNames ? dbName.toLower() : dbName);
0156     if (!d->useDatabase(storedDbName)) {
0157         storeResult();
0158         return false;
0159     }
0160     return true;
0161 }
0162 
0163 bool MysqlConnection::drv_closeDatabase()
0164 {
0165 //! @todo free resources, as far as I know, mysql doesn't support that
0166     return true;
0167 }
0168 
0169 bool MysqlConnection::drv_dropDatabase(const QString &dbName)
0170 {
0171 //! @todo is here escaping needed?
0172     const QString storedDbName(d->lowerCaseTableNames ? dbName.toLower() : dbName);
0173     return drv_executeSql(KDbEscapedString("DROP DATABASE %1").arg(escapeIdentifier(storedDbName)));
0174 }
0175 
0176 KDbSqlResult* MysqlConnection::drv_prepareSql(const KDbEscapedString& sql)
0177 {
0178     if (!drv_executeSql(sql)) {
0179         return nullptr;
0180     }
0181     MYSQL_RES *data = mysql_use_result(d->mysql); // more optimal than mysql_store_result
0182     //! @todo use mysql_error()
0183     return new MysqlSqlResult(this, data);
0184 }
0185 
0186 bool MysqlConnection::drv_executeSql(const KDbEscapedString& sql)
0187 {
0188     if (!d->executeSql(sql)) {
0189         storeResult();
0190         return false;
0191     }
0192     return true;
0193 }
0194 
0195 QString MysqlConnection::serverResultName() const
0196 {
0197     return MysqlConnectionInternal::serverResultName(d->mysql);
0198 }
0199 
0200 tristate MysqlConnection::drv_containsTable(const QString& tableName)
0201 {
0202     return resultExists(KDbEscapedString("SHOW TABLES LIKE %1")
0203                         .arg(escapeString(tableName)));
0204 }
0205 
0206 KDbPreparedStatementInterface* MysqlConnection::prepareStatementInternal()
0207 {
0208     return new MysqlPreparedStatement(d);
0209 }
0210 
0211 void MysqlConnection::storeResult()
0212 {
0213     d->storeResult(&m_result);
0214 }