diff --git a/src/DbStructureModel.cpp b/src/DbStructureModel.cpp index 53ea2454..7b526f6a 100644 --- a/src/DbStructureModel.cpp +++ b/src/DbStructureModel.cpp @@ -230,7 +230,7 @@ QMimeData* DbStructureModel::mimeData(const QModelIndexList& indices) const SqliteTableModel tableModel(m_db); sqlb::ObjectIdentifier objid(data(index.sibling(index.row(), ColumnSchema), Qt::DisplayRole).toString(), data(index.sibling(index.row(), ColumnName), Qt::DisplayRole).toString()); - tableModel.setTable(objid); + tableModel.setQuery(sqlb::Query(objid)); if(tableModel.completeCache()) { // Only continue if all data was fetched diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index c998dd00..52082a33 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -80,21 +80,21 @@ QDataStream& operator>>(QDataStream& ds, sqlb::ObjectIdentifier& objid) // These are temporary helper functions to turn a vector of sorted columns into a single column to sort and vice verse. This is done by just taking the // first sort column there is and ignoring all the others or creating a single item vector respectively. These functions can be removed once all parts // of the application have been converted to deal with vectors of sorted columns. -void fromSortOrderVector(const QVector& vector, int& index, Qt::SortOrder& mode) +static void fromSortOrderVector(const std::vector& vector, int& index, Qt::SortOrder& mode) { if(vector.size()) { - index = vector.at(0).index; - mode = vector.at(0).mode; + index = vector.at(0).column; + mode = vector.at(0).direction == sqlb::Ascending ? Qt::AscendingOrder : Qt::DescendingOrder; } else { index = 0; mode = Qt::AscendingOrder; } } -QVector toSortOrderVector(int index, Qt::SortOrder mode) +static std::vector toSortOrderVector(int index, Qt::SortOrder mode) { - QVector vector; - vector.push_back(BrowseDataTableSettings::SortedColumn(index, mode)); + std::vector vector; + vector.emplace_back(index, mode == Qt::AscendingOrder ? sqlb::Ascending : sqlb::Descending); return vector; } @@ -625,7 +625,7 @@ void MainWindow::populateTable() // No stored settings found. // Set table name and apply default display format settings - m_browseTableModel->setTable(tablename, 0, Qt::AscendingOrder); + m_browseTableModel->setQuery(sqlb::Query(tablename)); // There aren't any information stored for this table yet, so use some default values @@ -655,10 +655,21 @@ void MainWindow::populateTable() // The filters can be left empty as they are } else { - // Stored settings found. Retrieve them. + // Stored settings found. Retrieve them and assemble a query from them. BrowseDataTableSettings storedData = browseTableSettings[tablename]; + sqlb::Query query(tablename); - // Load display formats and set them along with the table name + // Sorting + int sortOrderIndex; + Qt::SortOrder sortOrderMode; + fromSortOrderVector(storedData.query.orderBy(), sortOrderIndex, sortOrderMode); + query.orderBy().emplace_back(sortOrderIndex, sortOrderMode == Qt::AscendingOrder ? sqlb::Ascending : sqlb::Descending); + + // Filters + for(auto it=storedData.filterValues.constBegin();it!=storedData.filterValues.constEnd();++it) + query.where().insert({it.key(), CondFormat::filterToSqlCondition(it.value(), m_browseTableModel->encoding()).toStdString()}); + + // Display formats QVector v; bool only_defaults = true; if(db.getObjectByName(tablename)) @@ -669,21 +680,18 @@ void MainWindow::populateTable() QString format = storedData.displayFormats[i+1]; if(format.size()) { - v.push_back(format); + query.selectedColumns().emplace_back(tablefields.at(i).name.toStdString(), format.toStdString()); only_defaults = false; } else { - v.push_back(sqlb::escapeIdentifier(tablefields.at(i).name)); + query.selectedColumns().emplace_back(tablefields.at(i).name.toStdString(), tablefields.at(i).name.toStdString()); } } } - - int sortOrderIndex; - Qt::SortOrder sortOrderMode; - fromSortOrderVector(storedData.sortOrder, sortOrderIndex, sortOrderMode); if(only_defaults) - m_browseTableModel->setTable(tablename, sortOrderIndex, sortOrderMode, storedData.filterValues); - else - m_browseTableModel->setTable(tablename, sortOrderIndex, sortOrderMode, storedData.filterValues, v); + query.selectedColumns().clear(); + + // Apply query + m_browseTableModel->setQuery(query); // There is information stored for this table, so extract it and apply it applyBrowseTableSettings(storedData); @@ -737,7 +745,7 @@ void MainWindow::applyBrowseTableSettings(BrowseDataTableSettings storedData, bo // Sorting int sortOrderIndex; Qt::SortOrder sortOrderMode; - fromSortOrderVector(storedData.sortOrder, sortOrderIndex, sortOrderMode); + fromSortOrderVector(storedData.query.orderBy(), sortOrderIndex, sortOrderMode); ui->dataTable->filterHeader()->setSortIndicator(sortOrderIndex, sortOrderMode); // Filters @@ -1984,9 +1992,9 @@ void MainWindow::browseTableHeaderClicked(int logicalindex) BrowseDataTableSettings& settings = browseTableSettings[currentlyBrowsedTableName()]; int dummy; Qt::SortOrder order; - fromSortOrderVector(settings.sortOrder, dummy, order); + fromSortOrderVector(settings.query.orderBy(), dummy, order); order = order == Qt::AscendingOrder ? Qt::DescendingOrder : Qt::AscendingOrder; - settings.sortOrder = toSortOrderVector(logicalindex, order); + settings.query.orderBy() = toSortOrderVector(logicalindex, order); ui->dataTable->sortByColumn(logicalindex, order); // select the first item in the column so the header is bold @@ -2466,7 +2474,7 @@ static void loadBrowseDataTableSettings(BrowseDataTableSettings& settings, QXmlS { int sortOrderIndex = xml.attributes().value("sort_order_index").toInt(); Qt::SortOrder sortOrderMode = static_cast(xml.attributes().value("sort_order_mode").toInt()); - settings.sortOrder = toSortOrderVector(sortOrderIndex, sortOrderMode); + settings.query.orderBy() = toSortOrderVector(sortOrderIndex, sortOrderMode); } settings.showRowid = xml.attributes().value("show_row_id").toInt(); @@ -2483,7 +2491,7 @@ static void loadBrowseDataTableSettings(BrowseDataTableSettings& settings, QXmlS { int index = xml.attributes().value("index").toInt(); int mode = xml.attributes().value("mode").toInt(); - settings.sortOrder.push_back(BrowseDataTableSettings::SortedColumn(index, mode)); + settings.query.orderBy().emplace_back(index, mode == Qt::AscendingOrder ? sqlb::Ascending : sqlb::Descending); xml.skipCurrentElement(); } } @@ -2704,7 +2712,7 @@ bool MainWindow::loadProject(QString filename, bool readOnly) int sortIndex; Qt::SortOrder sortMode; - fromSortOrderVector(browseTableSettings[current_table].sortOrder, sortIndex, sortMode); + fromSortOrderVector(browseTableSettings[current_table].query.orderBy(), sortIndex, sortMode); ui->dataTable->sortByColumn(sortIndex, sortMode); showRowidColumn(browseTableSettings[current_table].showRowid); unlockViewEditing(!browseTableSettings[current_table].unlockViewPk.isEmpty(), browseTableSettings[current_table].unlockViewPk); @@ -2767,11 +2775,11 @@ static void saveBrowseDataTableSettings(const BrowseDataTableSettings& object, Q xml.writeAttribute("unlock_view_pk", object.unlockViewPk); xml.writeStartElement("sort"); - for(const auto& column : object.sortOrder) + for(const auto& column : object.query.orderBy()) { xml.writeStartElement("column"); - xml.writeAttribute("index", QString::number(column.index)); - xml.writeAttribute("mode", QString::number(column.mode)); + xml.writeAttribute("index", QString::number(column.column)); + xml.writeAttribute("mode", QString::number(column.direction)); xml.writeEndElement(); } xml.writeEndElement(); diff --git a/src/MainWindow.h b/src/MainWindow.h index 7321cb22..ebb80fb8 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -5,6 +5,7 @@ #include "PlotDock.h" #include "Palette.h" #include "CondFormat.h" +#include "sql/Query.h" #include #include @@ -28,25 +29,7 @@ class MainWindow; struct BrowseDataTableSettings { - struct SortedColumn - { - SortedColumn() : - index(0), - mode(Qt::AscendingOrder) - {} - SortedColumn(int index_, Qt::SortOrder mode_) : - index(index_), - mode(mode_) - {} - SortedColumn(int index_, int mode_) : - index(index_), - mode(static_cast(mode_)) - {} - - int index; - Qt::SortOrder mode; - }; - QVector sortOrder; + sqlb::Query query; // NOTE: We only store the sort order in here (for now) QMap columnWidths; QMap filterValues; QMap> condFormats; @@ -68,7 +51,7 @@ struct BrowseDataTableSettings int sortOrderIndex, sortOrderMode; stream >> sortOrderIndex; stream >> sortOrderMode; - object.sortOrder.push_back(SortedColumn(sortOrderIndex, sortOrderMode)); + object.query.orderBy().emplace_back(sortOrderIndex, sortOrderMode == Qt::AscendingOrder ? sqlb::Ascending : sqlb::Descending); stream >> object.columnWidths; stream >> object.filterValues; stream >> object.displayFormats; diff --git a/src/sql/Query.cpp b/src/sql/Query.cpp index 99d2e24c..87db9cf2 100644 --- a/src/sql/Query.cpp +++ b/src/sql/Query.cpp @@ -47,15 +47,15 @@ std::string Query::buildQuery(bool withRowid) const for(auto i=m_where.cbegin();i!=m_where.cend();++i) { - const auto it = findSelectedColumnByName(i->first); - std::string column = sqlb::escapeIdentifier(i->first); + const auto it = findSelectedColumnByName(m_column_names.at(i->first)); + std::string column = sqlb::escapeIdentifier(m_column_names.at(i->first)); if(it != m_selected_columns.cend() && it->selector != column) column = it->selector; where += column + " " + i->second + " AND "; } - // Remove last 'AND ' - where.erase(where.size() - 4); + // Remove last ' AND ' + where.erase(where.size() - 5); } // Sorting @@ -64,7 +64,8 @@ std::string Query::buildQuery(bool withRowid) const { order_by = "ORDER BY "; for(const auto& sorted_column : m_sort) - order_by += sqlb::escapeIdentifier(sorted_column.column) + " " + sorted_column.direction + ","; + order_by += sqlb::escapeIdentifier(m_column_names.at(sorted_column.column)) + " " + + (sorted_column.direction == sqlb::Ascending ? "ASC" : "DESC") + ","; order_by.pop_back(); } diff --git a/src/sql/Query.h b/src/sql/Query.h index 83ac8d3e..e5961da8 100644 --- a/src/sql/Query.h +++ b/src/sql/Query.h @@ -10,15 +10,21 @@ namespace sqlb { +enum SortDirection +{ + Ascending, + Descending +}; + struct SortedColumn { - SortedColumn(const std::string& column_, const std::string& direction_) : + SortedColumn(int column_, SortDirection direction_) : column(column_), direction(direction_) {} - std::string column; - std::string direction; + int column; + SortDirection direction; }; struct SelectedColumn @@ -43,6 +49,9 @@ public: void clear(); std::string buildQuery(bool withRowid) const; + void setColumNames(const std::vector& column_names) { m_column_names = column_names; } + std::vector columnNames() const { return m_column_names; } + void setTable(const sqlb::ObjectIdentifier& table) { m_table = table; } sqlb::ObjectIdentifier table() const { return m_table; } @@ -52,17 +61,18 @@ public: const std::vector& selectedColumns() const { return m_selected_columns; } std::vector& selectedColumns() { return m_selected_columns; } - const std::unordered_map& where() const { return m_where; } - std::unordered_map& where() { return m_where; } + const std::unordered_map& where() const { return m_where; } + std::unordered_map& where() { return m_where; } const std::vector& orderBy() const { return m_sort; } std::vector& orderBy() { return m_sort; } private: + std::vector m_column_names; sqlb::ObjectIdentifier m_table; std::string m_rowid_column; std::vector m_selected_columns; - std::unordered_map m_where; + std::unordered_map m_where; std::vector m_sort; std::vector::iterator findSelectedColumnByName(const std::string& name); diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index 0a9b3749..660ca2e3 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -112,22 +112,22 @@ void SqliteTableModel::setChunkSize(size_t chunksize) m_chunkSize = chunksize; } -void SqliteTableModel::setTable(const sqlb::ObjectIdentifier& table, int sortColumn, Qt::SortOrder sortOrder, const QMap filterValues, const QVector& display_format) +void SqliteTableModel::setQuery(const sqlb::Query& query) { // Unset all previous settings. When setting a table all information on the previously browsed data set is removed first. reset(); - // Save the table name - m_query.setTable(table); + // Save the query + m_query = query; // The first column is the rowid column and therefore is always of type integer m_vDataTypes.push_back(SQLITE_INTEGER); // Get the data types of all other columns as well as the column names bool allOk = false; - if(m_db.getObjectByName(table) && m_db.getObjectByName(table)->type() == sqlb::Object::Types::Table) + if(m_db.getObjectByName(query.table()) && m_db.getObjectByName(query.table())->type() == sqlb::Object::Types::Table) { - sqlb::TablePtr t = m_db.getObjectByName(table); + sqlb::TablePtr t = m_db.getObjectByName(query.table()); if(t && t->fields.size()) // parsing was OK { QString rowid = t->rowidColumn(); @@ -157,34 +157,21 @@ void SqliteTableModel::setTable(const sqlb::ObjectIdentifier& table, int sortCol // NOTE: It would be nice to eventually get rid of this piece here. As soon as the grammar parser is good enough... if(!allOk) { - QString sColumnQuery = QString::fromUtf8("SELECT * FROM %1;").arg(table.toString()); + QString sColumnQuery = QString::fromUtf8("SELECT * FROM %1;").arg(query.table().toString()); m_query.setRowIdColumn("rowid"); m_headers.push_back("rowid"); m_headers.append(getColumns(nullptr, sColumnQuery, m_vDataTypes)); } - // Store filters and display formats - for(auto filterIt=filterValues.constBegin(); filterIt!=filterValues.constEnd(); ++filterIt) - updateFilter(filterIt.key(), filterIt.value(), false); - if(display_format.size()) - { - for(int i=1;i column_names; + for(const auto& h : m_headers) + column_names.push_back(h.toStdString()); + //column_names.erase(column_names.begin(), column_names.begin()+1); + m_query.setColumNames(column_names); - m_query.selectedColumns().emplace_back(m_headers[i].toStdString(), format.toStdString()); - } - } - - // Set sort parameters. We're setting the sort columns to no sorting before calling sort() because this way, in sort() the - // current sort order is always changed and thus buildQuery() is always going to be called. - // This is also why we don't need to call buildQuery() here again. - m_query.orderBy().clear(); - sort(sortColumn, sortOrder); + // Apply new query and update view + buildQuery(); } void SqliteTableModel::setQuery(const QString& sQuery, bool dontClearHeaders) @@ -465,7 +452,7 @@ Qt::ItemFlags SqliteTableModel::flags(const QModelIndex& index) const void SqliteTableModel::sort(int column, Qt::SortOrder order) { // Don't do anything when the sort order hasn't changed - if(m_query.orderBy().size() && QString::fromStdString(m_query.orderBy().at(0).column) == m_headers.at(column) && m_query.orderBy().at(0).direction == (order == Qt::AscendingOrder ? "ASC" : "DESC")) + if(m_query.orderBy().size() && m_query.orderBy().at(0).column == column && m_query.orderBy().at(0).direction == (order == Qt::AscendingOrder ? sqlb::Ascending : sqlb::Descending)) return; // Reset sort order @@ -473,7 +460,7 @@ void SqliteTableModel::sort(int column, Qt::SortOrder order) // Save sort order if (column >= 0 && column < m_headers.size()) - m_query.orderBy().emplace_back(m_headers.at(column).toStdString(), (order == Qt::AscendingOrder ? "ASC" : "DESC")); + m_query.orderBy().emplace_back(column, (order == Qt::AscendingOrder ? sqlb::Ascending : sqlb::Descending)); // Set the new query (but only if a table has already been set if(!m_query.table().isEmpty()) @@ -706,20 +693,18 @@ void SqliteTableModel::setCondFormats(int column, const QVector& con emit layoutChanged(); } -void SqliteTableModel::updateFilter(int column, const QString& value, bool applyQuery) +void SqliteTableModel::updateFilter(int column, const QString& value) { QString whereClause = CondFormat::filterToSqlCondition(value, m_encoding); // If the value was set to an empty string remove any filter for this column. Otherwise insert a new filter rule or replace the old one if there is already one if(whereClause.isEmpty()) - m_query.where().erase(m_headers.at(column).toStdString()); - else { - m_query.where()[m_headers.at(column).toStdString()] = whereClause.toStdString(); - } + m_query.where().erase(column); + else + m_query.where()[column] = whereClause.toStdString(); // Build the new query - if (applyQuery) - buildQuery(); + buildQuery(); } void SqliteTableModel::clearCache() diff --git a/src/sqlitetablemodel.h b/src/sqlitetablemodel.h index 66e856f2..db6e29a1 100644 --- a/src/sqlitetablemodel.h +++ b/src/sqlitetablemodel.h @@ -84,7 +84,7 @@ public: QString customQuery(bool withRowid) const { return QString::fromStdString(m_query.buildQuery(withRowid)); } /// configure for browsing specified table - void setTable(const sqlb::ObjectIdentifier& table, int sortColumn = 0, Qt::SortOrder sortOrder = Qt::AscendingOrder, const QMap filterValues = QMap(), const QVector &display_format = QVector()); + void setQuery(const sqlb::Query& query); void setChunkSize(size_t chunksize); void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; @@ -115,7 +115,7 @@ public: void setCondFormats(int column, const QVector& condFormats); public slots: - void updateFilter(int column, const QString& value, bool applyQuery = true); + void updateFilter(int column, const QString& value); signals: void finishedFetch(int fetched_row_begin, int fetched_row_end);