File indexing completed on 2024-04-28 15:58:53
0001 /* This file is part of the KDE project 0002 Copyright (C) 2003-2016 Jarosław Staniek <staniek@kde.org> 0003 0004 This program is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU Library General Public 0006 License as published by the Free Software Foundation; either 0007 version 2 of the License, or (at your option) any later version. 0008 0009 This program is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 Library General Public License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this program; see the file COPYING. If not, write to 0016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 * Boston, MA 02110-1301, USA. 0018 */ 0019 0020 #include "SqliteConnection.h" 0021 #include "SqliteConnection_p.h" 0022 #include "SqliteCursor.h" 0023 #include "SqlitePreparedStatement.h" 0024 #include "SqliteFunctions.h" 0025 #include "sqlite_debug.h" 0026 0027 #include <sqlite3.h> 0028 0029 #include "KDbConnectionData.h" 0030 #include "KDbConnectionOptions.h" 0031 #include "KDbUtils.h" 0032 #include "KDbUtils_p.h" 0033 #include "KDbVersionInfo.h" 0034 0035 #include <QFile> 0036 #include <QDir> 0037 #include <QRegularExpression> 0038 0039 SqliteConnection::SqliteConnection(KDbDriver *driver, const KDbConnectionData& connData, 0040 const KDbConnectionOptions &options) 0041 : KDbConnection(driver, connData, options) 0042 , d(new SqliteConnectionInternal(this)) 0043 { 0044 QByteArray propertyName = "extraSqliteExtensionPaths"; 0045 KDbUtils::Property extraSqliteExtensionPathsProperty = this->options()->property(propertyName); 0046 if (extraSqliteExtensionPathsProperty.isNull()) { 0047 this->options()->insert(propertyName, QStringList()); 0048 } 0049 this->options()->setCaption(propertyName, SqliteConnection::tr("Extra paths for SQLite plugins")); 0050 } 0051 0052 SqliteConnection::~SqliteConnection() 0053 { 0054 destroy(); 0055 delete d; 0056 } 0057 0058 void SqliteConnection::storeResult() 0059 { 0060 d->storeResult(&m_result); 0061 } 0062 0063 bool SqliteConnection::drv_connect() 0064 { 0065 return true; 0066 } 0067 0068 bool SqliteConnection::drv_getServerVersion(KDbServerVersionInfo* version) 0069 { 0070 version->setString(QLatin1String(SQLITE_VERSION)); //defined in sqlite3.h 0071 static const QRegularExpression re(QLatin1String("^(\\d+)\\.(\\d+)\\.(\\d+)$")); 0072 QRegularExpressionMatch match = re.match(version->string()); 0073 if (match.hasMatch()) { 0074 version->setMajor(match.captured(1).toInt()); 0075 version->setMinor(match.captured(2).toInt()); 0076 version->setRelease(match.captured(3).toInt()); 0077 } 0078 return true; 0079 } 0080 0081 bool SqliteConnection::drv_disconnect() 0082 { 0083 return true; 0084 } 0085 0086 bool SqliteConnection::drv_getDatabasesList(QStringList* list) 0087 { 0088 //this is one-db-per-file database 0089 list->append(data().databaseName()); 0090 return true; 0091 } 0092 0093 tristate SqliteConnection::drv_containsTable(const QString &tableName) 0094 { 0095 return resultExists(KDbEscapedString("SELECT name FROM sqlite_master WHERE type='table' AND name LIKE %1") 0096 .arg(escapeString(tableName))); 0097 } 0098 0099 #if 0 // TODO 0100 bool SqliteConnection::drv_getTablesList(QStringList* list) 0101 { 0102 KDbCursor *cursor; 0103 if (!(cursor = executeQuery(KDbEscapedString("SELECT name FROM sqlite_master WHERE type='table'")))) { 0104 sqliteWarning() << "!executeQuery()"; 0105 return false; 0106 } 0107 list->clear(); 0108 cursor->moveFirst(); 0109 while (!cursor->eof() && !cursor->result().isError()) { 0110 *list += cursor->value(0).toString(); 0111 cursor->moveNext(); 0112 } 0113 if (cursor->result().isError()) { 0114 deleteCursor(cursor); 0115 return false; 0116 } 0117 return deleteCursor(cursor); 0118 } 0119 #endif 0120 0121 bool SqliteConnection::drv_createDatabase(const QString &dbName) 0122 { 0123 Q_UNUSED(dbName); 0124 return drv_useDatabaseInternal(nullptr, nullptr, true/*create if missing*/); 0125 } 0126 0127 bool SqliteConnection::drv_useDatabase(const QString &dbName, bool *cancelled, 0128 KDbMessageHandler* msgHandler) 0129 { 0130 Q_UNUSED(dbName); 0131 return drv_useDatabaseInternal(cancelled, msgHandler, false/*do not create if missing*/); 0132 } 0133 0134 bool SqliteConnection::drv_useDatabaseInternal(bool *cancelled, 0135 KDbMessageHandler* msgHandler, bool createIfMissing) 0136 { 0137 //! @todo add option (command line or in kdbrc?) 0138 //! @todo int exclusiveFlag = KDbConnection::isReadOnly() ? SQLITE_OPEN_READONLY : SQLITE_OPEN_WRITE_LOCKED; // <-- shared read + (if !r/o): exclusive write 0139 int openFlags = 0; 0140 if (options()->isReadOnly()) { 0141 openFlags |= SQLITE_OPEN_READONLY; 0142 } 0143 else { 0144 openFlags |= SQLITE_OPEN_READWRITE; 0145 if (createIfMissing) { 0146 openFlags |= SQLITE_OPEN_CREATE; 0147 } 0148 } 0149 0150 //! @todo add option 0151 // int allowReadonly = 1; 0152 // const bool wasReadOnly = KDbConnection::isReadOnly(); 0153 0154 //sqliteDebug() << data().databaseName(); 0155 int res = sqlite3_open_v2( 0156 /* unicode expected since SQLite 3.1 */ 0157 QDir::toNativeSeparators(data().databaseName()).toUtf8().constData(), 0158 &d->data, 0159 openFlags, /*exclusiveFlag, 0160 allowReadonly *//* If 1 and locking fails, try opening in read-only mode */ 0161 nullptr 0162 ); 0163 if (res != SQLITE_OK) { 0164 m_result.setServerErrorCode(res); 0165 } 0166 storeResult(); 0167 0168 if (!m_result.isError()) { 0169 // Set the secure-delete on, so SQLite overwrites deleted content with zeros. 0170 // The default setting is determined by the SQLITE_SECURE_DELETE compile-time option but we overwrite it here. 0171 // Works with 3.6.23. Earlier version just ignore this pragma. 0172 // See https://www.sqlite.org/pragma.html#pragma_secure_delete 0173 //! @todo add connection flags to the driver and global setting to control the "secure delete" pragma 0174 if (!drv_executeSql(KDbEscapedString("PRAGMA secure_delete = on"))) { 0175 drv_closeDatabaseSilently(); 0176 return false; 0177 } 0178 // Load ICU extension for unicode collations 0179 if (!findAndLoadExtension(QLatin1String("kdb_sqlite_icu"))) { 0180 drv_closeDatabaseSilently(); 0181 return false; 0182 } 0183 // load ROOT collation for use as default collation 0184 if (!drv_executeSql(KDbEscapedString("SELECT icu_load_collation('', '')"))) { 0185 drv_closeDatabaseSilently(); 0186 return false; 0187 } 0188 if (!createCustomSQLiteFunctions(d->data)) { 0189 drv_closeDatabaseSilently(); 0190 return false; 0191 } 0192 } 0193 0194 //! @todo check exclusive status 0195 Q_UNUSED(cancelled); 0196 Q_UNUSED(msgHandler); 0197 //! @todo removed in kdb - reenable? 0198 /* 0199 if (d->res == SQLITE_OK && cancelled && !wasReadOnly && allowReadonly && isReadOnly()) { 0200 //opened as read only, ask 0201 if (KDbMessageHandler::Continue != 0202 askQuestion( 0203 KDbMessageHandler::WarningContinueCancel, 0204 futureTr("Do you want to open file \"%1\" as read-only?\n\n" 0205 "The file is probably already open on this or another computer. " 0206 "Could not gain exclusive access for writing the file.") 0207 .arg(QDir::fromNativeSeparators(data()->databaseName())), 0208 futureTr("Opening As Read-Only"), 0209 KDbMessageHandler::Continue, 0210 KDbMessageHandler::KDbGuiItem() 0211 .setProperty("text", futureTr("Open As Read-Only")) 0212 .setProperty("icon", "document-open"), 0213 KDbMessageHandler::KDbGuiItem(), 0214 "askBeforeOpeningFileReadOnly", 0215 KDbMessageHandler::Notify, 0216 msgHandler) 0217 { 0218 clearError(); 0219 if (!drv_closeDatabase()) 0220 return false; 0221 *cancelled = true; 0222 return false; 0223 } 0224 } 0225 0226 if (d->res == SQLITE_CANTOPEN_WITH_LOCKED_READWRITE) { 0227 setError(ERR_ACCESS_RIGHTS, 0228 futureTr("The file is probably already open on this or another computer.\n\n" 0229 "Could not gain exclusive access for reading and writing the file. " 0230 "Check the file's permissions and whether it is already opened and locked by another application.")); 0231 } else if (d->res == SQLITE_CANTOPEN_WITH_LOCKED_WRITE) { 0232 setError(ERR_ACCESS_RIGHTS, 0233 futureTr("The file is probably already open on this or another computer.\n\n" 0234 "Could not gain exclusive access for writing the file. " 0235 "Check the file's permissions and whether it is already opened and locked by another application.")); 0236 }*/ 0237 return res == SQLITE_OK; 0238 } 0239 0240 void SqliteConnection::drv_closeDatabaseSilently() 0241 { 0242 KDbResult result = this->result(); // save 0243 drv_closeDatabase(); 0244 m_result = result; 0245 } 0246 0247 bool SqliteConnection::drv_closeDatabase() 0248 { 0249 if (!d->data) 0250 return false; 0251 0252 const int res = sqlite3_close(d->data); 0253 if (SQLITE_OK == res) { 0254 d->data = nullptr; 0255 return true; 0256 } 0257 if (SQLITE_BUSY == res) { 0258 #if 0 //this is ANNOYING, needs fixing (by closing cursors or waiting) 0259 setError(ERR_CLOSE_FAILED, futureTr("Could not close busy database.")); 0260 #else 0261 return true; 0262 #endif 0263 } 0264 return false; 0265 } 0266 0267 bool SqliteConnection::drv_dropDatabase(const QString &dbName) 0268 { 0269 Q_UNUSED(dbName); // Each database is one single SQLite file. 0270 const QString filename = data().databaseName(); 0271 if (QFile::exists(filename) && !QFile::remove(filename)) { 0272 m_result = KDbResult(ERR_ACCESS_RIGHTS, 0273 SqliteConnection::tr("Could not delete file \"%1\". " 0274 "Check the file's permissions and whether it is already " 0275 "opened and locked by another application.") 0276 .arg(QDir::fromNativeSeparators(filename))); 0277 return false; 0278 } 0279 return true; 0280 } 0281 0282 KDbCursor* SqliteConnection::prepareQuery(const KDbEscapedString& sql, KDbCursor::Options options) 0283 { 0284 return new SqliteCursor(this, sql, options); 0285 } 0286 0287 KDbCursor* SqliteConnection::prepareQuery(KDbQuerySchema* query, KDbCursor::Options options) 0288 { 0289 return new SqliteCursor(this, query, options); 0290 } 0291 0292 KDbSqlResult* SqliteConnection::drv_prepareSql(const KDbEscapedString& sql) 0293 { 0294 #ifdef KDB_DEBUG_GUI 0295 KDb::debugGUI(QLatin1String("PrepareSQL (SQLite): ") + sql.toString()); 0296 #endif 0297 0298 sqlite3_stmt *prepared_st = nullptr; 0299 const int res = sqlite3_prepare( 0300 d->data, /* Database handle */ 0301 sql.constData(), /* SQL statement, UTF-8 encoded */ 0302 sql.length(), /* Length of zSql in bytes. */ 0303 &prepared_st, /* OUT: Statement handle */ 0304 nullptr/*const char **pzTail*/ /* OUT: Pointer to unused portion of zSql */ 0305 ); 0306 if (res != SQLITE_OK) { 0307 m_result.setServerErrorCode(res); 0308 storeResult(); 0309 #ifdef KDB_DEBUG_GUI 0310 KDb::debugGUI(QLatin1String(" Failure")); 0311 #endif 0312 return nullptr; 0313 } 0314 0315 #ifdef KDB_DEBUG_GUI 0316 KDb::debugGUI(QLatin1String(" Success")); 0317 #endif 0318 return new SqliteSqlResult(this, prepared_st); 0319 } 0320 0321 bool SqliteConnection::drv_executeSql(const KDbEscapedString& sql) 0322 { 0323 #ifdef KDB_DEBUG_GUI 0324 KDb::debugGUI(QLatin1String("ExecuteSQL (SQLite): ") + sql.toString()); 0325 #endif 0326 0327 char *errmsg_p = nullptr; 0328 const int res = sqlite3_exec( 0329 d->data, 0330 sql.constData(), 0331 nullptr/*callback*/, 0332 nullptr, 0333 &errmsg_p); 0334 if (res != SQLITE_OK) { 0335 m_result.setServerErrorCode(res); 0336 } 0337 if (errmsg_p) { 0338 clearResult(); 0339 m_result.setServerMessage(QLatin1String(errmsg_p)); 0340 sqlite3_free(errmsg_p); 0341 } else { 0342 storeResult(); 0343 } 0344 0345 #ifdef KDB_DEBUG_GUI 0346 KDb::debugGUI(QLatin1String( res == SQLITE_OK ? " Success" : " Failure")); 0347 #endif 0348 return res == SQLITE_OK; 0349 } 0350 0351 QString SqliteConnection::serverResultName() const 0352 { 0353 return SqliteConnectionInternal::serverResultName(m_result.serverErrorCode()); 0354 } 0355 0356 KDbPreparedStatementInterface* SqliteConnection::prepareStatementInternal() 0357 { 0358 return new SqlitePreparedStatement(d); 0359 } 0360 0361 bool SqliteConnection::findAndLoadExtension(const QString & name) 0362 { 0363 QStringList pluginPaths; 0364 foreach (const QString& path, KDb::libraryPaths()) { 0365 pluginPaths += path + QLatin1String("/sqlite3"); 0366 } 0367 pluginPaths += options()->property("extraSqliteExtensionPaths").value().toStringList(); 0368 foreach (const QString& path, pluginPaths) { 0369 if (loadExtension(path + QLatin1Char('/') + name + QLatin1String(KDB_SHARED_LIB_EXTENSION))) { 0370 return true; 0371 } 0372 } 0373 clearResult(); 0374 m_result = KDbResult(ERR_CANNOT_LOAD_OBJECT, 0375 SqliteConnection::tr("Could not load SQLite plugin \"%1\".").arg(name)); 0376 return false; 0377 } 0378 0379 bool SqliteConnection::loadExtension(const QString& path) 0380 { 0381 bool tempEnable = false; 0382 clearResult(); 0383 QFileInfo fileInfo(path); 0384 if (!fileInfo.exists()) { 0385 m_result = KDbResult(ERR_OBJECT_NOT_FOUND, 0386 SqliteConnection::tr("Could not find SQLite plugin file \"%1\".").arg(path)); 0387 //sqliteWarning() << "Could not find SQLite extension"; 0388 return false; 0389 } 0390 if (!d->extensionsLoadingEnabled()) { 0391 tempEnable = true; 0392 d->setExtensionsLoadingEnabled(true); 0393 } 0394 char *errmsg_p = nullptr; 0395 int res = sqlite3_load_extension(d->data, QDir::toNativeSeparators(path).toUtf8().constData(), 0396 nullptr, &errmsg_p); 0397 bool ok = res == SQLITE_OK; 0398 if (!ok) { 0399 m_result.setServerErrorCode(res); 0400 m_result = KDbResult(ERR_CANNOT_LOAD_OBJECT, 0401 SqliteConnection::tr("Could not load SQLite extension \"%1\".").arg(path)); 0402 sqliteWarning() << "SqliteConnection::loadExtension(): Could not load SQLite extension" 0403 << path << ":" << errmsg_p; 0404 if (errmsg_p) { 0405 m_result.setServerMessage(QLatin1String(errmsg_p)); 0406 sqlite3_free(errmsg_p); 0407 } 0408 } 0409 if (tempEnable) { 0410 d->setExtensionsLoadingEnabled(false); 0411 } 0412 return ok; 0413 }