diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index cfcaf15..b6366c3 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -37,6 +37,7 @@ void MainWindow::connectSignals() connect(this, &MainWindow::beginSearch, searchWorker, &SearchWorker::search); connect(searchWorker, &SearchWorker::searchResultsReady, this, &MainWindow::handleSearchResults); connect(searchWorker, &SearchWorker::searchCancelled, this, &MainWindow::handleCancelledSearch); + connect(searchWorker, &SearchWorker::searchError, this, &MainWindow::handleSearchError); connect(ui->treeResultsList, &QTreeWidget::itemActivated, this, &MainWindow::treeSearchItemActivated); connect(ui->treeResultsList, &QTreeWidget::customContextMenuRequested, this, &MainWindow::showSearchResultsContextMenu); connect(ui->tabWidget, &QTabWidget::currentChanged, this, &MainWindow::tabChanged); @@ -98,6 +99,11 @@ void MainWindow::pdfPreviewReceived(PdfPreview preview) void MainWindow::lineEditReturnPressed() { QString q = ui->txtSearch->text(); + if(!searchWorker->checkParanthesis(q)) + { + ui->lblSearchResults->setText("Invalid paranthesis"); + return; + } //TODO: validate q; ui->lblSearchResults->setText("Searching..."); emit beginSearch(q); @@ -158,6 +164,11 @@ void MainWindow::handleCancelledSearch() } +void MainWindow::handleSearchError(QString error) +{ + ui->lblSearchResults->setText("Error:" + error); +} + void MainWindow::treeSearchItemActivated(QTreeWidgetItem *item, int i) { QDesktopServices::openUrl(QUrl::fromLocalFile(item->text(1))); diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 0a5dd85..e5ffc03 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -40,8 +40,8 @@ private slots: void lineEditReturnPressed(); void handleSearchResults(const QVector &results); void handleCancelledSearch(); + void handleSearchError(QString error); void treeSearchItemActivated(QTreeWidgetItem *item, int i); - void showSearchResultsContextMenu(const QPoint &point); void tabChanged(); void pdfPreviewReceived(PdfPreview preview); diff --git a/gui/searchworker.cpp b/gui/searchworker.cpp index c963d45..61ff8af 100644 --- a/gui/searchworker.cpp +++ b/gui/searchworker.cpp @@ -3,6 +3,7 @@ #include #include #include +#include SearchWorker::SearchWorker() { @@ -19,6 +20,10 @@ SearchWorker::SearchWorker(const QString &dbpath) QVector SearchWorker::tokenize(QString expression) { + if(!checkParanthesis(expression)) + { + throw std::invalid_argument("Invalid paranthesis"); + } //TODO: merge lonewords QVector result; QRegularExpression rx("((?(\\.|\\w)+):(?\\((?[^\\)]+)\\)|(\\w)+)|(?AND|OR|!)|(?\\(|\\))|(?\\w+))"); @@ -111,11 +116,9 @@ QString SearchWorker::createSql(const SearchWorker::Command &cmd) } if(key == "contains" || key == "c") { - return " ( COALESCE( (SELECT 1 FROM content_fts WHERE content_fts.content MATCH '" + value + "' AND content_fts.ROWID= content.id), 0 ) )"; + return " content.id IN (SELECT content_fts.ROWID FROM content_fts WHERE content_fts.content MATCH '" + value + "' )"; } - qDebug() << "NOHIT" << key; - //TODO: exception? - return "NOTHING"; + throw std::invalid_argument("Unknown filter: " + key.toStdString()); } QString SearchWorker::makeSql(const QVector &tokens) @@ -128,26 +131,73 @@ QString SearchWorker::makeSql(const QVector &tokens) return result; } +bool SearchWorker::checkParanthesis(QString expression) +{ + QStack open; + QStack close; + + for(QChar &c : expression) + { + if(c == '(') + { + open.push(c); + } + if(c == ')') + { + close.push(c); + } + } + if(open.size() != close.size()) + { + return false; + } + while(!open.empty() && !close.empty()) + { + QChar o = open.pop(); + QChar c = close.pop(); + if(o != '(' && c != ')' ) + { + return false; + } + } + return true; +} + void SearchWorker::search(const QString &query) { QSqlQuery dbquery(db); QVector results; - QString whereSql = makeSql(tokenize(query)); + QString whereSql; + try + { + whereSql = makeSql(tokenize(query)); + } + catch(const std::exception &e) + { + emit searchError(e.what()); + return; + } QString prep; //TODO: hack, as we don't wanna look into content and get redundant results, when we don't even care about content if(whereSql.contains("content.")) { - prep = "SELECT file.path AS path, content.page AS page, file.mtime AS mtime FROM file INNER JOIN content ON file.id = content.fileid INNER JOIN content_fts ON content.id = content_fts.ROWID WHERE 1=1 AND " + whereSql + " ORDER By file.mtime DESC, content.page ASC"; + prep = "SELECT file.path AS path, content.page AS page, file.mtime AS mtime FROM file INNER JOIN content ON file.id = content.fileid WHERE 1=1 AND " + whereSql + " ORDER By file.mtime DESC, content.page ASC"; } else { prep = "SELECT file.path AS path, 0 as page, file.mtime AS mtime FROM file WHERE " + whereSql + " ORDER by file.mtime DESC"; } dbquery.prepare(prep); - dbquery.exec(); - qDebug() << "prepped: " << prep; - qDebug() << dbquery.lastError(); + bool success = dbquery.exec(); + if(!success) + { + qDebug() << "prepped: " << prep; + qDebug() << dbquery.lastError(); + emit searchError(dbquery.lastError().text()); + return; + } + while(dbquery.next()) { SearchResult result; diff --git a/gui/searchworker.h b/gui/searchworker.h index 3a77d01..e8586ac 100644 --- a/gui/searchworker.h +++ b/gui/searchworker.h @@ -33,11 +33,13 @@ private: public: SearchWorker(); SearchWorker(const QString &dbpath); + bool checkParanthesis(QString expression); public slots: void search(const QString &query); signals: void searchResultsReady(const QVector &results); void searchCancelled(); + void searchError(QString e); }; #endif // SEARCHWORKER_H