From 7a485c55bb9c6613ce60e801f297fa1dccaa52f2 Mon Sep 17 00:00:00 2001 From: Albert S Date: Sun, 2 Sep 2018 13:54:27 +0200 Subject: [PATCH] checks on input, exceptions, improved contains query performance --- gui/mainwindow.cpp | 11 +++++++ gui/mainwindow.h | 2 +- gui/searchworker.cpp | 70 +++++++++++++++++++++++++++++++++++++------- gui/searchworker.h | 2 ++ 4 files changed, 74 insertions(+), 11 deletions(-) diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 3c67a08..8b6263b 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -35,6 +35,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); @@ -99,6 +100,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); @@ -156,6 +162,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 030b682..b72187f 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -42,8 +42,8 @@ class MainWindow : public QMainWindow 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 0ccceea..59b5e13 100644 --- a/gui/searchworker.cpp +++ b/gui/searchworker.cpp @@ -3,6 +3,7 @@ #include #include #include +#include SearchWorker::SearchWorker() { } @@ -18,6 +19,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|!)|" @@ -108,12 +113,10 @@ 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) @@ -126,18 +129,59 @@ 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 " + + "file.id = content.fileid WHERE 1=1 AND " + whereSql + " ORDER By file.mtime DESC, content.page ASC"; } else @@ -146,9 +190,15 @@ void SearchWorker::search(const QString &query) " 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 9254d56..6cd3052 100644 --- a/gui/searchworker.h +++ b/gui/searchworker.h @@ -32,11 +32,13 @@ class SearchWorker : public QObject 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