[KLF Application][KLF Tools][KLF Backend][KLF Home]
KLatexFormula Project
klflibdbengine.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * file klflibdbengine.cpp
3  * This file is part of the KLatexFormula Project.
4  * Copyright (C) 2011 by Philippe Faist
5  * philippe.faist at bluewin.ch
6  * *
7  * This program is free software; you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation; either version 2 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program; if not, write to the *
19  * Free Software Foundation, Inc., *
20  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
21  ***************************************************************************/
22 /* $Id: klflibdbengine.cpp 603 2011-02-26 23:14:55Z phfaist $ */
23 
24 #include <QDebug>
25 #include <QApplication> // qApp
26 #include <QString>
27 #include <QBuffer>
28 #include <QFile>
29 #include <QByteArray>
30 #include <QDataStream>
31 #include <QMessageBox>
32 #include <QSqlRecord>
33 #include <QSqlDatabase>
34 #include <QSqlQuery>
35 #include <QSqlError>
36 
37 #include <klfguiutil.h>
38 #include "klflib.h"
39 #include "klflibview.h"
40 #include "klflibdbengine.h"
41 #include "klflibdbengine_p.h"
42 
43 
44 
45 
88 static QByteArray image_data(const QImage& img, const char *format)
89 {
90  QByteArray data;
91  QBuffer buf(&data);
92  buf.open(QIODevice::WriteOnly);
93  img.save(&buf, format);
94  buf.close();
95  return data;
96 }
97 
98 template<class T>
99 static QByteArray metatype_to_data(const T& object)
100 {
101  QByteArray data;
102  {
103  QDataStream stream(&data, QIODevice::WriteOnly);
104  stream << object;
105  // force close of buffer in destroying stream
106  }
107  return data;
108 }
109 
110 template<class T>
111 static T metatype_from_data(const QByteArray& data)
112 {
113  T object;
114  QDataStream stream(data);
115  stream >> object;
116  return object;
117 }
118 
119 
120 
121 
122 // --------------------------------------------
123 
124 
125 
127 {
128  pAutoDisconnectDB = false;
130 }
132 {
133  if (pAutoDisconnectDB)
135 }
136 
137 
138 
139 // --------------------------------------------
140 
141 
142 // static
144 {
145  bool accessshared = false;
146 
147  QUrl url = givenurl;
148 
149  if (url.hasQueryItem("klfDefaultSubResource")) {
150  QString defaultsubres = url.queryItemValue("klfDefaultSubResource");
151  // force lower-case default sub-resource
152  url.removeAllQueryItems("klfDefaultSubResource");
153  url.addQueryItem("klfDefaultSubResource", defaultsubres.toLower());
154  }
155 
156  QSqlDatabase db;
157  if (url.scheme() == "klf+sqlite") {
158  QUrl dburl = url;
159  dburl.removeAllQueryItems("klfDefaultSubResource");
160  dburl.removeAllQueryItems("klfReadOnly");
161  accessshared = false;
162  QString dburlstr = dburl.toString();
163  QString path = klfUrlLocalFilePath(dburl);
164  if (dburlstr.isEmpty() || !QFile::exists(path)) {
165  QMessageBox::critical(0, tr("Error"),
166  tr("Database file <b>%1</b> does not exist.").arg(path));
167  return NULL;
168  }
169  db = QSqlDatabase::database(dburlstr);
170  if ( ! db.isValid() ) {
171  // connection not already open
172  db = QSqlDatabase::addDatabase("QSQLITE", dburl.toString());
173  db.setDatabaseName(path);
174  if ( !db.open() || db.lastError().isValid() ) {
175  QMessageBox::critical(0, tr("Error"),
176  tr("Unable to open library file \"%1\" (engine: \"%2\").\nError: %3")
177  .arg(path, db.driverName(), db.lastError().text()), QMessageBox::Ok);
178  return NULL;
179  }
180  }
181  } else {
182  qWarning("KLFLibDBEngine::openUrl: bad url scheme in URL\n\t%s",
183  qPrintable(url.toString()));
184  return NULL;
185  }
186 
187  return new KLFLibDBEngine(db, true /*autoDisconnect*/, url, accessshared, parent);
188 }
189 
190 // static
191 KLFLibDBEngine * KLFLibDBEngine::createSqlite(const QString& fileName, const QString& sresname,
192  const QString& srestitle, QObject *parent)
193 {
195  klfDbgSt("fileName="<<fileName<<", sresname="<<sresname<<", srestitle="<<srestitle) ;
196 
197  QString subresname = sresname;
198  QString subrestitle = srestitle;
199 
200  bool r;
201 
202  if (QFile::exists(fileName)) {
203  // fail; we want to _CREATE_ a database. use openUrl() to open an existing DB.
204  // To overwrite, erase the file first.
205  return NULL;
206  }
207  QUrl url = QUrl::fromLocalFile(fileName);
208  url.setScheme("klf+sqlite");
209 
210  QString dburlstr = url.toString();
211  QSqlDatabase db = QSqlDatabase::database(dburlstr);
212  if (!db.isValid()) {
213  db = QSqlDatabase::addDatabase("QSQLITE", dburlstr);
214  QString path = klfUrlLocalFilePath(url);
215  db.setDatabaseName(path);
216  r = db.open(); // go open (here create) the DB
217  if ( !r || db.lastError().isValid() ) {
218  QMessageBox::critical(0, tr("Error"),
219  tr("Unable to create library file %1 (SQLITE database):\n"
220  "%2")
221  .arg(path, db.lastError().text()), QMessageBox::Ok);
222  return NULL;
223  }
224  }
225 
226  if (subresname.isEmpty()) {
227  subresname = "table1";
228  if (subrestitle.isEmpty()) {
229  subrestitle = "Table 1";
230  }
231  }
232  if (subrestitle.isEmpty())
233  subrestitle = subresname;
234 
235  url.addQueryItem("klfDefaultSubResource", subresname);
236  if (subresname.contains("\"")) {
237  // SQLite table name cannot contain double-quote char (itself is used to escape the table name!)
238  qWarning()<<KLF_FUNC_NAME<<"\" character is not allowed in SQLITE database tables (<-> library sub-resources).";
239  return NULL;
240  }
241 
242  r = initFreshDatabase(db);
243  if ( r ) {
244  // and create default table
245  r = createFreshDataTable(db, subresname);
246  }
247  if ( !r ) {
248  QMessageBox::critical(0, tr("Error"),
249  tr("Unable to initialize the SQLITE database file %1!").arg(url.path()));
250  return NULL;
251  }
252 
253  KLFLibDBEngine *res = new KLFLibDBEngine(db, true /*autoDisconnect*/, url, false, parent);
254 
255  r = res->setSubResourceProperty(subresname, SubResPropTitle, subrestitle);
256  if ( ! r ) {
257  qWarning()<<"Failed to create table named "<<subresname<<"!";
258  delete res;
259  return NULL;
260  }
261 
262  return res;
263 }
264 
265 // private
266 KLFLibDBEngine::KLFLibDBEngine(const QSqlDatabase& db, bool autodisconnect,
267  const QUrl& url, bool accessshared, QObject *parent)
268  : KLFLibResourceEngine(url, FeatureReadOnly|FeatureLocked|FeatureSubResources
269  |FeatureSubResourceProps, parent)
270 {
271  pAutoDisconnectDB = autodisconnect;
272 
273  // load some read-only properties in memory (these are NOT stored in the DB)
275 
276  setDatabase(db);
277  readResourceProperty(-1); // read all resource properties from DB
278 
279  readDbMetaInfo();
280  QStringList subres = subResourceList();
281  int k;
282  for (k = 0; k < subres.size(); ++k)
283  readAvailColumns(subres[k]);
284 
285  KLFLibDBEnginePropertyChangeNotifier *dbNotifier = dbPropertyNotifierInstance(db.connectionName());
286  connect(dbNotifier, SIGNAL(resourcePropertyChanged(int)),
287  this, SLOT(resourcePropertyUpdate(int)));
288  connect(dbNotifier, SIGNAL(subResourcePropertyChanged(const QString&, int)),
289  this, SLOT(subResourcePropertyUpdate(const QString&, int)));
290 
291  dbNotifier->ref();
292 }
293 
295 {
296  pDBConnectionName = pDB.connectionName();
297  KLFLibDBEnginePropertyChangeNotifier *dbNotifier = dbPropertyNotifierInstance(pDBConnectionName);
298  if (dbNotifier->deRef() && pAutoDisconnectDB) {
299  pDB.close();
300  pAutoDisconnectDB = true;
301  } else {
302  pAutoDisconnectDB = false;
303  }
304 }
305 
306 // private
307 bool KLFLibDBEngine::tableExists(const QString& subResource) const
308 {
309  return pDB.tables().contains(dataTableName(subResource), Qt::CaseInsensitive);
310 }
311 
312 // static
313 QString KLFLibDBEngine::dataTableName(const QString& subResource)
314 {
315  return "t_"+subResource.toLower();
316 }
317 // static
318 QString KLFLibDBEngine::quotedDataTableName(const QString& subResource)
319 {
320  QString dtname = dataTableName(subResource);
321  dtname.replace('"', "\"\"");
322  return '"' + dtname + '"';
323 }
324 
325 
326 uint KLFLibDBEngine::compareUrlTo(const QUrl& other, uint interestFlags) const
327 {
328  // we can only test for these flags (see doc for KLFLibResourceEngine::compareUrlTo())
329  interestFlags = interestFlags & (KlfUrlCompareBaseEqual);
330  // and we have to compare sub-resources case-insensitive (SQL table names)
332 
333  return klfUrlCompare(url(), other, interestFlags);
334 }
335 
336 bool KLFLibDBEngine::canModifyData(const QString& subResource, ModifyType mt) const
337 {
338  if ( !KLFLibResourceEngine::canModifyData(subResource, mt) )
339  return false;
340  if ( !validDatabase() )
341  return false;
342 
345  return true;
346 }
347 
348 bool KLFLibDBEngine::canModifyProp(int propId) const
349 {
351 }
352 bool KLFLibDBEngine::canRegisterProperty(const QString& /*propName*/) const
353 {
354  // we can register properties if we can write them
355  return canModifyProp(-1);
356 }
357 
359 {
360  return pDB.isOpen();
361 }
362 
364 {
365  pDB = db;
366 }
367 
368 // private
369 bool KLFLibDBEngine::saveResourceProperty(int propId, const QVariant& value)
370 {
372 
373  KLF_ASSERT_CONDITION( validDatabase() , "Database connection not valid!" ,
374  return false; ) ;
375 
376  QString propName = propertyNameForId(propId);
377  if ( propName.isEmpty() )
378  return false;
379 
380  if ( KLFPropertizedObject::property(propId) == value )
381  return true;
382 
383  {
384  QSqlQuery q = QSqlQuery(pDB);
385  q.prepare("DELETE FROM klf_properties WHERE name = ?");
386  q.addBindValue(propName);
387  bool r = q.exec();
388  if ( !r || q.lastError().isValid() ) {
389  qWarning()<<"KLFLibDBEngine::setRes.Property("<<propId<<","<<value<<"): can't DELETE!\n\t"
390  <<q.lastError().text()<<"\n\tSQL="<<q.lastQuery();
391  return false;
392  }
393  }
394  {
395  QSqlQuery q = QSqlQuery(pDB);
396  q.prepare("INSERT INTO klf_properties (name,value) VALUES (?,?)");
397  q.bindValue(0, propName);
398  q.bindValue(1, convertVariantToDBData(value));
399  if ( ! q.exec() || q.lastError().isValid() ) {
400  qWarning()<<"KLFLibDBEngine::setRes.Property("<<propId<<","<<value<<"): can't INSERT!\n\t"
401  <<q.lastError().text()<<"\n\tSQL="<<q.lastQuery();
402  return false;
403  }
404  }
405  KLFPropertizedObject::setProperty(propId, value);
406  dbPropertyNotifierInstance(pDB.connectionName())->notifyResourcePropertyChanged(propId);
407  // will be emitted by own slot from above call
408  // emit resourcePropertyChanged(propId);
409  return true;
410 }
411 
412 void KLFLibDBEngine::resourcePropertyUpdate(int propId)
413 {
414  readResourceProperty(propId);
415 }
416 
417 void KLFLibDBEngine::subResourcePropertyUpdate(const QString& subResource, int propId)
418 {
419  emit subResourcePropertyChanged(subResource, propId);
420 }
421 
422 void KLFLibDBEngine::readResourceProperty(int propId)
423 {
424  KLF_ASSERT_CONDITION( validDatabase() , "Database connection not valid!" ,
425  return ) ;
426 
427  QString sqlstr = "SELECT name,value FROM klf_properties";
428  QString propName;
429  if (propId >= 0) {
430  if (!propertyIdRegistered(propId)) {
431  qWarning()<<"Can't read un-registered resource property "<<propId<<" !";
432  return;
433  }
434  sqlstr += " WHERE name = ?";
435  propName = propertyNameForId(propId);
436  }
437  // read resource properties from database
438  QSqlQuery q = QSqlQuery(pDB);
439  q.prepare("SELECT name,value FROM klf_properties");
440  q.exec();
441  while (q.next()) {
442  QString propname = q.value(0).toString();
443  int propId = propertyIdForName(propname);
444  if (!propertyNameRegistered(propname)) {
445  if (!canRegisterProperty(propname))
446  continue;
448  }
449  QVariant propvalue = convertVariantFromDBData(q.value(1));
450  klfDbg( "Setting property `"<<propname<<"' (id #"<<propId<<") to "<<propvalue<<"" ) ;
451  KLFPropertizedObject::setProperty(propId, propvalue);
452  emit resourcePropertyChanged(propId);
453  }
454 }
455 
456 void KLFLibDBEngine::readDbMetaInfo()
457 {
458  QSqlQuery q = QSqlQuery(pDB);
459  q.prepare("SELECT name,value FROM klf_dbmetainfo");
460  bool r = q.exec();
461  if ( !r || q.lastError().isValid() ) {
462  qWarning()<<KLF_FUNC_NAME<<": unable to fetch DB meta-info: "<<q.lastError().text();
463  return;
464  }
465  while (q.next()) {
466  QString name = q.value(0).toString();
467  QString version = q.value(1).toString();
468  if (name == QLatin1String("klf_dbversion")) {
469  pDBVersion = version.toInt();
470  }
471  }
472 }
473 void KLFLibDBEngine::readAvailColumns(const QString& subResource)
474 {
475  QSqlRecord rec = pDB.record(dataTableName(subResource));
476  QStringList columns;
477  int k;
478  for (k = 0; k < rec.count(); ++k)
479  columns << rec.fieldName(k);
480 
481  pDBAvailColumns[subResource] = columns;
482 }
483 
484 
485 
486 // private
487 QStringList KLFLibDBEngine::columnNameList(const QString& subResource, const QList<int>& entryPropList,
488  bool wantIdFirst)
489 {
490  QStringList cols;
491  KLFLibEntry dummy; // to get prop name
492  int k;
493  for (k = 0; k < entryPropList.size(); ++k) {
494  QString col = dummy.propertyNameForId(entryPropList[k]);
495  if (pDBAvailColumns[subResource].contains(col))
496  cols << col;
497  else if (entryPropList[k] == KLFLibEntry::PreviewSize) // previewsize not available, use preview
498  cols << "Preview";
499  }
500  if (entryPropList.size() == 0) {
501  cols << "*";
502  }
503  if (wantIdFirst && (!cols.size() || cols[0] != "id") )
504  cols.prepend("id");
505 
506  return cols;
507 }
508 // private
509 QStringList KLFLibDBEngine::detectEntryColumns(const QSqlQuery& q)
510 {
511  const QSqlRecord rec = q.record();
512  QStringList cols;
513  KLFLibEntry dummy; // to get prop ID and possibly register property
514  int k;
515  for (k = 0; k < rec.count(); ++k) {
516  QString propName = rec.fieldName(k);
517  int propId = dummy.propertyIdForName(propName);
518  if (propId < 0 && propName != "id") { // don't register field "id"
519  klfDbg( "Registering property "<<propName ) ;
520  dummy.setEntryProperty(propName, QVariant()); // register property.
521  }
522  cols << propName;
523  }
524  return cols;
525 }
526 // private
527 KLFLibEntry KLFLibDBEngine::readEntry(const QSqlQuery& q, const QStringList& cols)
528 {
529  // and actually read the result and return it
531  int k;
532  for (k = 0; k < cols.size(); ++k) {
533  QVariant v = q.value(k);
534  if (cols[k] == "id")
535  continue;
536  int propId = entry.propertyIdForName(cols[k]);
537  QVariant value = dbReadEntryPropertyValue(q.value(k), propId);
538  entry.setEntryProperty(cols[k], value);
539  }
540  // klfDbg( ": cols="<<cols.join(",") ) ;
541  // klfDbg( ": read entry="<<entry<<" previewsize="<<entry.property(KLFLibEntry::PreviewSize)<<"; it's valid="<<entry.property(KLFLibEntry::PreviewSize).toSize().isValid()<<"; preview=... /null="<<entry.property(KLFLibEntry::Preview).value<QImage>().isNull() ) ;
542  // add a preview size if necessary
543  const QImage& preview = entry.property(KLFLibEntry::Preview).value<QImage>();
544  if (!preview.isNull()) {
545  const QSize& s = entry.property(KLFLibEntry::PreviewSize).toSize();
546  if (!s.isValid() || s != preview.size()) {
547  klfDbg( ": missing or incorrect preview size set to "<<entry.preview().size() ) ;
548  entry.setPreviewSize(entry.preview().size());
549  }
550  }
551  return entry;
552 }
553 
554 
556 {
557  KLF_ASSERT_CONDITION( validDatabase() , "Database connection not valid!" ,
558  return QList<KLFLib::entryId>() ) ;
559 
560  QSqlQuery q = QSqlQuery(pDB);
561  q.prepare(QString("SELECT id FROM %1").arg(quotedDataTableName(subResource)));
562  q.setForwardOnly(true);
563  bool r = q.exec();
564  if ( ! r || q.lastError().isValid() ) {
565  qWarning("KLFLibDBEngine::allIds: Error fetching IDs!\n"
566  "SQL Error: %s", qPrintable(q.lastError().text()));
567  return QList<KLFLib::entryId>();
568  }
569  QList<KLFLib::entryId> idlist;
570  while (q.next()) {
571  idlist << q.value(0).toInt();
572  }
573  return idlist;
574 }
575 bool KLFLibDBEngine::hasEntry(const QString& subResource, entryId id)
576 {
577  KLF_ASSERT_CONDITION( validDatabase() , "Database connection not valid!" ,
578  return false ) ;
579 
580  QSqlQuery q = QSqlQuery(pDB);
581  q.prepare(QString("SELECT id FROM %1 WHERE id = ?").arg(quotedDataTableName(subResource)));
582  q.addBindValue(id);
583  bool r = q.exec();
584  if ( ! r || q.lastError().isValid() ) {
585  qWarning("KLFLibDBEngine::hasEntry: Error!\n"
586  "SQL Error: %s", qPrintable(q.lastError().text()));
587  return false;
588  }
589  if (q.next())
590  return true;
591  return false;
592 }
594 /* */ KLFLibDBEngine::entries(const QString& subResource, const QList<KLFLib::entryId>& idList,
595  const QList<int>& wantedEntryProperties)
596 {
597  KLF_DEBUG_BLOCK(KLF_FUNC_NAME); klfDbg( "\t: subResource="<<subResource<<"; idlist="<<idList ) ;
598  KLF_ASSERT_CONDITION( validDatabase() , "Database connection not valid!" ,
599  return QList<KLFLibEntryWithId>() ) ;
600  if (idList.isEmpty())
601  return QList<KLFLibEntryWithId>();
602 
603  QStringList cols = columnNameList(subResource, wantedEntryProperties, true);
604  if (cols.contains("*")) {
605  cols = QStringList();
606  cols << "id" // first column is ID.
607  << pDBAvailColumns[subResource];
608  }
609 
610  QSqlQuery q = QSqlQuery(pDB);
611  q.prepare(QString("SELECT %1 FROM %2 WHERE id = ?").arg(cols.join(","),
612  quotedDataTableName(subResource)));
613 
614  KLFProgressReporter progr(0, idList.size(), this);
616  emit operationStartReportingProgress(&progr, tr("Fetching items from library database ..."));
617 
619 
620  int k;
621  for (k = 0; k < idList.size(); ++k) {
622  if (k % 10 == 0)
623  progr.doReportProgress(k);
624 
625  q.bindValue(0, idList[k]);
626  bool r = q.exec();
627  if ( !r || q.lastError().isValid() ) {
628  klfDbg( " SQL Error, sql="<<q.lastQuery()<<"; boundvalues="<<q.boundValues() ) ;
629  qWarning("KLFLibDBEngine::entries: Error\n"
630  "SQL Error (?): %s", qPrintable(q.lastError().text()));
631  continue;
632  }
633  if ( !q.next() ) {
634  klfDbg( ": id="<<idList[k]<<" does not exist in DB." ) ;
635  KLFLibEntryWithId e; e.entry = KLFLibEntry(); e.id = -1;
636  eList << e;
637  continue;
638  }
639  // get the entry
641  e.entry = readEntry(q, cols);
642  e.id = q.value(0).toInt();
643  eList << e;
644  }
645 
646  progr.doReportProgress(idList.size());
647 
648  return eList;
649 }
650 
651 
653 {
654  s.replace("'", "''");
655  return "'" + s + "'";
656 }
657 
658 // note: does not enclose expression in parens
659 static QString make_like_condition(QString field, QString val, bool wildbefore, bool wildafter, bool casesensitive)
660 {
661  if (casesensitive) { // use GLOB for case sensitive match
662  QString globval = val;
663  if (wildbefore)
664  globval.prepend("*");
665  if (wildafter)
666  globval.append("*");
667  // we cannot escape special chars in GLOB -> use glob in conjunction with like
668  return field+" GLOB "+escape_sql_data_string(globval)+" AND "
669  + make_like_condition(field, val, wildbefore, wildafter, false);
670  } else {
671  // use LIKE for case-insensitive match
672  val.replace("%", "\\%");
673  val.replace("_", "\\_");
674  val.replace("\\", "\\\\");
675  val = escape_sql_data_string(val);
676  if (wildbefore)
677  val.prepend("%");
678  if (wildafter)
679  val.append("%");
680  return field+" LIKE '"+val+"' ESCAPE '\\' ";
681  }
682 }
683 
684 
685 
686 static QString make_sql_condition(const KLFLib::EntryMatchCondition m, QVariantList *placeholders,
687  bool *haspostsqlcondition, KLFLib::EntryMatchCondition *postsqlcondition)
688 {
691  *haspostsqlcondition = false;
692 
694  return "1";
695  }
697  QString condition;
699  KLFLibEntry dummyentry;
700  QString field = dummyentry.propertyNameForId(pm.propertyId());
701  condition += "(";
702  uint f = pm.matchFlags();
703  switch ( f & 0xFF ) { // the match type
704  case Qt::MatchExactly:
705  condition += field+" = ?";
706  if ((f & Qt::CaseSensitive) == 0)
707  condition += " COLLATE NOCASE";
708  if (f & Qt::MatchFixedString)
709  placeholders->append(pm.matchValueString());
710  else
711  placeholders->append(pm.matchValueString());
712  if (pm.matchValueString().isEmpty()) // allow this field to be sql-NULL
713  condition += " OR "+field+" IS NULL";
714  break;
715  case Qt::MatchContains:
716  condition += make_like_condition(field, pm.matchValueString(), true, true, (f & Qt::CaseSensitive));
717  break;
718  case Qt::MatchStartsWith:
719  condition += make_like_condition(field, pm.matchValueString(), true, false, (f & Qt::CaseSensitive));
720  break;
721  case Qt::MatchEndsWith:
722  condition += make_like_condition(field, pm.matchValueString(), false, true, (f & Qt::CaseSensitive));
723  break;
724  case Qt::MatchRegExp:
725  // sqlite does not support regexp natively
726  *haspostsqlcondition = true;
727  *postsqlcondition = m;
728  condition += "1";
729  break;
730  case Qt::MatchWildcard:
731  if (f & Qt::CaseSensitive) {
732  condition += field+" GLOB ? ";
733  } else {
734  condition += " lower("+field+") GLOB lower(?) ";
735  }
736  placeholders->append(pm.matchValueString());
737  break;
738  default:
739  qWarning()<<KLF_FUNC_NAME<<": unknown property match type flags: "<<f ;
740  return "0";
741  }
742  condition += ")";
743  return condition;
744  }
746  if (m.conditionList().size() == 0) {
747  qWarning()<<KLF_FUNC_NAME<<": condition list is empty for NOT match type!";
748  return "0";
749  }
750  KLFLib::EntryMatchCondition postm = KLFLib::EntryMatchCondition::mkMatchAll(); // has to be initialized to sth..
751  QString c = "(NOT " + make_sql_condition(m.conditionList()[0], placeholders,
752  haspostsqlcondition, &postm) ;
753  if (*haspostsqlcondition) {
754  *postsqlcondition = KLFLib::EntryMatchCondition::mkNegateMatch(postm);
755  }
756  return c;
757  }
760  static const char *w_and = " AND ";
761  static const char *w_or = " OR ";
762  const char * word = (m.type() == KLFLib::EntryMatchCondition::AndMatchType) ? w_and : w_or ;
764  if (clist.isEmpty())
765  return "1";
766  int k;
767  QString str;
768  QList<KLFLib::EntryMatchCondition> postconditionlist;
769  for (k = 0; k < clist.size(); ++k) {
770  if (k > 0)
771  str += word;
772 
774  bool thishaspostsql;
775  QString c = make_sql_condition(m.conditionList()[0], placeholders,
776  &thishaspostsql, &thispostm) ;
777  if (thishaspostsql) {
778  postconditionlist.append(thispostm);
779  }
780  str += c;
781  } // for
782  if (postconditionlist.size()) {
783  *haspostsqlcondition = true;
784  *postsqlcondition = (m.type() == KLFLib::EntryMatchCondition::OrMatchType)
785  ? KLFLib::EntryMatchCondition::mkOrMatch(postconditionlist)
786  : KLFLib::EntryMatchCondition::mkOrMatch(postconditionlist) ;
787  }
788  return str;
789  }
790  qWarning()<<KLF_FUNC_NAME<<": unknown entry match condition type: "<<m.type();
791  return "0";
792 }
793 
794 int KLFLibDBEngine::query(const QString& subResource, const Query& query, QueryResult *result)
795 {
797  klfDbg( "\t: subResource="<<subResource<<"; query="<<query ) ;
798 
799  KLF_ASSERT_CONDITION( validDatabase() , "Database connection not valid!" ,
800  return -1 ) ;
801 
802  QStringList cols = columnNameList(subResource, query.wantedEntryProperties, true);
803 
804  QString sql;
805  // prepare SQL string.
806  sql = QString("SELECT %1 FROM %2 ").arg(cols.join(","), quotedDataTableName(subResource));
807  QVariantList placeholders;
808  bool haspostsqlcondition = false;
810  QString wherecond = make_sql_condition(query.matchCondition, &placeholders, &haspostsqlcondition,
811  &postsqlcondition);
812  sql += " WHERE "+wherecond;
813 
815  if (haspostsqlcondition) {
816  // fallback on very rudimentary search
817  klfDbg("You are using a feature that is not natively implemented in KLFLibDBEngine: falling back to "
818  "rudimentary and slow implementation!");
819  return KLFLibResourceSimpleEngine::queryImpl(this, subResource, query, result);
820  }
821 
822  if (query.orderPropId != -1) {
823  sql += " ORDER BY "+KLFLibEntry().propertyNameForId(query.orderPropId)+" ";
824  sql += (query.orderDirection==Qt::AscendingOrder)?"ASC ":"DESC ";
825  }
826 
827  if (query.limit != -1) {
828  sql += " LIMIT "+QString::number(query.skip+query.limit);
829  }
830 
831  klfDbg("Built query: SQL="<<sql<<"; placeholders="<<placeholders) ;
832 
833  QSqlQuery q = QSqlQuery(pDB);
834  q.prepare(sql);
835  q.setForwardOnly(true);
836  int k;
837  for (k = 0; k < placeholders.size(); ++k)
838  q.bindValue(k, placeholders[k]);
839 
840  // and exec the query
841  bool r = q.exec();
842  if ( !r || q.lastError().isValid() ) {
843  qWarning()<<KLF_FUNC_NAME<<"SQL Error: "<<qPrintable(q.lastError().text())
844  <<"\nSql was="<<sql<<"; bound values="<<q.boundValues();
845  return -1;
846  }
847 
848  // retrieve the entries
849 
850  cols = detectEntryColumns(q);
851 
852  int N = q.size();
853  if (N == -1)
854  N = 100;
855  else
856  N -= query.skip;
857  KLFProgressReporter progr(0, N, this);
859  emit operationStartReportingProgress(&progr, tr("Querying items from library database ..."));
860 
861  // skip the first 'query.skip' entries
862  int skipped = 0;
863  bool ok = true;
864  while (skipped < query.skip && (ok = q.next()))
865  ++skipped;
866  klfDbg("skipped "<<skipped<<" entries.") ;
867 
868  // warning: Qt crashes on two consequent failing q.next() calls, if forward-only mode is enabled.
869 
870  int count = 0;
871  while (ok && q.next()) {
872  if (count % 10 == 0 && count < N) {
873  // emit every 10 items, without exceeding what maximum we gave
874  progr.doReportProgress(count);
875  }
876 
878  e.id = q.value(0).toInt(); // column 0 is 'id', see \ref columnNameList()
879  e.entry = readEntry(q, cols);
880 
882  result->entryIdList << e.id;
884  result->rawEntryList << e.entry;
886  result->entryWithIdList << e;
887  ++count;
888  }
889 
890  progr.doReportProgress(count);
891  klfDbg("got "<<count<<" entries.") ;
892  return count;
893 }
894 QList<QVariant> KLFLibDBEngine::queryValues(const QString& subResource, int entryPropId)
895 {
897  klfDbg( "\t: subResource="<<subResource<<"; entryPropId="<<entryPropId ) ;
898 
899  KLF_ASSERT_CONDITION( validDatabase() , "Database connection not valid!" ,
900  return QList<QVariant>() ) ;
901 
902  if (!pDBAvailColumns.contains(subResource) || !hasSubResource(subResource)) {
903  qWarning()<<KLF_FUNC_NAME<<": bad sub-resource: "<<subResource;
904  return QVariantList();
905  }
906 
907  KLFLibEntry dummye;
908  QString pname;
909  if (!dummye.propertyIdRegistered(entryPropId)) {
910  qWarning()<<KLF_FUNC_NAME<<": Invalid property ID "<<entryPropId;
911  return QVariantList();
912  }
913  pname = dummye.propertyNameForId(entryPropId);
914  if (!pDBAvailColumns[subResource].contains(pname)) {
915  qWarning()<<KLF_FUNC_NAME<<": property "<<pname<<" is not available in tables for sub-res "<<subResource
916  <<" (avail are "<<pDBAvailColumns[subResource]<<")";
917  return QVariantList();
918  }
919 
920  QString sql = "SELECT DISTINCT "+pname+" FROM "+quotedDataTableName(subResource);
921 
922  QSqlQuery q = QSqlQuery(pDB);
923  q.prepare(sql);
924  q.setForwardOnly(true);
925  bool r = q.exec();
926  if ( !r || q.lastError().isValid() ) {
927  qWarning()<<KLF_FUNC_NAME<<"SQL Error: "<<qPrintable(q.lastError().text())
928  <<"\nSQL was: "<<sql;
929  return QVariantList();
930  }
931 
932  QVariantList list;
933  while (q.next()) {
934  list << dbReadEntryPropertyValue(q.value(0), entryPropId);
935  klfDbg("adding value "<<list.last().toString()) ;
936  }
937 
938  return list;
939 }
940 
941 
942 
944 {
945  KLF_ASSERT_CONDITION( validDatabase() , "Database connection not valid!" ,
946  return KLFLibEntry() ) ;
947 
948  QSqlQuery q = QSqlQuery(pDB);
949  q.prepare(QString("SELECT * FROM %1 WHERE id = ?").arg(quotedDataTableName(subResource)));
950  q.addBindValue(id);
951  bool r = q.exec();
952 
953  if ( !r || q.lastError().isValid() || q.size() == 0) {
954  qWarning("KLFLibDBEngine::entry: id=%d cannot be found!\n"
955  "SQL Error (?): %s", id, qPrintable(q.lastError().text()));
956  return KLFLibEntry();
957  }
958  // if (q.size() != 1) {
959  // qWarning("KLFLibDBEngine::entry: %d results returned for id %d!", q.size(), id);
960  // return KLFLibEntry();
961  // }
962  if ( ! q.next() ) {
963  qWarning("KLFLibDBEngine::entry(): no entry available!\n"
964  "SQL=\"%s\" (?=%d)", qPrintable(q.lastQuery()), id);
965  return KLFLibEntry();
966  }
967 
968  // read the result and return it
969  KLFLibEntry e = readEntry(q, detectEntryColumns(q));
970  q.finish();
971  return e;
972 }
973 
974 
976 /* */ KLFLibDBEngine::allEntries(const QString& subResource, const QList<int>& wantedEntryProperties)
977 {
978  KLF_ASSERT_CONDITION( validDatabase() , "Database connection not valid!" ,
979  return QList<KLFLibEntryWithId>() ) ;
980 
981  QStringList cols = columnNameList(subResource, wantedEntryProperties, true);
982 
983  QSqlQuery q = QSqlQuery(pDB);
984  q.prepare(QString("SELECT %1 FROM %2 ORDER BY id ASC").arg(cols.join(","), quotedDataTableName(subResource)));
985  q.setForwardOnly(true);
986  bool r = q.exec();
987  if ( ! r || q.lastError().isValid() ) {
988  qWarning()<<KLF_FUNC_NAME<<": SQL ERROR. Sql="<<q.lastQuery()<<"\n\tError: "<<q.lastError().text()
989  <<"\nbound values: "<<q.boundValues();
990  return QList<KLFLibEntryWithId>();
991  }
992 
993  QList<KLFLibEntryWithId> entryList;
994 
995  cols = detectEntryColumns(q);
996 
997  int count = q.size();
998 
999  KLFProgressReporter progr(0, count, this);
1001  emit operationStartReportingProgress(&progr, tr("Fetching items from library database ..."));
1002 
1003  int n = 0;
1004  while (q.next()) {
1005  if (n % 10 == 0) // emit every 10 items
1006  progr.doReportProgress(n++);
1008  e.id = q.value(0).toInt(); // column 0 is 'id', see \ref columnNameList()
1009  e.entry = readEntry(q, cols);
1010  entryList << e;
1011  }
1012 
1013  progr.doReportProgress(count);
1014 
1015  return entryList;
1016 }
1017 
1018 
1020 {
1021  return QString::compare(defaultSubResource(), subResourceName, Qt::CaseInsensitive) == 0;
1022 }
1023 
1024 
1026 {
1027  return baseCanModifyStatus(false) == MS_CanModify;
1028 }
1029 
1030 bool KLFLibDBEngine::canDeleteSubResource(const QString& subResource) const
1031 {
1032  if (baseCanModifyStatus(true, subResource) == MS_CanModify)
1033  if (tableExists(subResource) && subResourceList().size() > 1)
1034  return true;
1035 
1036  return false;
1037 }
1038 
1039 QVariant KLFLibDBEngine::subResourceProperty(const QString& subResource, int propId) const
1040 {
1041  KLF_ASSERT_CONDITION( validDatabase() , "Database connection not valid!" ,
1042  return QVariant() ) ;
1043 
1044  QSqlQuery q = QSqlQuery(pDB);
1045  q.prepare("SELECT pvalue FROM klf_subresprops WHERE lower(subresource) = lower(?) AND pid = ?");
1046  q.addBindValue(QVariant::fromValue<QString>(subResource));
1047  q.addBindValue(QVariant::fromValue<int>(propId));
1048  int r = q.exec();
1049  if ( !r || q.lastError().isValid() ) {
1050  qWarning()<<"KLFLibDBEngine::subResourceProperty("<<subResource<<","<<propId<<"): SQL Error: "
1051  <<q.lastError().text() << "\n\t\tSQL: "<<q.lastQuery()<<"\n\t\tBound values: "<<q.boundValues();
1052  klfDbg("DB: "<<pDB.connectionName());
1053  return QVariant();
1054  }
1055  //klfDbg( "KLFLibDBEngine::subRes.Prop.(): SQL="<<q.lastQuery()<<"; vals="<<q.boundValues() ) ;
1056  //qDebug("KLFLibDBEngine::subResourceProperty: Got size %d, valid=%d", q.size(), (int)q.isValid());
1057  if ( !q.next() ) {
1058  // return some default values, at least to ensure the correct data type
1059  if (propId == SubResPropLocked)
1060  return QVariant(false);
1061  if (propId == SubResPropViewType)
1062  return QVariant("");
1063  if (propId == SubResPropTitle)
1064  return QVariant("");
1065  return QVariant();
1066  }
1067  return convertVariantFromDBData(q.value(0));
1068 }
1069 
1070 
1071 bool KLFLibDBEngine::hasSubResource(const QString& subRes) const
1072 {
1073  return tableExists(subRes);
1074 }
1075 
1077 {
1078  KLF_ASSERT_CONDITION( validDatabase() , "Database connection not valid!" ,
1079  return QStringList() ) ;
1080 
1081  QStringList allTables = pDB.tables();
1082  QStringList subreslist;
1083  int k;
1084  for (k = 0; k < allTables.size(); ++k) {
1085  if (allTables[k].startsWith("t_"))
1086  subreslist << allTables[k].mid(2);
1087  }
1088  return subreslist;
1089 }
1090 
1091 
1092 bool KLFLibDBEngine::setSubResourceProperty(const QString& subResource, int propId, const QVariant& value)
1093 {
1095 
1096  KLF_ASSERT_CONDITION( validDatabase() , "Database connection not valid!" ,
1097  return false ) ;
1098 
1099  if ( !canModifyProp(-1) )
1100  return false;
1101  if ( subResourceProperty(subResource, SubResPropLocked).toBool() && propId != SubResPropLocked )
1102  return false; // still allow us to unlock the sub-resource (!)
1103 
1104  klfDbg( ": setting sub-resource property "<<propId<<" to "<<value<<" in sub-res "
1105  <<subResource ) ;
1106 
1107  if ( subResourceProperty(subResource, propId) == value ) {
1108  klfDbg("property already has the requested value "<<value<<".");
1109  return true;
1110  }
1111 
1112  {
1113  QSqlQuery q = QSqlQuery(pDB);
1114  q.prepare("DELETE FROM klf_subresprops WHERE lower(subresource) = lower(?) and pid = ?");
1115  q.bindValue(0, subResource);
1116  q.bindValue(1, propId);
1117  bool r = q.exec();
1118  if ( !r || q.lastError().isValid() ) {
1119  qWarning()<<"KLFLibDBEngine::setSubRes.Prop.("<<subResource<<","<<propId<<","<<value<<"):"
1120  <<" can't DELETE!\n\t"<<q.lastError().text()<<"\n\tBound values="<<q.boundValues();
1121  return false;
1122  }
1123  }
1124  {
1125  QSqlQuery q = QSqlQuery(pDB);
1126  q.prepare("INSERT INTO klf_subresprops (subresource,pid,pvalue) VALUES (?,?,?)");
1127  q.bindValue(0, subResource);
1128  q.bindValue(1, propId);
1129  q.bindValue(2, convertVariantToDBData(value));
1130  if ( ! q.exec() || q.lastError().isValid() ) {
1131  qWarning()<<"KLFLibDBEngine::setSubRes.Prop.("<<subResource<<","<<propId<<","<<value<<"):"
1132  <<" can't INSERT!\n\t"<<q.lastError().text()<<"\n\tBound values="<<q.boundValues();
1133  return false;
1134  }
1135  }
1136 
1137  dbPropertyNotifierInstance(pDB.connectionName())
1138  ->notifySubResourcePropertyChanged(subResource, propId);
1139  // will be emitted by own slot from above call
1140  // qDebug("DBEngine::setSubResourceProperty: Emitting signal!");
1141  // emit subResourcePropertyChanged(subResource, propId);
1142 
1143  return true;
1144 }
1145 
1146 
1147 
1148 QVariant KLFLibDBEngine::dbMakeEntryPropertyValue(const QVariant& entryval, int propertyId)
1149 {
1150  if (propertyId == KLFLibEntry::Latex)
1151  return QVariant::fromValue<QString>(entryval.toString());
1152  if (propertyId == KLFLibEntry::DateTime)
1153  return QVariant::fromValue<qulonglong>(entryval.toDateTime().toTime_t());
1154  if (propertyId == KLFLibEntry::Preview)
1155  return QVariant::fromValue<QByteArray>(image_data(entryval.value<QImage>(), "PNG"));
1156  if (propertyId == KLFLibEntry::Category)
1157  return QVariant::fromValue<QString>(entryval.toString());
1158  if (propertyId == KLFLibEntry::Tags)
1159  return QVariant::fromValue<QString>(entryval.toString());
1160  if (propertyId == KLFLibEntry::PreviewSize) {
1161  QSize s = entryval.value<QSize>();
1162  return QVariant::fromValue<qulonglong>( (((qulonglong)s.width()) << 32) |
1163  (((qulonglong)s.height()) & 0xFFFFFFFF) );
1164  }
1165  // otherwise, return a generic encapsulation
1166  return convertVariantToDBData(entryval);
1167 }
1168 QVariant KLFLibDBEngine::dbReadEntryPropertyValue(const QVariant& dbdata, int propertyId)
1169 {
1170  if (propertyId == KLFLibEntry::Latex)
1171  return dbdata.toString();
1172  if (propertyId == KLFLibEntry::DateTime)
1173  return QVariant::fromValue<QDateTime>(QDateTime::fromTime_t(dbdata.toULongLong()));
1174  if (propertyId == KLFLibEntry::Preview) {
1175  QImage img;
1176  img.loadFromData(dbdata.toByteArray(), "PNG");
1177  return QVariant::fromValue<QImage>(img);
1178  }
1179  if (propertyId == KLFLibEntry::Category)
1180  return dbdata.toString();
1181  if (propertyId == KLFLibEntry::Tags)
1182  return dbdata.toString();
1183  if (propertyId == KLFLibEntry::PreviewSize) {
1184  qulonglong val = dbdata.toULongLong();
1185  int w = (int)((val>>32) & 0xFFFFFFFF) ;
1186  int h = (int)(val & 0xFFFFFFFF) ;
1187  return QVariant::fromValue<QSize>(QSize(w, h));
1188  }
1189  // otherwise, return the generic decapsulation
1190  return convertVariantFromDBData(dbdata);
1191 }
1192 
1193 
1194 
1195 
1196 
1197 // private
1198 QVariant KLFLibDBEngine::convertVariantToDBData(const QVariant& value) const
1199 {
1200  // setup data in proper format if needed
1201 
1202  if (!value.isValid())
1203  return QVariant();
1204 
1205  int t = value.type();
1206  const char *ts = value.typeName();
1207  if (t == QVariant::Int || t == QVariant::UInt || t == QVariant::LongLong || t == QVariant::ULongLong ||
1208  t == QVariant::Double || t == QVariant::Bool)
1209  return value; // value is OK
1210 
1211  // these types are to be converted to string
1212  if (t == QVariant::String)
1213  return encaps(ts, value.toString());
1214 
1215  // these types are to be converted by byte array
1216  if (t == QVariant::ByteArray)
1217  return encaps(ts, value.toByteArray());
1218  if (t == QVariant::DateTime)
1219  return encaps(ts, value.value<QDateTime>().toString(Qt::ISODate).toLatin1());
1220  if (t == QVariant::Image)
1221  return encaps(ts, image_data(value.value<QImage>(), "PNG"));
1222 
1223  // any other case: convert metatype to QByteArray.
1224  QByteArray valuedata;
1225  { QDataStream stream(&valuedata, QIODevice::WriteOnly);
1226  stream.setVersion(QDataStream::Qt_4_4);
1227  stream << value; }
1228  return encaps(ts, valuedata);
1229 }
1230 
1231 QVariant KLFLibDBEngine::encaps(const char *ts, const QString& data) const
1232 {
1233  return QVariant::fromValue<QString>(QString("[")+ts+"]"+data);
1234 }
1235 QVariant KLFLibDBEngine::encaps(const char *ts, const QByteArray& data) const
1236 {
1237  QByteArray edata;
1238  edata.append("[");
1239  edata.append(ts);
1240  edata.append("]");
1241  edata.append(data);
1242  return QVariant::fromValue<QByteArray>(edata);
1243 }
1244 QVariant KLFLibDBEngine::convertVariantFromDBData(const QVariant& dbdata) const
1245 {
1246  if ( !dbdata.isValid() )
1247  return QVariant();
1248 
1249  int t = dbdata.type();
1250  if (t == QVariant::Int || t == QVariant::UInt || t == QVariant::LongLong || t == QVariant::ULongLong ||
1251  t == QVariant::Double || t == QVariant::Bool)
1252  return dbdata; // value is OK
1253 
1254  if (t == QVariant::String)
1255  return decaps(dbdata.toString());
1256  if (t == QVariant::ByteArray)
1257  return decaps(dbdata.toByteArray());
1258 
1259  qWarning()<<"Unexpected DB data variant found: "<<dbdata;
1260  return QVariant();
1261 }
1262 QVariant KLFLibDBEngine::decaps(const QString& sdata) const
1263 {
1264  return decaps(sdata.toUtf8());
1265 }
1266 QVariant KLFLibDBEngine::decaps(const QByteArray& data) const
1267 {
1268  // klfDbg( "decaps(): "<<data ) ;
1269  int k;
1270  if (!data.size())
1271  return QVariant();
1272  if (data[0] != '[')
1273  return QVariant::fromValue<QString>(QString::fromUtf8(data));
1274  for (k = 1; k < data.size() && data[k] != ']'; ++k) ;
1275  if (k >= data.size()) {
1276  qWarning()<<"KLFLibDBEngine::decaps(QB.A.): bad data:"<<data;
1277  return QVariant();
1278  }
1279  const QByteArray typenam = data.mid(1, k-1);
1280  const QByteArray valuedata = data.mid(k+1);
1281 
1282  if (typenam == "bool") {
1283  QString svaluedata = QString::fromUtf8(valuedata).trimmed();
1284  return QVariant::fromValue<bool>(svaluedata[0] != '0' ||
1285  (svaluedata != "1" && (svaluedata[0].toLower() == 't' ||
1286  svaluedata[0].toLower() == 'y' ||
1287  svaluedata.toInt() != 0)) );
1288 }
1289  if (typenam == "QString")
1290  return QVariant::fromValue<QString>(QString::fromUtf8(valuedata));
1291  if (typenam == "QByteArray")
1292  return QVariant::fromValue<QByteArray>(valuedata);
1293  if (typenam == "QDateTime")
1294  return QDateTime::fromString(QString::fromLatin1(valuedata), Qt::ISODate);
1295  if (typenam == "QImage") {
1296  QImage img;
1297  img.loadFromData(valuedata);
1298  return QVariant::fromValue<QImage>(img);
1299  }
1300 
1301  // OTHERWISE, load from datastream save:
1302 
1303  QVariant value;
1304  { QDataStream stream(valuedata);
1305  stream.setVersion(QDataStream::Qt_4_4);
1306  stream >> value; }
1307  return value;
1308 }
1309 
1310 bool KLFLibDBEngine::ensureDataTableColumnsExist(const QString& subResource, const QStringList& columnList)
1311 {
1312  QSqlRecord rec = pDB.record(dataTableName(subResource));
1313  int k;
1314  bool failed = false;
1315  for (k = 0; k < columnList.size(); ++k) {
1316  if (columnList[k] == "*") // in case a superfluous '*' remained in a 'cols' stringlist... in eg. entries()
1317  continue;
1318  if (rec.contains(columnList[k]))
1319  continue;
1320  QSqlQuery sql = QSqlQuery(pDB);
1321  sql.prepare("ALTER TABLE "+quotedDataTableName(subResource)+" ADD COLUMN "+columnList[k]+" BLOB");
1322  bool r = sql.exec();
1323  if (!r || sql.lastError().isValid()) {
1324  qWarning()<<"KLFLibDBEngine::ensureDataTableColumnsExist("<<subResource<<"): Can't add column "
1325  <<columnList[k]<<"!\n"<<sql.lastError().text()<<" SQL="<<sql.lastQuery();
1326  failed = true;
1327  }
1328  }
1329 
1330  readAvailColumns(subResource);
1331 
1332  return !failed;
1333 }
1334 bool KLFLibDBEngine::ensureDataTableColumnsExist(const QString& subResource)
1335 {
1336  KLFLibEntry dummy;
1337  QSqlRecord rec = pDB.record(dataTableName(subResource));
1338  QStringList propNameList = dummy.registeredPropertyNameList();
1339  return ensureDataTableColumnsExist(subResource, propNameList);
1340 }
1341 
1342 
1343 // --
1344 
1345 
1347 {
1348  KLF_ASSERT_CONDITION( validDatabase() , "Database connection not valid!" ,
1349  return false ) ;
1350 
1351  if (!canDeleteSubResource(subResource))
1352  return false;
1353 
1354  QSqlQuery q = QSqlQuery(pDB);
1355  q.prepare(QString("DROP TABLE %1").arg(quotedDataTableName(subResource)));
1356  int r = q.exec();
1357  if ( !r || q.lastError().isValid() ) {
1358  qWarning()<<KLF_FUNC_NAME<<"("<<subResource<<"): SQL Error: "
1359  <<q.lastError().text() << "\n\tSQL="<<q.lastQuery() ;
1360  return false;
1361  }
1362 
1363  // all ok
1364  emit subResourceDeleted(subResource);
1365 
1366  if (subResource == defaultSubResource()) {
1367  QString newDefaultSubResource;
1368  if (subResourceList().size() >= 1)
1369  newDefaultSubResource = subResourceList()[0];
1370  else
1371  newDefaultSubResource = QString();
1372  setDefaultSubResource(newDefaultSubResource);
1373  emit defaultSubResourceChanged(newDefaultSubResource);
1374  }
1375  return true;
1376 }
1377 
1378 
1379 bool KLFLibDBEngine::createSubResource(const QString& subResource, const QString& subResourceTitle)
1380 {
1381 
1382  KLF_ASSERT_CONDITION( validDatabase() , "Database connection not valid!" ,
1383  return false ) ;
1384 
1385  if ( subResource.isEmpty() )
1386  return false;
1387 
1388  if ( subResourceList().contains(subResource) ) {
1389  qWarning()<<"KLFLibDBEngine::createSubResource: Sub-Resource "<<subResource<<" already exists!";
1390  return false;
1391  }
1392 
1393  bool r = createFreshDataTable(pDB, subResource);
1394  if (!r)
1395  return false;
1396  QString title = subResourceTitle;
1397  if (title.isEmpty())
1398  title = subResource;
1399  r = setSubResourceProperty(subResource, SubResPropTitle, QVariant(title));
1400  if (!r)
1401  return false;
1402 
1403  // success
1404  emit subResourceCreated(subResource);
1405 
1406  return true;
1407 }
1408 
1409 
1411  const KLFLibEntryList& entrylist)
1412 {
1413  int k, j;
1414 
1415  KLF_ASSERT_CONDITION( validDatabase() , "Database connection not valid!" ,
1417 
1418  klfDbg("subres="<<subres<<"; entrylist="<<entrylist) ;
1419 
1420  if ( entrylist.size() == 0 ) {
1421  return QList<entryId>();
1422  }
1423 
1424  if ( !canModifyData(subres, InsertData) ) {
1425  klfDbg("can't modify data.") ;
1426  return QList<entryId>();
1427  }
1428 
1429  if ( !tableExists(subres) ) {
1430  qWarning()<<KLF_FUNC_NAME<<": Sub-Resource "<<subres<<" does not exist.";
1431  return QList<entryId>();
1432  }
1433 
1434  KLFLibEntry e; // dummy object to test for properties
1435  QList<int> propids = e.registeredPropertyIdList();
1436  QStringList props;
1437  QStringList questionmarks;
1438  for (k = 0; k < propids.size(); ++k) {
1439  props << e.propertyNameForId(propids[k]);
1440  questionmarks << "?";
1441  }
1442 
1443  QList<entryId> insertedIds;
1444 
1445  ensureDataTableColumnsExist(subres);
1446 
1447  KLFProgressReporter progr(0, entrylist.size(), this);
1449  emit operationStartReportingProgress(&progr, tr("Inserting items into library database ..."));
1450 
1451  QSqlQuery q = QSqlQuery(pDB);
1452  q.prepare("INSERT INTO " + quotedDataTableName(subres) + " (" + props.join(",") + ") "
1453  " VALUES (" + questionmarks.join(",") + ")");
1454  klfDbg( "INSERT query: "<<q.lastQuery() ) ;
1455  // now loop all entries, and exec the query with appropriate bound values
1456  for (j = 0; j < entrylist.size(); ++j) {
1457  if (j % 10 == 0) // emit every 10 items
1458  progr.doReportProgress(j);
1459  // klfDbg( "New entry to insert." ) ;
1460  for (k = 0; k < propids.size(); ++k) {
1461  QVariant data = dbMakeEntryPropertyValue(entrylist[j].property(propids[k]), propids[k]);
1462  // and add a corresponding bind value for sql query
1463  klfDbg( "Binding value "<<k<<": "<<data ) ;
1464  q.bindValue(k, data);
1465  }
1466  // and exec the query with these bound values
1467  bool r = q.exec();
1468  if ( ! r || q.lastError().isValid() ) {
1469  qWarning()<<"INSERT failed! SQL Error: "<<q.lastError().text()<<"\n\tSQL="<<q.lastQuery();
1470  insertedIds << -1;
1471  } else {
1472  QVariant v_id = q.lastInsertId();
1473  if ( ! v_id.isValid() )
1474  insertedIds << -2;
1475  else
1476  insertedIds << v_id.toInt();
1477  }
1478  }
1479 
1480  // make sure the last signal is emitted as specified by KLFLibResourceEngine doc (needed
1481  // for example to close progress dialog!)
1482  progr.doReportProgress(entrylist.size());
1483 
1484  emit dataChanged(subres, InsertData, insertedIds);
1485  return insertedIds;
1486 }
1487 
1488 
1489 bool KLFLibDBEngine::changeEntries(const QString& subResource, const QList<entryId>& idlist,
1490  const QList<int>& properties, const QList<QVariant>& values)
1491 {
1492  if ( ! validDatabase() )
1493  return false;
1494  if ( ! canModifyData(subResource, ChangeData) )
1495  return false;
1496 
1497  if ( !tableExists(subResource) ) {
1498  qWarning()<<KLF_FUNC_NAME<<": Sub-Resource "<<subResource<<" does not exist.";
1499  return false;
1500  }
1501 
1502  if ( properties.size() != values.size() ) {
1503  qWarning("KLFLibDBEngine::changeEntry(): properties' and values' sizes mismatch!");
1504  return false;
1505  }
1506 
1507  if ( idlist.size() == 0 )
1508  return true; // no items to change
1509 
1510  klfDbg( "KLFLibDBEngine::changeEntries: funcional tests passed; idlist="<<idlist<<" props="
1511  <<properties<<" vals="<<values ) ;
1512 
1513  ensureDataTableColumnsExist(subResource);
1514 
1515  KLFLibEntry e; // dummy
1516  QStringList updatepairs;
1517  int k;
1518  for (k = 0; k < properties.size(); ++k) {
1519  updatepairs << (e.propertyNameForId(properties[k]) + " = ?");
1520  }
1521  // prepare query
1522  QSqlQuery q = QSqlQuery(pDB);
1523  q.prepare(QString("UPDATE %1 SET %2 WHERE id = ?")
1524  .arg(quotedDataTableName(subResource), updatepairs.join(",")));
1525  for (k = 0; k < properties.size(); ++k) {
1526  q.bindValue(k, dbMakeEntryPropertyValue(values[k], properties[k]));
1527  }
1528  const int idBindValueNum = k;
1529 
1530  KLFProgressReporter progr(0, idlist.size(), this);
1532  emit operationStartReportingProgress(&progr, tr("Changing entries in database ..."));
1533 
1534  bool failed = false;
1535  for (k = 0; k < idlist.size(); ++k) {
1536  if (k % 10 == 0)
1537  progr.doReportProgress(k);
1538 
1539  q.bindValue(idBindValueNum, idlist[k]);
1540  bool r = q.exec();
1541  if ( !r || q.lastError().isValid() ) {
1542  qWarning() << "SQL UPDATE Error: "<<q.lastError().text()<<"\nWith SQL="<<q.lastQuery()
1543  <<";\n and bound values="<<q.boundValues();
1544  failed = true;
1545  }
1546  }
1547 
1548  progr.doReportProgress(idlist.size());
1549 
1550  emit dataChanged(subResource, ChangeData, idlist);
1551 
1552  return !failed;
1553 }
1554 
1555 bool KLFLibDBEngine::deleteEntries(const QString& subResource, const QList<entryId>& idlist)
1556 {
1557  if ( ! validDatabase() )
1558  return false;
1559  if (idlist.size() == 0)
1560  return true; // nothing to do
1561  if ( ! canModifyData(subResource, DeleteData) )
1562  return false;
1563 
1564  if ( !tableExists(subResource) ) {
1565  qWarning()<<KLF_FUNC_NAME<<": Sub-Resource "<<subResource<<" does not exist.";
1566  return false;
1567  }
1568 
1569  int k;
1570  bool failed = false;
1571 
1572  QString sql = QString("DELETE FROM %1 WHERE id = ?").arg(quotedDataTableName(subResource));
1573 
1574  klfDbg("sql is "<<sql<<", idlist is "<<idlist) ;
1575 
1576  QSqlQuery q = QSqlQuery(pDB);
1577  q.prepare(sql);
1578 
1579  KLFProgressReporter progr(0, idlist.size(), this);
1581  emit operationStartReportingProgress(&progr, tr("Removing entries from database ..."));
1582 
1583  for (k = 0; k < idlist.size(); ++k) {
1584  if (k % 10 == 0)
1585  progr.doReportProgress(k);
1586 
1587  q.bindValue(0, idlist[k]);
1588  bool r = q.exec();
1589  if ( !r || q.lastError().isValid() ) {
1590  qWarning()<<KLF_FUNC_NAME<<": Sql error: "<<q.lastError().text();
1591  failed = true;
1592  continue;
1593  }
1594  }
1595 
1596  progr.doReportProgress(idlist.size());
1597 
1598  emit dataChanged(subResource, DeleteData, idlist);
1599 
1600  return !failed;
1601 }
1602 
1603 bool KLFLibDBEngine::saveTo(const QUrl& newPath)
1604 {
1605  if (newPath.scheme() == QLatin1String("klf+sqlite") && url().scheme() == QLatin1String("klf+sqlite")) {
1606  if (!newPath.host().isEmpty()) {
1607  qWarning()<<"KLFLibDBEngine::saveTo("<<newPath<<"): Expected empty host!";
1608  return false;
1609  }
1611  }
1612  qWarning()<<"KLFLibDBEngine::saveTo("<<newPath<<"): Bad scheme!";
1613  return false;
1614 }
1615 
1616 // static
1617 bool KLFLibDBEngine::initFreshDatabase(QSqlDatabase db)
1618 {
1619  if ( ! db.isOpen() )
1620  return false;
1621 
1622  // the CREATE TABLE statements should be adapted to the database type (sqlite, mysql, pgsql) since
1623  // syntax is slightly different...
1624  QStringList sql;
1625  sql << "CREATE TABLE klf_properties (id INTEGER PRIMARY KEY, name TEXT, value BLOB)";
1626  sql << "INSERT INTO klf_properties (name, value) VALUES ('Title', 'New Resource')";
1627  sql << "INSERT INTO klf_properties (name, value) VALUES ('Locked', 'false')";
1628  sql << "CREATE TABLE klf_dbmetainfo (id INTEGER PRIMARY KEY, name TEXT, value BLOB)";
1629  sql << "INSERT INTO klf_dbmetainfo (name, value) VALUES ('klf_version', '" KLF_VERSION_STRING "')";
1630  sql << "INSERT INTO klf_dbmetainfo (name, value) VALUES ('klf_dbversion', '"+
1631  QString::number(1)+"')";
1632  sql << "CREATE TABLE klf_subresprops (id INTEGER PRIMARY KEY, pid INTEGER, subresource TEXT, pvalue BLOB)";
1633 
1634  int k;
1635  for (k = 0; k < sql.size(); ++k) {
1636  QSqlQuery query(db);
1637  query.prepare(sql[k]);
1638  bool r = query.exec();
1639  if ( !r || query.lastError().isValid() ) {
1640  qWarning()<<"KLFLibDBEngine::initFreshDatabase(): SQL Error: "<<query.lastError().text()<<"\n"
1641  <<"SQL="<<sql[k];
1642  return false;
1643  }
1644  }
1645  return true;
1646 }
1647 
1648 // static
1649 bool KLFLibDBEngine::createFreshDataTable(QSqlDatabase db, const QString& subres)
1650 {
1651  qDebug("KLFLibDBEngine::createFreshDataTable(.., '%s')", qPrintable(subres));
1652  QString datatablename = dataTableName(subres);
1653  if ( ! db.isOpen() ) {
1654  qWarning("KLFLibDBEngine::createFreshDataTable(..,%s): DB is not open!", qPrintable(subres));
1655  return false;
1656  }
1657 
1658  if ( db.tables().contains(datatablename) ) {
1659  qWarning("KLFLibDBEngine::createFreshDataTable(..,%s): table %s exists!", qPrintable(subres),
1660  qPrintable(datatablename));
1661  return false;
1662  }
1663  QString qdtname = quotedDataTableName(subres);
1664 
1665 
1666  QSqlQuery query(db);
1667  query.prepare(QString("")+
1668  "CREATE TABLE "+qdtname+" (id INTEGER PRIMARY KEY, Latex TEXT, DateTime TEXT, "
1669  " Preview BLOB, PreviewSize TEXT, Category TEXT, Tags TEXT, Style BLOB)");
1670  bool r = query.exec();
1671  if ( !r || query.lastError().isValid() ) {
1672  qWarning()<<"createFreshDataTable(): SQL Error: "<<query.lastError().text()<<"\n"
1673  <<"SQL="<<query.lastQuery();
1674  return false;
1675  }
1676 
1677  return true;
1678 }
1679 
1680 
1681 /*
1682  QStringList KLFLibDBEngine::getDataTableNames(const QUrl& url)
1683  {
1684  KLFLibDBEngine *resource = openUrl(url, NULL);
1685  QStringList tables = resource->pDB.tables(QSql::Tables);
1686  delete resource;
1687  // filter out t_<tablename> tables
1688  int k;
1689  QStringList dataTableNames;
1690  for (k = 0; k < tables.size(); ++k)
1691  if (tables[k].startsWith("t_"))
1692  dataTableNames << tables[k].mid(2);
1693  return dataTableNames;
1694  }
1695 */
1696 
1697 
1698 
1699 QMap<QString,KLFLibDBEnginePropertyChangeNotifier*> KLFLibDBEngine::pDBPropertyNotifiers
1701 
1702 // static
1703 KLFLibDBEnginePropertyChangeNotifier *KLFLibDBEngine::dbPropertyNotifierInstance(const QString& dbname)
1704 {
1705  if (!pDBPropertyNotifiers.contains(dbname))
1706  pDBPropertyNotifiers[dbname] = new KLFLibDBEnginePropertyChangeNotifier(dbname, qApp);
1707  return pDBPropertyNotifiers[dbname];
1708 }
1709 
1710 // ------------------------------------
1711 
1712 #define MAGIC_SQLITE_HEADER_LEN 16
1713 
1715 {
1716  // refer to http://www.sqlite.org/fileformat.html (section 2.2.1) for more info
1717  const char magic_sqlite_header[MAGIC_SQLITE_HEADER_LEN]
1718  = { 0x53, 0x51, 0x4c, 0x69, 0x74, 0x65, 0x20, 0x66,
1719  0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x33, 0x00 }; // "SQLite format 3"
1720  char header[MAGIC_SQLITE_HEADER_LEN];
1721 
1722  klfDbg("guessing scheme of "<<fileName) ;
1723 
1724  if (fileName.endsWith(".klf.db"))
1725  return QLatin1String("klf+sqlite");
1726 
1727  QFile f(fileName);
1728  if ( ! f.open(QIODevice::ReadOnly) ) {
1729  klfDbg("Yikes, can't read file "<<fileName);
1730  return QString();
1731  }
1732 
1733  int len = f.read(header, MAGIC_SQLITE_HEADER_LEN);
1734 
1735  if (len < MAGIC_SQLITE_HEADER_LEN) {
1736  klfDbg("Nope-can't read header.");
1737  return QString(); // error reading
1738  }
1739 
1740  if (!strncmp(header, magic_sqlite_header, MAGIC_SQLITE_HEADER_LEN)) {
1741  klfDbg("Yep, it's klf-sqlite!");
1742  return QLatin1String("klf+sqlite");
1743  }
1744 
1745  klfDbg("Nope-bad header.");
1746  return QString();
1747 }
1748 
1749 // ------------------------------------
1750 
1751 
1753  : KLFLibEngineFactory(parent)
1754 {
1756  f.scheme = QLatin1String("klf+sqlite");
1757  f.filepattern = QLatin1String("*.klf.db");
1758  f.filter = QString("%1 (%2)").arg(schemeTitle(f.scheme), f.filepattern);
1761 }
1762 
1764 {
1765  return QStringList() << QLatin1String("klf+sqlite");
1766 }
1767 
1769 {
1770  if (scheme == QLatin1String("klf+sqlite"))
1771  return tr("Local Library Database File");
1772  return QString();
1773 }
1775 {
1776  uint flags = FuncOpen;
1777  if (scheme == QLatin1String("klf+sqlite"))
1778  flags |= FuncCreate;
1779  else
1780  qWarning()<<"KLFLibDBEngineFactory::schemeFunctions: Bad scheme: "<<scheme;
1781  return flags;
1782 }
1783 
1785 {
1786  if (scheme == QLatin1String("klf+sqlite"))
1787  return QLatin1String("LocalFile");
1788  return QString();
1789 }
1790 
1791 
1793 {
1794  return KLFLibDBEngine::openUrl(location, parent);
1795 }
1796 
1797 
1799  const Parameters& parameters, QObject *parent)
1800 {
1801  QString defsubres = parameters["klfDefaultSubResource"].toString();
1802  QString defsubrestitle = parameters["klfDefaultSubResourceTitle"].toString();
1803  if (defsubres.isEmpty())
1804  defsubres = "entries";
1805  if (defsubrestitle.isEmpty())
1806  defsubrestitle = tr("Default Table", "[[default sub-resource title]]");
1807 
1808  // ensure that the sub-resource (internal) name conforms to standard characters
1809  defsubres = KLFLibNewSubResDlg::makeSubResInternalName(defsubres);
1810 
1811  if ( !parameters.contains("Filename") ) {
1812  qWarning()
1813  <<"KLFLibLegacyEngineFactory::createResource: bad parameters. They do not contain `Filename': "
1814  <<parameters;
1815  }
1816 
1817  if (scheme == QLatin1String("klf+sqlite"))
1818  return KLFLibDBEngine::createSqlite(parameters["Filename"].toString(), defsubres,
1819  defsubrestitle, parent);
1820  qWarning()<<"KLFLibDBEngineFactory::createResource("<<scheme<<","<<parameters<<","<<parent<<"):"
1821  <<"Bad scheme!";
1822  return NULL;
1823 }
fieldName(int index)
virtual int query(const QString &subResource, const Query &query, QueryResult *result)
void doReportProgress(int value)
virtual void setDefaultSubResource(const QString &subResource)
Set the default sub-resource.
Definition: klflib.cpp:584
virtual bool changeEntries(const QString &subRes, const QList< entryId > &idlist, const QList< int > &properties, const QList< QVariant > &values)
loadFromData(const uchar *data, int len, const char *format=0)
append(const QByteArray &ba)
virtual KLFLibResourceEngine * openResource(const QUrl &location, QObject *parent=NULL)
contains(const Key &key)
toString(const QString &format)
A structure that describes a query for query()
Definition: klflib.h:933
A cached value of the size of value in Preview.
Definition: klflib.h:65
const Qt::MatchFlags matchFlags() const
Definition: klflib.h:173
int setEntryProperty(const QString &propName, const QVariant &value)
Definition: klflib.cpp:92
entries have to match with one of a list of conditions
Definition: klflib.h:220
KLF_EXPORT uint klfUrlCompare(const QUrl &url1, const QUrl &url2, uint interestFlags, const QStringList &interestQueryItems)
virtual QString schemeTitle(const QString &scheme) const
save(const QString &fileName, const char *format=0, int quality=-1)
static T metatype_from_data(const QByteArray &data)
KLFLib::entryId entryId
Definition: klflib.h:444
exec(const QString &query)
contains(const QString &name)
database(const QString &connectionName=QLatin1String(defaultConnection)
contains(const QString &str, Qt::CaseSensitivity cs=Qt::CaseSensitive)
static QString escape_sql_data_string(QString s)
virtual void setProperty(const QString &propname, const QVariant &value)
#define klfDbg(streamableItems)
addDatabase(const QString &type, const QString &connectionName=QLatin1String(defaultConnection)
#define KLF_DEBUG_BLOCK(msg)
Qt::SortOrder orderDirection
Definition: klflib.h:950
virtual bool hasSubResource(const QString &subRes) const
QString guessScheme(const QString &fileName) const
Matches entries that don't match a condition.
Definition: klflib.h:219
virtual uint compareUrlTo(const QUrl &other, uint interestFlags=0xfffffff) const
virtual QString correspondingWidgetType(const QString &scheme) const
prepend(const QString &str)
QList< int > registeredPropertyIdList() const
virtual QList< entryId > insertEntries(const QString &subRes, const KLFLibEntryList &entries)
record(const QString &tablename)
join(const QString &separator)
hasQueryItem(const QString &key)
static EntryMatchCondition mkMatchAll()
Definition: klflib.cpp:776
toString(FormattingOptions options=None)
static KLFLibDBEngine * openUrl(const QUrl &url, QObject *parent=NULL)
toULongLong(bool *ok=0)
tr(const char *sourceText, const char *comment=0, int n=-1)
virtual bool setSubResourceProperty(const QString &subResource, int propId, const QVariant &value)
virtual bool canCreateSubResource() const
QList< KLFLibEntryWithId > entryWithIdList
Definition: klflib.h:981
copy(const QString &newName)
replace(int position, int n, const QString &after)
The Latex Code of the equation.
Definition: klflib.h:62
virtual bool deleteEntries(const QString &subRes, const QList< entryId > &idlist)
static void addLocalFileType(const LocalFileType &fileType)
int registerProperty(const QString &propertyName) const
number(long n, int base=10)
append(const QString &str)
fromTime_t(uint seconds)
prepare(const QString &query)
void dataChanged(const QString &subResource, int modificationType, const QList< KLFLib::entryId > &entryIdList)
Emitted when data has changed.
bool propertyIdRegistered(int propId) const
bool thisOperationProgressBlocked() const
Definition: klflib.cpp:660
static EntryMatchCondition mkOrMatch(QList< EntryMatchCondition > conditions)
Definition: klflib.cpp:795
append(const T &value)
removeDatabase(const QString &connectionName)
fromUtf8(const char *str, int size=-1)
virtual bool hasEntry(const QString &, entryId id)
void subResourceCreated(const QString &newSubResource)
Emitted when a sub-resource is created.
virtual bool compareDefaultSubResourceEquals(const QString &subResourceName) const
property(const char *name)
It's possible to modify that something.
Definition: klflib.h:1518
toInt(bool *ok=0)
KLF_EXPORT QString klfUrlLocalFilePath(const QUrl &url)
void subResourceDeleted(const QString &subResource)
Emitted when a sub-resource is deleted.
virtual QList< KLFLib::entryId > allIds()
Returns all IDs in this resource (and the default sub-resource)
Definition: klflib.cpp:642
QList< KLFLib::entryId > entryIdList
Definition: klflib.h:979
virtual QList< QVariant > queryValues(const QString &subResource, int entryPropId)
value(int index)
int propertyId() const
Definition: klflib.h:198
bindValue(const QString &placeholder, const QVariant &val, QSql::ParamType paramType=QSql::In)
const QString matchValueString() const
Definition: klflib.h:178
void resourcePropertyChanged(int propId)
Emitted when a resource property changes.
removeAllQueryItems(const QString &key)
toInt(bool *ok=0, int base=10)
virtual bool createSubResource(const QString &subResource, const QString &subResourceTitle)
A known local file type for KLFLibBasicWidgetFactory-created widgets.
Definition: klflibview.h:1141
setScheme(const QString &scheme)
Open Resources.
Definition: klflib.h:1696
KLFLibEntryList rawEntryList
Definition: klflib.h:980
endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive)
compare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs)
static QByteArray metatype_to_data(const T &object)
int propertyIdForName(const QString &propertyName) const
virtual KLFLibEntry entry(const QString &subRes, entryId id)
The Category to which eq. belongs (path-style string)
Definition: klflib.h:66
virtual void setDatabase(const QSqlDatabase &db_connection)
setForwardOnly(bool forward)
mid(int pos, int len=-1)
virtual QList< KLFLibEntryWithId > entries(const QString &, const QList< KLFLib::entryId > &idList, const QList< int > &wantedEntryProperties=QList< int >())
open(OpenMode mode)
void subResourcePropertyChanged(const QString &subResource, int propId)
Emitted when a sub-resource property changes.
scheme()
virtual QList< KLFLibEntryWithId > allEntries(const QString &subRes, const QList< int > &wantedEntryProperties=QList< int >())
static QString makeSubResInternalName(const QString &title)
virtual QString defaultSubResource() const
Definition: klflib.cpp:504
#define KLF_DEBUG_TIME_BLOCK(msg)
void setPreviewSize(const QSize &sz)
Definition: klflib.h:96
static QByteArray image_data(const QImage &img, const char *format)
virtual bool canModifyData(const QString &subResource, ModifyType modifytype) const
Definition: klflib.cpp:472
virtual ~KLFLibDBEngine()
#define KLF_FUNC_NAME
contains(const QString &str, Qt::CaseSensitivity cs=Qt::CaseSensitive)
Tags about the equation (string)
Definition: klflib.h:67
virtual bool canModifyProp(int propid) const
fromString(const QString &string, Qt::DateFormat format=Qt::TextDate)
virtual QString title() const
The human-set title of this resource.
Definition: klflib.h:599
QStringList registeredPropertyNameList() const
QImage preview() const
Definition: klflib.h:84
static KLFLibDBEngine * createSqlite(const QString &fileName, const QString &subresourcename, const QString &subresourcetitle, QObject *parent=NULL)
virtual uint schemeFunctions(const QString &scheme) const
bool propertyNameRegistered(const QString &propertyName) const
entries have to match with all given conditions
Definition: klflib.h:221
A structure that will hold the result of a query() query.
Definition: klflib.h:970
static QString make_like_condition(QString field, QString val, bool wildbefore, bool wildafter, bool casesensitive)
Create New Resources.
Definition: klflib.h:1697
static EntryMatchCondition mkNegateMatch(const EntryMatchCondition &condition)
Definition: klflib.cpp:788
QString filter
eg. "Local Library Database File (*.klf.db)"
Definition: klflibview.h:1144
static int queryImpl(KLFLibResourceEngine *resource, const QString &subResource, const Query &query, QueryResult *result)
Definition: klflib.cpp:964
An entry (single formula) in the library.
Definition: klflib.h:55
virtual QStringList supportedTypes() const
void defaultSubResourceChanged(const QString &newDefaultSubResource)
Emitted when the default sub-resource changes.
QList< int > wantedEntryProperties
Definition: klflib.h:951
#define klfDbgSt(streamableItems)
An Image Preview of equation (scaled down QImage)
Definition: klflib.h:64
#define MAGIC_SQLITE_HEADER_LEN
critical(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons=Ok, StandardButton defaultButton=NoButton)
Matches a property ID with a string (with a StringMatch)
Definition: klflib.h:218
virtual bool validDatabase() const
tables(QSql::TableType type=QSql::Tables)
fromLatin1(const char *str, int size=-1)
virtual bool canRegisterProperty(const QString &propName) const
virtual KLFLibResourceEngine * createResource(const QString &scheme, const Parameters &parameters, QObject *parent=NULL)
addQueryItem(const QString &key, const QString &value)
virtual bool saveResourceProperty(int propId, const QVariant &value)
QString scheme
eg. "klf+sqlite"
Definition: klflibview.h:1142
virtual QVariant property(const QString &propName) const
A KLFLibEntry in combination with a KLFLib::entryId.
Definition: klflib.h:447
virtual QStringList subResourceList() const
setDatabaseName(const QString &name)
virtual ModifyStatus baseCanModifyStatus(bool inSubResource, const QString &subResource=QString()) const
can modify data in resource (base common tests only)
Definition: klflib.cpp:752
virtual QUrl url(uint flags=0x0) const
query URL
Definition: klflib.cpp:457
virtual bool saveTo(const QUrl &newPath)
#define KLF_ASSERT_CONDITION(expr, msg, failaction)
Type type() const
Get which type of condition this is.
Definition: klflib.h:225
An abstract resource engine.
Definition: klflib.h:440
addBindValue(const QVariant &val, QSql::ParamType paramType=QSql::In)
QString propertyNameForId(int propId) const
The Date/Time at which the equation was evaluated.
Definition: klflib.h:63
connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type=Qt::AutoCompatConnection)
bool deRef()
Returns TRUE if the object needs to be deleted.
KLFLib::EntryMatchCondition matchCondition
Definition: klflib.h:946
virtual bool canDeleteSubResource(const QString &subResource) const
KLFLibDBEngineFactory(QObject *parent=NULL)
virtual bool canModifyProp(int propId) const
Definition: klflib.cpp:485
static QString make_sql_condition(const KLFLib::EntryMatchCondition m, QVariantList *placeholders, bool *haspostsqlcondition, KLFLib::EntryMatchCondition *postsqlcondition)
virtual bool canModifyData(const QString &subRes, ModifyType modifytype) const
queryItemValue(const QString &key)
void operationStartReportingProgress(KLFProgressReporter *progressReporter, const QString &descriptiveText)
virtual QVariant subResourceProperty(const QString &subResource, int propId) const
fromLocalFile(const QString &localFile)
PropertyMatch propertyMatch() const
Relevant for type PropertyMatchType.
Definition: klflib.h:227
virtual bool deleteSubResource(const QString &subResource)
QString filepattern
eg. "*.klf.db"
Definition: klflibview.h:1143
QList< EntryMatchCondition > conditionList() const
Relevant for types OrMatchType and AndMatchType.
Definition: klflib.h:229

Generated by doxygen 1.8.8