File indexing completed on 2024-04-21 05:49:01
0001 /* 0002 SPDX-FileCopyrightText: 2010 Marco Mentasti <marcomentasti@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-only 0005 */ 0006 0007 #include "sqlmanager.h" 0008 #include "connectionmodel.h" 0009 0010 #include <KConfig> 0011 #include <KConfigGroup> 0012 #include <KLocalizedString> 0013 0014 #include <QDebug> 0015 #include <QSqlDatabase> 0016 #include <QSqlDriver> 0017 #include <QSqlError> 0018 0019 using KWallet::Wallet; 0020 0021 SQLManager::SQLManager(QObject *parent) 0022 : QObject(parent) 0023 , m_model(new ConnectionModel(this)) 0024 { 0025 } 0026 0027 SQLManager::~SQLManager() 0028 { 0029 for (int i = 0; i < m_model->rowCount(); i++) { 0030 QString connection = m_model->data(m_model->index(i), Qt::DisplayRole).toString(); 0031 QSqlDatabase::removeDatabase(connection); 0032 } 0033 0034 delete m_model; 0035 delete m_wallet; 0036 } 0037 0038 void SQLManager::createConnection(const Connection &conn) 0039 { 0040 if (QSqlDatabase::contains(conn.name)) { 0041 qDebug() << "connection" << conn.name << "already exist"; 0042 QSqlDatabase::removeDatabase(conn.name); 0043 } 0044 0045 QSqlDatabase db = QSqlDatabase::addDatabase(conn.driver, conn.name); 0046 0047 if (!db.isValid()) { 0048 Q_EMIT error(db.lastError().text()); 0049 QSqlDatabase::removeDatabase(conn.name); 0050 return; 0051 } 0052 0053 db.setHostName(conn.hostname); 0054 db.setUserName(conn.username); 0055 db.setPassword(conn.password); 0056 db.setDatabaseName(conn.database); 0057 db.setConnectOptions(conn.options); 0058 0059 if (conn.port > 0) { 0060 db.setPort(conn.port); 0061 } 0062 0063 m_model->addConnection(conn); 0064 0065 // try to open connection, with or without password 0066 if (db.open()) { 0067 m_model->setStatus(conn.name, Connection::ONLINE); 0068 } else { 0069 if (conn.status != Connection::REQUIRE_PASSWORD) { 0070 m_model->setStatus(conn.name, Connection::OFFLINE); 0071 Q_EMIT error(db.lastError().text()); 0072 } 0073 } 0074 0075 Q_EMIT connectionCreated(conn.name); 0076 } 0077 0078 bool SQLManager::testConnection(const Connection &conn, QSqlError &error) 0079 { 0080 QString connectionName = (conn.name.isEmpty()) ? QStringLiteral("katesql-test") : conn.name; 0081 0082 QSqlDatabase db = QSqlDatabase::addDatabase(conn.driver, connectionName); 0083 0084 if (!db.isValid()) { 0085 error = db.lastError(); 0086 QSqlDatabase::removeDatabase(connectionName); 0087 return false; 0088 } 0089 0090 db.setHostName(conn.hostname); 0091 db.setUserName(conn.username); 0092 db.setPassword(conn.password); 0093 db.setDatabaseName(conn.database); 0094 db.setConnectOptions(conn.options); 0095 0096 if (conn.port > 0) { 0097 db.setPort(conn.port); 0098 } 0099 0100 if (!db.open()) { 0101 error = db.lastError(); 0102 QSqlDatabase::removeDatabase(connectionName); 0103 return false; 0104 } 0105 0106 QSqlDatabase::removeDatabase(connectionName); 0107 return true; 0108 } 0109 0110 bool SQLManager::isValidAndOpen(const QString &connection) 0111 { 0112 QSqlDatabase db = QSqlDatabase::database(connection); 0113 0114 if (!db.isValid()) { 0115 m_model->setStatus(connection, Connection::OFFLINE); 0116 Q_EMIT error(db.lastError().text()); 0117 return false; 0118 } 0119 0120 if (!db.isOpen()) { 0121 qDebug() << "database connection is not open. trying to open it..."; 0122 0123 if (m_model->status(connection) == Connection::REQUIRE_PASSWORD) { 0124 QString password; 0125 int ret = readCredentials(connection, password); 0126 0127 if (ret != SQLManager::K_WALLET_CONNECTION_SUCCESSFUL) { 0128 qDebug() << "Can't retrieve password from kwallet. returned code" << ret; 0129 } else { 0130 db.setPassword(password); 0131 m_model->setPassword(connection, password); 0132 } 0133 } 0134 0135 if (!db.open()) { 0136 m_model->setStatus(connection, Connection::OFFLINE); 0137 Q_EMIT error(db.lastError().text()); 0138 return false; 0139 } 0140 } 0141 0142 m_model->setStatus(connection, Connection::ONLINE); 0143 0144 return true; 0145 } 0146 0147 void SQLManager::reopenConnection(const QString &name) 0148 { 0149 Q_EMIT connectionAboutToBeClosed(name); 0150 0151 QSqlDatabase db = QSqlDatabase::database(name); 0152 0153 db.close(); 0154 isValidAndOpen(name); 0155 } 0156 0157 Wallet *SQLManager::openWallet() 0158 { 0159 if (!m_wallet) { 0160 /// FIXME get kate window id... 0161 m_wallet = Wallet::openWallet(KWallet::Wallet::NetworkWallet(), 0); 0162 } 0163 0164 if (!m_wallet) { 0165 return nullptr; 0166 } 0167 0168 QString folder(QStringLiteral("SQL Connections")); 0169 0170 if (!m_wallet->hasFolder(folder)) { 0171 m_wallet->createFolder(folder); 0172 } 0173 0174 m_wallet->setFolder(folder); 0175 0176 return m_wallet; 0177 } 0178 0179 int SQLManager::storeCredentials(const Connection &conn) 0180 { 0181 Wallet *wallet = openWallet(); 0182 0183 if (!wallet) { // user reject 0184 return SQLManager::K_WALLET_CONNECTION_REJECTED_BY_USER; 0185 } 0186 0187 QMap<QString, QString> map; 0188 0189 map[QStringLiteral("driver")] = conn.driver.toUpper(); 0190 map[QStringLiteral("options")] = conn.options; 0191 0192 // Sqlite is without password 0193 if (conn.driver.contains(QLatin1String("QSQLITE"))) { 0194 map[QStringLiteral("database")] = conn.database; 0195 } else { 0196 map[QStringLiteral("database")] = conn.database.toUpper(); 0197 map[QStringLiteral("username")] = conn.username; 0198 map[QStringLiteral("password")] = conn.password; 0199 map[QStringLiteral("hostname")] = conn.hostname.toUpper(); 0200 map[QStringLiteral("port")] = QString::number(conn.port); 0201 } 0202 const int result = (wallet->writeMap(conn.name, map) == SQLManager::K_WALLET_CONNECTION_SUCCESSFUL) ? SQLManager::K_WALLET_CONNECTION_SUCCESSFUL 0203 : SQLManager::K_WALLET_CONNECTION_ERROR; 0204 return result; 0205 } 0206 0207 // if success, password contain the password 0208 int SQLManager::readCredentials(const QString &name, QString &password) 0209 { 0210 Wallet *wallet = openWallet(); 0211 0212 if (!wallet) { // user reject 0213 return SQLManager::K_WALLET_CONNECTION_REJECTED_BY_USER; 0214 } 0215 0216 QMap<QString, QString> map; 0217 0218 if (wallet->readMap(name, map) == 0) { 0219 if (!map.isEmpty()) { 0220 password = map.value(QStringLiteral("password")); 0221 return SQLManager::K_WALLET_CONNECTION_SUCCESSFUL; 0222 } 0223 } 0224 0225 return SQLManager::K_WALLET_CONNECTION_ERROR; 0226 } 0227 0228 ConnectionModel *SQLManager::connectionModel() 0229 { 0230 return m_model; 0231 } 0232 0233 void SQLManager::removeConnection(const QString &name) 0234 { 0235 Q_EMIT connectionAboutToBeClosed(name); 0236 0237 m_model->removeConnection(name); 0238 0239 QSqlDatabase::removeDatabase(name); 0240 0241 Q_EMIT connectionRemoved(name); 0242 } 0243 0244 void SQLManager::loadConnections(const KConfigGroup &connectionsGroup) 0245 { 0246 Connection c; 0247 const auto groupList = connectionsGroup.groupList(); 0248 0249 for (const QString &groupName : groupList) { 0250 qDebug() << "reading group:" << groupName; 0251 0252 KConfigGroup group = connectionsGroup.group(groupName); 0253 0254 c.name = groupName; 0255 c.driver = group.readEntry("driver"); 0256 c.options = group.readEntry("options"); 0257 0258 if (c.driver.contains(QLatin1String("QSQLITE"))) { 0259 c.database = QUrl(group.readEntry("database")).path(); 0260 } else { 0261 c.database = group.readEntry("database"); 0262 c.hostname = group.readEntry("hostname"); 0263 c.username = group.readEntry("username"); 0264 c.port = group.readEntry("port", 0); 0265 0266 // for compatibility with version 0.2, when passwords 0267 // were stored in config file instead of kwallet 0268 c.password = group.readEntry("password"); 0269 0270 if (!c.password.isEmpty()) { 0271 c.status = Connection::ONLINE; 0272 } else { 0273 c.status = Connection::REQUIRE_PASSWORD; 0274 } 0275 } 0276 createConnection(c); 0277 } 0278 } 0279 0280 void SQLManager::saveConnections(KConfigGroup *connectionsGroup) 0281 { 0282 // qDebug() << "Saving " << m_model->rowCount() << " groups"; 0283 for (int i = 0; i < m_model->rowCount(); i++) { 0284 saveConnection(connectionsGroup, m_model->data(m_model->index(i), Qt::UserRole).value<Connection>()); 0285 } 0286 } 0287 0288 void SQLManager::saveConnection(KConfigGroup *connectionsGroup, const Connection &conn) 0289 { 0290 // qDebug() << "saving connection " << conn.name; 0291 KConfigGroup group = connectionsGroup->group(conn.name); 0292 0293 group.writeEntry("driver", conn.driver); 0294 group.writeEntry("options", conn.options); 0295 0296 if (conn.driver.contains(QLatin1String("QSQLITE"))) { 0297 group.writeEntry("database", QUrl::fromLocalFile(conn.database)); 0298 return; 0299 } 0300 group.writeEntry("database", conn.database); 0301 group.writeEntry("hostname", conn.hostname); 0302 group.writeEntry("username", conn.username); 0303 group.writeEntry("port", conn.port); 0304 } 0305 0306 void SQLManager::runQuery(const QString &text, const QString &connection) 0307 { 0308 // qDebug() << "connection:" << connection; 0309 // qDebug() << "text:" << text; 0310 0311 if (text.isEmpty()) { 0312 return; 0313 } 0314 0315 if (!isValidAndOpen(connection)) { 0316 return; 0317 } 0318 0319 QSqlDatabase db = QSqlDatabase::database(connection); 0320 QSqlQuery query(db); 0321 0322 if (!query.prepare(text)) { 0323 QSqlError err = query.lastError(); 0324 0325 if (err.type() == QSqlError::ConnectionError) { 0326 m_model->setStatus(connection, Connection::OFFLINE); 0327 } 0328 0329 Q_EMIT error(err.text()); 0330 return; 0331 } 0332 0333 if (!query.exec()) { 0334 QSqlError err = query.lastError(); 0335 0336 if (err.type() == QSqlError::ConnectionError) { 0337 m_model->setStatus(connection, Connection::OFFLINE); 0338 } 0339 0340 Q_EMIT error(err.text()); 0341 return; 0342 } 0343 0344 QString message; 0345 0346 /// TODO: improve messages 0347 if (query.isSelect()) { 0348 if (!query.driver()->hasFeature(QSqlDriver::QuerySize)) { 0349 message = i18nc("@info", "Query completed successfully"); 0350 } else { 0351 int nRowsSelected = query.size(); 0352 message = i18ncp("@info", "%1 record selected", "%1 records selected", nRowsSelected); 0353 } 0354 } else { 0355 int nRowsAffected = query.numRowsAffected(); 0356 message = i18ncp("@info", "%1 row affected", "%1 rows affected", nRowsAffected); 0357 } 0358 0359 Q_EMIT success(message); 0360 Q_EMIT queryActivated(query, connection); 0361 } 0362 0363 #include "moc_sqlmanager.cpp"