ctkPluginDatabase.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809
  1. /*=============================================================================
  2. Library: CTK
  3. Copyright (c) German Cancer Research Center,
  4. Division of Medical and Biological Informatics
  5. Licensed under the Apache License, Version 2.0 (the "License");
  6. you may not use this file except in compliance with the License.
  7. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. =============================================================================*/
  15. #include "ctkPluginDatabase_p.h"
  16. #include "ctkPluginDatabaseException.h"
  17. #include "ctkPlugin.h"
  18. #include "ctkPluginConstants.h"
  19. #include "ctkPluginException.h"
  20. #include "ctkPluginArchive_p.h"
  21. #include "ctkPluginStorage_p.h"
  22. #include "ctkServiceException.h"
  23. #include <QApplication>
  24. #include <QFileInfo>
  25. #include <QUrl>
  26. #include <QServiceManager>
  27. #include <QDebug>
  28. //database table names
  29. #define PLUGINS_TABLE "Plugins"
  30. #define PLUGIN_RESOURCES_TABLE "PluginResources"
  31. enum TBindIndexes
  32. {
  33. EBindIndex=0,
  34. EBindIndex1,
  35. EBindIndex2,
  36. EBindIndex3,
  37. EBindIndex4,
  38. EBindIndex5,
  39. EBindIndex6,
  40. EBindIndex7
  41. };
  42. ctkPluginDatabase::ctkPluginDatabase(ctkPluginStorage* storage)
  43. :m_isDatabaseOpen(false), m_inTransaction(false), m_PluginStorage(storage)
  44. {
  45. }
  46. ctkPluginDatabase::~ctkPluginDatabase()
  47. {
  48. close();
  49. }
  50. void ctkPluginDatabase::open()
  51. {
  52. if (m_isDatabaseOpen)
  53. return;
  54. QString path;
  55. //Create full path to database
  56. if(m_databasePath.isEmpty ())
  57. m_databasePath = getDatabasePath();
  58. path = m_databasePath;
  59. QFileInfo dbFileInfo(path);
  60. if (!dbFileInfo.dir().exists())
  61. {
  62. if(!QDir::root().mkpath(dbFileInfo.path()))
  63. {
  64. close();
  65. QString errorText("Could not create database directory: %1");
  66. throw ctkPluginDatabaseException(errorText.arg(dbFileInfo.path()), ctkPluginDatabaseException::DB_CREATE_DIR_ERROR);
  67. }
  68. }
  69. m_connectionName = dbFileInfo.completeBaseName();
  70. QSqlDatabase database;
  71. if (QSqlDatabase::contains(m_connectionName))
  72. {
  73. database = QSqlDatabase::database(m_connectionName);
  74. }
  75. else
  76. {
  77. database = QSqlDatabase::addDatabase("QSQLITE", m_connectionName);
  78. database.setDatabaseName(path);
  79. }
  80. if (!database.isValid())
  81. {
  82. close();
  83. throw ctkPluginDatabaseException(QString("Invalid database connection: %1").arg(m_connectionName),
  84. ctkPluginDatabaseException::DB_CONNECTION_INVALID);
  85. }
  86. //Create or open database
  87. if (!database.isOpen())
  88. {
  89. if (!database.open())
  90. {
  91. close();
  92. throw ctkPluginDatabaseException(QString("Could not open database. ") + database.lastError().text(),
  93. ctkPluginDatabaseException::DB_SQL_ERROR);
  94. }
  95. }
  96. m_isDatabaseOpen = true;
  97. //Check if the sqlite version supports foreign key constraints
  98. QSqlQuery query(database);
  99. if (!query.exec("PRAGMA foreign_keys"))
  100. {
  101. close();
  102. throw ctkPluginDatabaseException(QString("Check for foreign key support failed."),
  103. ctkPluginDatabaseException::DB_SQL_ERROR);
  104. }
  105. if (!query.next())
  106. {
  107. close();
  108. throw ctkPluginDatabaseException(QString("SQLite db does not support foreign keys. It is either older than 3.6.19 or was compiled with SQLITE_OMIT_FOREIGN_KEY or SQLITE_OMIT_TRIGGER"),
  109. ctkPluginDatabaseException::DB_SQL_ERROR);
  110. }
  111. query.finish();
  112. query.clear();
  113. //Enable foreign key support
  114. if (!query.exec("PRAGMA foreign_keys = ON"))
  115. {
  116. close();
  117. throw ctkPluginDatabaseException(QString("Enabling foreign key support failed."),
  118. ctkPluginDatabaseException::DB_SQL_ERROR);
  119. }
  120. query.finish();
  121. //Check database structure (tables) and recreate tables if neccessary
  122. //If one of the tables is missing remove all tables and recreate them
  123. //This operation is required in order to avoid data coruption
  124. if (!checkTables())
  125. {
  126. if (dropTables())
  127. {
  128. createTables();
  129. }
  130. else
  131. {
  132. //dropTables() should've handled error message
  133. //and warning
  134. close();
  135. }
  136. }
  137. //Update database based on the recorded timestamps
  138. updateDB();
  139. }
  140. void ctkPluginDatabase::updateDB()
  141. {
  142. checkConnection();
  143. QSqlDatabase database = QSqlDatabase::database(m_connectionName);
  144. QSqlQuery query(database);
  145. beginTransaction(&query, Write);
  146. QString statement = "SELECT ID, Location, LocalPath, Timestamp, SymbolicName, Version FROM Plugins WHERE State != ?";
  147. QList<QVariant> bindValues;
  148. bindValues.append(ctkPlugin::UNINSTALLED);
  149. QList<qlonglong> outdatedIds;
  150. QList<QPair<QString,QString> > outdatedPlugins;
  151. QStringList outdatedServiceNames;
  152. try
  153. {
  154. executeQuery(&query, statement, bindValues);
  155. while (query.next())
  156. {
  157. QFileInfo pluginInfo(query.value(EBindIndex2).toString());
  158. if (pluginInfo.lastModified() > QDateTime::fromString(query.value(EBindIndex3).toString(), Qt::ISODate))
  159. {
  160. outdatedIds.append(query.value(EBindIndex).toLongLong());
  161. outdatedPlugins.append(qMakePair(query.value(EBindIndex1).toString(), query.value(EBindIndex2).toString()));
  162. outdatedServiceNames.append(query.value(EBindIndex4).toString() + "_" + query.value(EBindIndex5).toString());
  163. }
  164. }
  165. }
  166. catch (...)
  167. {
  168. rollbackTransaction(&query);
  169. throw;
  170. }
  171. query.finish();
  172. query.clear();
  173. try
  174. {
  175. statement = "DELETE FROM Plugins WHERE ID=?";
  176. QListIterator<qlonglong> idIter(outdatedIds);
  177. while (idIter.hasNext())
  178. {
  179. bindValues.clear();
  180. bindValues.append(idIter.next());
  181. executeQuery(&query, statement, bindValues);
  182. }
  183. }
  184. catch (...)
  185. {
  186. rollbackTransaction(&query);
  187. throw;
  188. }
  189. try
  190. {
  191. QtMobility::QServiceManager serviceManager;
  192. QStringListIterator serviceNameIter(outdatedServiceNames);
  193. while (serviceNameIter.hasNext())
  194. {
  195. QString serviceName = serviceNameIter.next();
  196. serviceManager.removeService(serviceName);
  197. QtMobility::QServiceManager::Error error = serviceManager.error();
  198. if (!(error == QtMobility::QServiceManager::NoError ||
  199. error == QtMobility::QServiceManager::ComponentNotFound))
  200. {
  201. throw ctkServiceException(QString("Removing service named ") + serviceName +
  202. " failed: " + QString::number(static_cast<unsigned int>(error)));
  203. }
  204. }
  205. }
  206. catch (...)
  207. {
  208. rollbackTransaction(&query);
  209. throw;
  210. }
  211. commitTransaction(&query);
  212. QListIterator<QPair<QString,QString> > locationIter(outdatedPlugins);
  213. while (locationIter.hasNext())
  214. {
  215. const QPair<QString,QString>& locations = locationIter.next();
  216. insertPlugin(QUrl(locations.first), locations.second, false);
  217. }
  218. }
  219. ctkPluginArchive* ctkPluginDatabase::insertPlugin(const QUrl& location, const QString& localPath, bool createArchive)
  220. {
  221. checkConnection();
  222. // Assemble the data for the sql record
  223. QFileInfo fileInfo(localPath);
  224. if (!fileInfo.exists())
  225. {
  226. throw std::invalid_argument((localPath + " does not exist").toStdString());
  227. }
  228. const QString lastModified = fileInfo.lastModified().toString(Qt::ISODate);
  229. QString resourcePrefix = fileInfo.baseName();
  230. if (resourcePrefix.startsWith("lib"))
  231. {
  232. resourcePrefix = resourcePrefix.mid(3);
  233. }
  234. resourcePrefix.replace("_", ".");
  235. resourcePrefix = QString(":/") + resourcePrefix + "/";
  236. QSqlDatabase database = QSqlDatabase::database(m_connectionName);
  237. QSqlQuery query(database);
  238. beginTransaction(&query, Write);
  239. QString statement = "INSERT INTO Plugins(Location,LocalPath,SymbolicName,Version,State,Timestamp) VALUES(?,?,?,?,?,?)";
  240. QList<QVariant> bindValues;
  241. bindValues.append(location.toString());
  242. bindValues.append(localPath);
  243. bindValues.append(QString("na"));
  244. bindValues.append(QString("na"));
  245. bindValues.append(ctkPlugin::INSTALLED);
  246. bindValues.append(lastModified);
  247. qlonglong pluginId = -1;
  248. try
  249. {
  250. executeQuery(&query, statement, bindValues);
  251. QVariant lastId = query.lastInsertId();
  252. if (lastId.isValid())
  253. {
  254. pluginId = lastId.toLongLong();
  255. }
  256. }
  257. catch (...)
  258. {
  259. rollbackTransaction(&query);
  260. throw;
  261. }
  262. // Load the plugin and cache the resources
  263. QPluginLoader pluginLoader;
  264. pluginLoader.setFileName(localPath);
  265. if (!pluginLoader.load())
  266. {
  267. rollbackTransaction(&query);
  268. throw ctkPluginException(QString("The plugin could not be loaded: %1").arg(localPath));
  269. }
  270. QDirIterator dirIter(resourcePrefix, QDirIterator::Subdirectories);
  271. while (dirIter.hasNext())
  272. {
  273. QString resourcePath = dirIter.next();
  274. if (QFileInfo(resourcePath).isDir()) continue;
  275. QFile resourceFile(resourcePath);
  276. resourceFile.open(QIODevice::ReadOnly);
  277. QByteArray resourceData = resourceFile.readAll();
  278. resourceFile.close();
  279. statement = "INSERT INTO PluginResources(PluginID, ResourcePath, Resource) VALUES(?,?,?)";
  280. bindValues.clear();
  281. bindValues.append(QVariant::fromValue<qlonglong>(pluginId));
  282. bindValues.append(resourcePath.mid(resourcePrefix.size()-1));
  283. bindValues.append(resourceData);
  284. try
  285. {
  286. executeQuery(&query, statement, bindValues);
  287. }
  288. catch (...)
  289. {
  290. rollbackTransaction(&query);
  291. throw;
  292. }
  293. }
  294. pluginLoader.unload();
  295. try
  296. {
  297. ctkPluginArchive* archive = new ctkPluginArchive(m_PluginStorage, location, localPath,
  298. pluginId);;
  299. statement = "UPDATE Plugins SET SymbolicName=?,Version=? WHERE ID=?";
  300. QString versionString = archive->getAttribute(ctkPluginConstants::PLUGIN_VERSION);
  301. bindValues.clear();
  302. bindValues.append(archive->getAttribute(ctkPluginConstants::PLUGIN_SYMBOLICNAME));
  303. bindValues.append(versionString.isEmpty() ? "0.0.0" : versionString);
  304. bindValues.append(pluginId);
  305. if (!createArchive)
  306. {
  307. delete archive;
  308. archive = 0;
  309. }
  310. executeQuery(&query, statement, bindValues);
  311. commitTransaction(&query);
  312. return archive;
  313. }
  314. catch (...)
  315. {
  316. rollbackTransaction(&query);
  317. throw;
  318. }
  319. }
  320. QStringList ctkPluginDatabase::findResourcesPath(long pluginId, const QString& path) const
  321. {
  322. checkConnection();
  323. QString statement = "SELECT SUBSTR(ResourcePath,?) FROM PluginResources WHERE PluginID=? AND SUBSTR(ResourcePath,1,?)=?";
  324. QString resourcePath = path.startsWith('/') ? path : QString("/") + path;
  325. if (!resourcePath.endsWith('/'))
  326. resourcePath += "/";
  327. QList<QVariant> bindValues;
  328. bindValues.append(resourcePath.size()+1);
  329. bindValues.append(qlonglong(pluginId));
  330. bindValues.append(resourcePath.size());
  331. bindValues.append(resourcePath);
  332. QSqlDatabase database = QSqlDatabase::database(m_connectionName);
  333. QSqlQuery query(database);
  334. executeQuery(&query, statement, bindValues);
  335. QStringList paths;
  336. while (query.next())
  337. {
  338. QString currPath = query.value(EBindIndex).toString();
  339. int slashIndex = currPath.indexOf('/');
  340. if (slashIndex > 0)
  341. {
  342. currPath = currPath.left(slashIndex+1);
  343. }
  344. paths << currPath;
  345. }
  346. return paths;
  347. }
  348. void ctkPluginDatabase::removeArchive(const ctkPluginArchive *pa)
  349. {
  350. checkConnection();
  351. QSqlDatabase database = QSqlDatabase::database(m_connectionName);
  352. QSqlQuery query(database);
  353. QString statement = "DELETE FROM Plugins WHERE ID=?";
  354. QList<QVariant> bindValues;
  355. bindValues.append(pa->getPluginId());
  356. executeQuery(&query, statement, bindValues);
  357. }
  358. void ctkPluginDatabase::executeQuery(QSqlQuery *query, const QString &statement, const QList<QVariant> &bindValues) const
  359. {
  360. Q_ASSERT(query != 0);
  361. bool success = false;
  362. enum {Prepare =0 , Execute=1};
  363. for (int stage=Prepare; stage <= Execute; ++stage)
  364. {
  365. if ( stage == Prepare)
  366. success = query->prepare(statement);
  367. else // stage == Execute
  368. success = query->exec();
  369. if (!success)
  370. {
  371. QString errorText = "Problem: Could not %1 statement: %2\n"
  372. "Reason: %3\n"
  373. "Parameters: %4\n";
  374. QString parameters;
  375. if (bindValues.count() > 0)
  376. {
  377. for (int i = 0; i < bindValues.count(); ++i)
  378. {
  379. parameters.append(QString("\n\t[") + QString::number(i) + "]: " + bindValues.at(i).toString());
  380. }
  381. }
  382. else
  383. {
  384. parameters = "None";
  385. }
  386. ctkPluginDatabaseException::Type errorType;
  387. int result = query->lastError().number();
  388. if (result == 26 || result == 11) //SQLILTE_NOTADB || SQLITE_CORRUPT
  389. {
  390. qWarning() << "ctkPluginFramework:- Database file is corrupt or invalid:" << getDatabasePath();
  391. errorType = ctkPluginDatabaseException::DB_FILE_INVALID;
  392. }
  393. else if (result == 8) //SQLITE_READONLY
  394. errorType = ctkPluginDatabaseException::DB_WRITE_ERROR;
  395. else
  396. errorType = ctkPluginDatabaseException::DB_SQL_ERROR;
  397. query->finish();
  398. query->clear();
  399. throw ctkPluginDatabaseException(errorText.arg(stage == Prepare ? "prepare":"execute")
  400. .arg(statement).arg(query->lastError().text()).arg(parameters), errorType);
  401. }
  402. if (stage == Prepare)
  403. {
  404. foreach(const QVariant &bindValue, bindValues)
  405. query->addBindValue(bindValue);
  406. }
  407. }
  408. }
  409. void ctkPluginDatabase::close()
  410. {
  411. if (m_isDatabaseOpen)
  412. {
  413. QSqlDatabase database = QSqlDatabase::database(m_connectionName, false);
  414. if (database.isValid())
  415. {
  416. if(database.isOpen())
  417. {
  418. database.close();
  419. m_isDatabaseOpen = false;
  420. return;
  421. }
  422. }
  423. else
  424. {
  425. throw ctkPluginDatabaseException(QString("Problem closing database: Invalid connection %1").arg(m_connectionName));
  426. }
  427. }
  428. }
  429. void ctkPluginDatabase::setDatabasePath(const QString &databasePath)
  430. {
  431. m_databasePath = QDir::toNativeSeparators(databasePath);
  432. }
  433. QString ctkPluginDatabase::getDatabasePath() const
  434. {
  435. QString path;
  436. if(m_databasePath.isEmpty())
  437. {
  438. QSettings settings;
  439. path = settings.value("PluginDB/Path").toString();
  440. if (path.isEmpty())
  441. {
  442. path = QApplication::applicationDirPath();
  443. if (path.lastIndexOf("/") != path.length() -1)
  444. {
  445. path.append("/");
  446. }
  447. QString appName = QApplication::applicationName();
  448. appName.replace(" ", "");
  449. if (!appName.isEmpty())
  450. {
  451. path.append(appName + "_plugins.db");
  452. }
  453. else
  454. {
  455. path.append("pluginfw.db");
  456. qWarning() << "Warning: Using generic plugin database name. You should "
  457. "set an application name via QCoreApplication::setApplicationName()";
  458. }
  459. }
  460. path = QDir::toNativeSeparators(path);
  461. }
  462. else
  463. {
  464. path = m_databasePath;
  465. }
  466. qDebug() << "Using database:" << path;
  467. return path;
  468. }
  469. QByteArray ctkPluginDatabase::getPluginResource(long pluginId, const QString& res) const
  470. {
  471. checkConnection();
  472. QSqlDatabase database = QSqlDatabase::database(m_connectionName);
  473. QSqlQuery query(database);
  474. QString statement = "SELECT Resource FROM PluginResources WHERE PluginID=? AND ResourcePath=?";
  475. QString resourcePath = res.startsWith('/') ? res : QString("/") + res;
  476. QList<QVariant> bindValues;
  477. bindValues.append(qlonglong(pluginId));
  478. bindValues.append(resourcePath);
  479. executeQuery(&query, statement, bindValues);
  480. if (query.next())
  481. {
  482. return query.value(EBindIndex).toByteArray();
  483. }
  484. return QByteArray();
  485. }
  486. void ctkPluginDatabase::createTables()
  487. {
  488. QSqlDatabase database = QSqlDatabase::database(m_connectionName);
  489. QSqlQuery query(database);
  490. //Begin Transaction
  491. beginTransaction(&query, Write);
  492. QString statement("CREATE TABLE Plugins("
  493. "ID INTEGER PRIMARY KEY,"
  494. "Location TEXT NOT NULL UNIQUE,"
  495. "LocalPath TEXT NOT NULL UNIQUE,"
  496. "SymbolicName TEXT NOT NULL,"
  497. "Version TEXT NOT NULL,"
  498. "State INTEGER NOT NULL,"
  499. "Timestamp TEXT NOT NULL)");
  500. try
  501. {
  502. executeQuery(&query, statement);
  503. }
  504. catch (...)
  505. {
  506. rollbackTransaction(&query);
  507. throw;
  508. }
  509. statement = "CREATE TABLE PluginResources("
  510. "PluginID INTEGER NOT NULL,"
  511. "ResourcePath TEXT NOT NULL, "
  512. "Resource BLOB NOT NULL,"
  513. "FOREIGN KEY(PluginID) REFERENCES Plugins(ID) ON DELETE CASCADE)";
  514. try
  515. {
  516. executeQuery(&query, statement);
  517. }
  518. catch (...)
  519. {
  520. rollbackTransaction(&query);
  521. throw;
  522. }
  523. try
  524. {
  525. commitTransaction(&query);
  526. }
  527. catch (...)
  528. {
  529. rollbackTransaction(&query);
  530. throw;
  531. }
  532. }
  533. bool ctkPluginDatabase::checkTables() const
  534. {
  535. bool bTables(false);
  536. QStringList tables = QSqlDatabase::database(m_connectionName).tables();
  537. if (tables.contains(PLUGINS_TABLE)
  538. && tables.contains(PLUGIN_RESOURCES_TABLE))
  539. {
  540. bTables = true;
  541. }
  542. return bTables;
  543. }
  544. bool ctkPluginDatabase::dropTables()
  545. {
  546. //Execute transaction for deleting the database tables
  547. QSqlDatabase database = QSqlDatabase::database(m_connectionName);
  548. QSqlQuery query(database);
  549. QStringList expectedTables;
  550. expectedTables << PLUGINS_TABLE << PLUGIN_RESOURCES_TABLE;
  551. if (database.tables().count() > 0)
  552. {
  553. beginTransaction(&query, Write);
  554. QStringList actualTables = database.tables();
  555. foreach(const QString expectedTable, expectedTables)
  556. {
  557. if (actualTables.contains(expectedTable))
  558. {
  559. try
  560. {
  561. executeQuery(&query, QString("DROP TABLE ") + expectedTable);
  562. }
  563. catch (...)
  564. {
  565. rollbackTransaction(&query);
  566. throw;
  567. }
  568. }
  569. try
  570. {
  571. commitTransaction(&query);
  572. }
  573. catch (...)
  574. {
  575. rollbackTransaction(&query);
  576. throw;
  577. }
  578. }
  579. }
  580. return true;
  581. }
  582. bool ctkPluginDatabase::isOpen() const
  583. {
  584. return m_isDatabaseOpen;
  585. }
  586. void ctkPluginDatabase::checkConnection() const
  587. {
  588. if(!m_isDatabaseOpen)
  589. {
  590. throw ctkPluginDatabaseException("Database not open.", ctkPluginDatabaseException::DB_NOT_OPEN_ERROR);
  591. }
  592. if (!QSqlDatabase::database(m_connectionName).isValid())
  593. {
  594. throw ctkPluginDatabaseException(QString("Database connection invalid: %1").arg(m_connectionName),
  595. ctkPluginDatabaseException::DB_CONNECTION_INVALID);
  596. }
  597. }
  598. void ctkPluginDatabase::beginTransaction(QSqlQuery *query, TransactionType type)
  599. {
  600. bool success;
  601. if (type == Read)
  602. success = query->exec(QLatin1String("BEGIN"));
  603. else
  604. success = query->exec(QLatin1String("BEGIN IMMEDIATE"));
  605. if (!success) {
  606. int result = query->lastError().number();
  607. if (result == 26 || result == 11) //SQLITE_NOTADB || SQLITE_CORRUPT
  608. {
  609. throw ctkPluginDatabaseException(QString("ctkPluginFramework: Database file is corrupt or invalid: %1").arg(getDatabasePath()),
  610. ctkPluginDatabaseException::DB_FILE_INVALID);
  611. }
  612. else if (result == 8) //SQLITE_READONLY
  613. {
  614. throw ctkPluginDatabaseException(QString("ctkPluginFramework: Insufficient permissions to write to database: %1").arg(getDatabasePath()),
  615. ctkPluginDatabaseException::DB_WRITE_ERROR);
  616. }
  617. else
  618. throw ctkPluginDatabaseException(QString("ctkPluginFramework: ") + query->lastError().text(),
  619. ctkPluginDatabaseException::DB_SQL_ERROR);
  620. }
  621. }
  622. void ctkPluginDatabase::commitTransaction(QSqlQuery *query)
  623. {
  624. Q_ASSERT(query != 0);
  625. query->finish();
  626. query->clear();
  627. if (!query->exec(QLatin1String("COMMIT")))
  628. {
  629. throw ctkPluginDatabaseException(QString("ctkPluginFramework: ") + query->lastError().text(),
  630. ctkPluginDatabaseException::DB_SQL_ERROR);
  631. }
  632. }
  633. void ctkPluginDatabase::rollbackTransaction(QSqlQuery *query)
  634. {
  635. Q_ASSERT(query !=0);
  636. query->finish();
  637. query->clear();
  638. if (!query->exec(QLatin1String("ROLLBACK")))
  639. {
  640. throw ctkPluginDatabaseException(QString("ctkPluginFramework: ") + query->lastError().text(),
  641. ctkPluginDatabaseException::DB_SQL_ERROR);
  642. }
  643. }
  644. QList<ctkPluginArchive*> ctkPluginDatabase::getPluginArchives() const
  645. {
  646. checkConnection();
  647. QSqlQuery query(QSqlDatabase::database(m_connectionName));
  648. QString statement("SELECT ID, Location, LocalPath FROM Plugins WHERE State != ?");
  649. QList<QVariant> bindValues;
  650. bindValues.append(ctkPlugin::UNINSTALLED);
  651. executeQuery(&query, statement, bindValues);
  652. QList<ctkPluginArchive*> archives;
  653. while (query.next())
  654. {
  655. const long id = query.value(EBindIndex).toLongLong();
  656. const QUrl location(query.value(EBindIndex1).toString());
  657. const QString localPath(query.value(EBindIndex2).toString());
  658. if (id <= 0 || location.isEmpty() || localPath.isEmpty())
  659. {
  660. throw ctkPluginDatabaseException(QString("Database integrity corrupted, row %1 contains empty values.").arg(id),
  661. ctkPluginDatabaseException::DB_FILE_INVALID);
  662. }
  663. try
  664. {
  665. ctkPluginArchive* pa = new ctkPluginArchive(m_PluginStorage, location, localPath, id);
  666. archives.append(pa);
  667. }
  668. catch (const ctkPluginException& exc)
  669. {
  670. qWarning() << exc;
  671. }
  672. }
  673. return archives;
  674. }