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 }