diff --git a/pglab/DatabaseWindow.cpp b/pglab/DatabaseWindow.cpp index 4514c7f..2676f3d 100644 --- a/pglab/DatabaseWindow.cpp +++ b/pglab/DatabaseWindow.cpp @@ -1,6 +1,6 @@ #include "DatabaseWindow.h" #include "util.h" -#include "CrudTab.h" +#include "crud/CrudTab.h" #include "widgets/CatalogTablesPage.h" #include "OpenDatabase.h" #include "catalog/PgDatabaseCatalog.h" @@ -268,7 +268,7 @@ void DatabaseWindow::newCatalogInspectorPage(QString caption, NamespaceFilter fi if (!m_database) return; // would be better if we queued the operation for later - auto ct = new CatalogInspector(m_database, this); + auto ct = new CatalogInspector(m_database, this); ct->addAction(actionRefreshCatalog); addPage(ct, caption); diff --git a/pglab/CrudModel.cpp b/pglab/crud/CrudModel.cpp similarity index 66% rename from pglab/CrudModel.cpp rename to pglab/crud/CrudModel.cpp index 9a2fb9d..fec8ab5 100644 --- a/pglab/CrudModel.cpp +++ b/pglab/crud/CrudModel.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "Pgsql_oids.h" #include "Pgsql_PgException.h" #include "Pgsql_Params.h" @@ -18,7 +19,7 @@ CrudModel::CrudModel(QObject *parent) : QAbstractTableModel(parent) -, m_dbConn() + , m_dbConn() { qDebug("CrudModel created"); connect(&m_dbConn, &ASyncDBConnection::onStateChanged, this, &CrudModel::connectionStateChanged); @@ -62,16 +63,9 @@ QVariant CrudModel::headerData(int section, Qt::Orientation orientation, int rol return r; } - -// Basic functionality: int CrudModel::rowCount(const QModelIndex &/*parent*/) const { -// int row_count = m_roData ? m_roData->rows() : 0; - -// todo there will be rownumbers that are not in m_roData - -// ++row_count; // one empty new row at the end - return m_rowCount; + return (int)m_rowMapping.size(); } int CrudModel::columnCount(const QModelIndex &/*parent*/) const @@ -85,52 +79,56 @@ Oid CrudModel::getType(int column) const return m_roData ? m_roData->type(column) : InvalidOid; } -CrudModel::Value CrudModel::getData(const QModelIndex &index) const +CrudModel::Value CrudModel::getLatestData(int columnIndex, int rowIndex) const { - Value value; if (m_roData) { - int grid_row = index.row(); - int col = index.column(); + auto row_mapping = m_rowMapping[rowIndex]; - auto row_mapping = m_rowMapping[grid_row]; - const int last_row = rowCount() - 1; - - //Oid o = m_roData->type(col); - // First see if we have buffered editted values that still need saving std::optional val; if (row_mapping.pending) { - val = m_pendingRowList.getValue(col, row_mapping.rowKey); - } - if (row_mapping.modified && !val && grid_row < last_row) { // last_row should never be in modified list, when it is put in modified list a new last row should be created - // No pending save have a look if we have modified saved data in the modified list - auto find_res = m_modifiedRowList.find(row_mapping.rowKey); - if (find_res != m_modifiedRowList.end()) { - val = find_res->second.data()[col]; - } - } - - //Value value; - // If we did not have pending or modified data - if (!val && row_mapping.rowKey < m_roData->rows()) { - // Then we are going to read the original data. - if (!m_roData->null(col, row_mapping.rowKey)) { - value = std::string(m_roData->val(col, row_mapping.rowKey)); - } - } - else { - if (val) { - value = *val; - } + val = m_pendingRowList.getValue(columnIndex, row_mapping.rowKey); } + if (!val.has_value()) + { + val = getSavedData(row_mapping, columnIndex); + } + return val.value_or(Value()); } - return value; + return {}; +} + +CrudModel::Value CrudModel::getSavedData(int columnIndex, int rowIndex) const +{ + if (m_roData) { + auto row_mapping = m_rowMapping[rowIndex]; + return getSavedData(row_mapping, columnIndex); + } + return {}; +} + +CrudModel::Value CrudModel::getSavedData(const RowMapping &row_mapping, int columnIndex) const +{ + // First see if we have buffered editted values that still need saving + std::optional val; + if (!val.has_value() && row_mapping.isModified()) { + val = row_mapping.modifiedValue(columnIndex); + } + + // If we did not have pending or modified data + if (!val.has_value() && row_mapping.rowKey < m_roData->rows()) { + // Then we are going to read the original data. + if (!m_roData->null(columnIndex, row_mapping.rowKey)) { + val = std::string(m_roData->val(columnIndex, row_mapping.rowKey)); + } + } + return val.value_or(Value()); } QVariant CrudModel::data(const QModelIndex &index, int role) const { QVariant v; if (role == Qt::EditRole) { - auto value = getData(index); + auto value = getLatestData(index); if (value) { QString s = QString::fromUtf8(value->c_str()); v = s; @@ -138,7 +136,7 @@ QVariant CrudModel::data(const QModelIndex &index, int role) const } else if (role == Qt::DisplayRole) { - auto value = getData(index); + auto value = getLatestData(index); if (value) { Oid o = m_roData->type(index.column()); if (o == Pgsql::bool_oid) { @@ -159,7 +157,6 @@ QVariant CrudModel::data(const QModelIndex &index, int role) const return v; } - void CrudModel::loadData() { QString table_name = m_table->fullyQualifiedQuotedObjectName(); // genFQTableName(*m_database->catalog(), *m_table); @@ -180,10 +177,8 @@ void CrudModel::loadIntoModel(std::shared_ptr data) { beginResetModel(); m_pendingRowList.clear(); - m_modifiedRowList.clear(); m_roData = data; - lastRowKey = data->rows(); - m_rowCount = data->rows(); + lastRowKey = data->rows() - 1; initRowMapping(); appendNewRow(); endResetModel(); @@ -191,9 +186,10 @@ void CrudModel::loadIntoModel(std::shared_ptr data) void CrudModel::initRowMapping() { - m_rowMapping.resize(static_cast(m_rowCount)); - for (int i = 0; i < m_rowCount; ++i) - m_rowMapping[i] = RowMapping{ i }; + size_t cnt = m_roData->rows(); + m_rowMapping.reserve(cnt + 1); + for (int i = 0; i < cnt; ++i) + m_rowMapping.emplace_back(i); } void CrudModel::connectionStateChanged(ASyncDBConnection::State state) @@ -252,63 +248,68 @@ bool CrudModel::setData(const QModelIndex &index, const QVariant &value, int rol return false; } -const CrudModel::ModifiedRow* CrudModel::getModifiedRow(int row) const +std::tuple> CrudModel::saveRow(const PendingRow &pending_row) { - auto iter = m_modifiedRowList.find(row); - if (iter == m_modifiedRowList.end()) - return nullptr; - else - return &iter->second; -} + auto data = pending_row.data(); + RowMapping& rowmapping = m_rowMapping[pending_row.row()]; -CrudModel::PKeyValues CrudModel::getPKeyForRow(int row) const -{ - PKeyValues values; - values.reserve(m_primaryKey->key.size()); + if (!data.empty()) { + QString buffer; + Pgsql::Params params; + if (rowmapping.isNew()){ + std::tie(buffer, params) = createInsertQuery(pending_row); + } + else { + Pgsql::Params pkey_params = getPKeyParamsForRow(pending_row.row()); + std::tie(buffer, params) = createUpdateQuery(pkey_params, pending_row); + } - auto mod_row = getModifiedRow(row); - if (mod_row){ - for (auto attnum : m_primaryKey->key) { - const int col = attNumToCol(attnum); - values.push_back(*(mod_row->data()[col])); - } - } - else if (row < m_roData->rows()){ - for (auto attnum : m_primaryKey->key) { - int col = attNumToCol(attnum); - values.push_back(m_roData->get(col, row).c_str()); - } - } + Pgsql::Connection db_update_conn; + auto dbconfig = m_database->config(); + db_update_conn.connect(dbconfig.connectionString()); + try { + auto result = db_update_conn.queryParam(buffer, params); + if (result && result.rows() == 1) { - return values; + std::vector values; + auto row = *result.begin(); + for (auto v : row) { + if (v.null()) + values.push_back(Value()); + else + values.push_back(std::string(v.c_str())); + } + + return { true, values }; + } + } + catch (const Pgsql::PgResultError &ex) { + QMessageBox msgBox; + msgBox.setText(ex.what()); + msgBox.exec(); + } + } + return { false, {} }; } Pgsql::Params CrudModel::getPKeyParamsForRow(int row) const { Pgsql::Params params; - auto mod_row = getModifiedRow(row); for (auto attnum : m_primaryKey->key) { const int col = attNumToCol(attnum); Oid t = getType(col); - std::string s; - if (mod_row){ - s = *(mod_row->data()[col]); - } - else if (row < m_roData->rows()){ - s = m_roData->get(col, row).c_str(); - } - params.add(s, t); + auto s = getSavedData(col, row); + params.add(s, t); } return params; } - QString CrudModel::columnName(int col) const { return m_roData->getColName(col); } -std::tuple CrudModel::createUpdateQuery(const PKeyValues &pkey_values, const PendingRow &pending_row) +std::tuple CrudModel::createUpdateQuery(const Pgsql::Params &pkey_params, const PendingRow &pending_row) { Pgsql::Params params; auto data = pending_row.data(); @@ -317,7 +318,7 @@ std::tuple CrudModel::createUpdateQuery(const PKeyValues QTextStream q(&buffer); q << "UPDATE " << table_name << " AS d\n SET "; int param = 0; - for (auto e : data) { + for (auto& e : data) { if (param > 0) q << ","; q << quoteIdent(columnName(e.first)) << "=$" << ++param; @@ -333,10 +334,9 @@ std::tuple CrudModel::createUpdateQuery(const PKeyValues if (i > 0) q << " AND "; q << quoteIdent(columnName(col)) << "=$" << ++param; - - params.add(pkey_values[i].c_str(), getType(col)); ++i; } + params.addParams(pkey_params); q << "\nRETURNING *"; q.flush(); return { buffer, params }; @@ -353,7 +353,7 @@ std::tuple CrudModel::createInsertQuery(const PendingRow auto columns = m_database->catalog()->attributes()->getColumnsForRelation(m_table->oid()); bool first = true; - for (auto e : data) { + for (const auto& e : data) { int num = e.first + 1; auto find_res = std::find_if(columns.begin(), columns.end(), [num] (const auto &elem) -> bool { return num == elem.num; }); @@ -368,8 +368,6 @@ std::tuple CrudModel::createInsertQuery(const PendingRow q << ",$" << p; q << ") RETURNING *"; for (auto e : data) { - // Add value to paramlist - params.add(e.second, getType(e.first)); } q.flush(); @@ -410,74 +408,25 @@ QString CrudModel::createDeleteStatement() const } -std::tuple CrudModel::updateRow(const PendingRow &pending_row) -{ - auto data = pending_row.data(); - - if (!data.empty()) { - auto pkey_values = getPKeyForRow(pending_row.row()); - - QString buffer; - Pgsql::Params params; - if (pkey_values.empty()){ - std::tie(buffer, params) = createInsertQuery(pending_row); - } - else { - std::tie(buffer, params) = createUpdateQuery(pkey_values, pending_row); - } - - - int row_number = pending_row.row(); - Pgsql::Connection db_update_conn; - auto dbconfig = m_database->config(); - db_update_conn.connect(dbconfig.connectionString()); - try { - auto result = db_update_conn.queryParam(buffer, params); - if (result && result.rows() == 1) { - - std::vector values; - auto row = *result.begin(); - for (auto v : row) { - if (v.null()) - values.push_back(Value()); - else - values.push_back(std::string(v.c_str())); - } - - ModifiedRow modified_row(row_number, values); - - return { true, modified_row }; - } - } - catch (const Pgsql::PgResultError &ex) { - //Mes - } - } - return { false, {} }; -} - bool CrudModel::savePendingChanges() { while (!m_pendingRowList.m_rows.empty()) { auto iter = m_pendingRowList.m_rows.begin(); - auto [ok, modified_row] = updateRow(iter->second); + auto [ok, modified_row] = saveRow(iter->second); if (ok) { int rowKey = iter->first; - m_modifiedRowList.insert_or_assign(rowKey, modified_row); m_pendingRowList.m_rows.erase(iter); auto iter = std::find_if(m_rowMapping.begin(), m_rowMapping.end(), - [rowKey](const RowMapping &rhs) -> auto { return rhs.rowKey == rowKey; }); + [rowKey](const RowMapping &rhs) -> bool { return rhs.rowKey == rowKey; }); if (iter != m_rowMapping.end()) { - iter->modified = true; + iter->setModifiedRowData(modified_row); iter->pending = false; - int row = iter - m_rowMapping.begin(); - if (row == m_rowCount - 1) + if (IsLastRow(iter)) + { appendNewRow(); - + } } - - } else { return false; @@ -487,6 +436,11 @@ bool CrudModel::savePendingChanges() return true; } +bool CrudModel::IsLastRow(RowMappingVector::iterator mapping_iter) const +{ + return mapping_iter == --m_rowMapping.end(); +} + bool CrudModel::submit() { return savePendingChanges(); @@ -499,15 +453,12 @@ void CrudModel::revert() void CrudModel::appendNewRow() { - int row = m_rowCount; + int row = m_rowMapping.size(); beginInsertRows(QModelIndex(), row, row); - m_rowMapping.emplace_back(allocNewRowKey()); - ++m_rowCount; - endInsertRows(); + m_rowMapping.emplace_back(allocNewRowKey(), std::vector(m_roData->cols())); + endInsertRows(); } - - std::tuple CrudModel::removeRows(const std::set> &row_ranges) { if (row_ranges.empty()) return { true, "" }; @@ -545,29 +496,7 @@ std::tuple CrudModel::removeRows(const std::set // If something goes wrong after this commit we should reload contents of model } // Then from model - { - - int rows_deleted = 0; - for (auto range : row_ranges) { - range.setStart(range.start() - rows_deleted); - beginRemoveRows(QModelIndex(), range.start(), range.end() - 1); - SCOPE_EXIT { endRemoveRows(); }; - for (int current_row = range.start(); current_row < range.end(); ++current_row) { - auto&& mapping = m_rowMapping[static_cast(current_row)]; - - // if the row is in modified it can be removed from modified - if (mapping.modified) { - m_modifiedRowList.erase(mapping.rowKey); - } - /// \todo can it be pending? should be removed if it is. - - } - // remove the rows from m_rowMapping - auto first = m_rowMapping.begin() + range.start(); - m_rowMapping.erase(first, first + range.length()); - rows_deleted += range.length(); - } - } + RemoveRangesOfRowsFromModel(row_ranges); return { true, "" }; } catch (const Pgsql::PgResultError &error) { return { false, QString::fromUtf8(error.details().messageDetail.c_str()) }; @@ -583,4 +512,21 @@ bool CrudModel::removeRows(int row, int count, const QModelIndex &) return res; } +void CrudModel::RemoveRangesOfRowsFromModel(const std::set> &row_ranges) +{ + int rows_deleted = 0; + for (auto range : row_ranges) { + range.setStart(range.start() - rows_deleted); + RemoveRangeOfRowsFromModel(range); + rows_deleted += range.length(); + } +} + +void CrudModel::RemoveRangeOfRowsFromModel(IntegerRange range) +{ + beginRemoveRows(QModelIndex(), range.start(), range.end() - 1); + SCOPE_EXIT { endRemoveRows(); }; + auto first = m_rowMapping.begin() + range.start(); + m_rowMapping.erase(first, first + range.length()); +} diff --git a/pglab/CrudModel.h b/pglab/crud/CrudModel.h similarity index 52% rename from pglab/CrudModel.h rename to pglab/crud/CrudModel.h index 19482bb..92e2d58 100644 --- a/pglab/CrudModel.h +++ b/pglab/crud/CrudModel.h @@ -1,14 +1,13 @@ #ifndef CRUDMODEL_H #define CRUDMODEL_H -#include #include "ASyncDBConnection.h" #include "Pgsql_Connection.h" #include "IntegerRange.h" #include "catalog/PgClass.h" #include "catalog/PgConstraint.h" -#include "Pgsql_Connection.h" +#include #include #include #include @@ -19,38 +18,6 @@ class PgConstraint; class OpenDatabase; -/** - * @brief The CrudModel class - * - * Features - * - order by one or more user selectable columns - * - user filter condition - * - user limit and offset - * - hide columns (will not be retrieved can greatly speed up things when you disable wide columns) - * - can use foreign keys to display dropdown - * - * - * Need to keep track of changes from the original. Things to track - * - new data being entered for existing row - * - new data for new row - * - current data of modified row - * - current data of new row - * - which rows are deleted - * We need to be able to: - * - determine row count easily - * - find specific row from table easily - * - support resorting (without requerying) - * //- support custom filtering (without requerying) - * - * Keep data as much in original result - * - use redirect list to go from index to actual row in the result - * - use an additional map index by original row number containing changed data - * - might be beneficial to have vector in sync with original list to signal changes from the result - * this could prevent doing a lot of slower searches in the changed row set by only doing this when the direty markes - * has been set vector takes 1 bit per value making it very efficient and fast. We also could put a bit in the mapping - * vector but this probably would double the size of that list taking 32 times the size of vector. - * - */ class CrudModel: public QAbstractTableModel { Q_OBJECT public: @@ -110,41 +77,8 @@ private: }; - -// using RowData = std::vector; -// using RowDataPtr = std::unique_ptr; -// /** Choosen for a vector of pointers while vector is relatively slow with insertion and removal in the middle -// * I hope the small size of the elements will make this not much of an issue while the simple linear and sequential -// * nature of a vector will keep memory allocation fairly efficient. -// */ -// using RowList = std::vector; - - -// std::shared_ptr resultToRowList(std::shared_ptr result); - using Value = std::optional; - /** Used to remember the changes that have been made to rows. - * - * For simplicity it also holds the values for columns that have not been changed. - */ - class ModifiedRow { - public: - ModifiedRow() = default; - ModifiedRow(int row, const std::vector &values) - : m_row(row), m_values(values) - {} - ModifiedRow(int row, const std::vector &&values) - : m_row(row), m_values(values) - {} - const auto& data() const { return m_values; } - int row() const { return m_row; } - private: - int m_row = -1; - std::vector m_values; - }; - using ModifiedRowList = std::map; - /** Similar to a modified row but it only stores values for columns that actually have been edited. * */ @@ -218,16 +152,52 @@ private: class RowMapping { public: - bool modified = false; ///< Row has been modified, look there for actual data bool pending = false; ///< Row has pending changes, look first in pending for current data int rowKey = -1; ///< value to use as key/index into the data lists RowMapping() = default; - explicit RowMapping(int row_key) + explicit RowMapping(int row_key, std::vector data) : rowKey(row_key) + , modifiedRow(std::move(data)) {} + + RowMapping(int row_key) + : rowKey(row_key) + {} + + bool isModified() const + { + return !modifiedRow.empty(); + } + + Value modifiedValue(int columnIndex) const + { + return modifiedRow[columnIndex]; + } + + void setModifiedRowData(std::vector data) + { + modifiedRow = std::move(data); + } + + bool isNew() const + { + // row in m_roData, thus it is not new + if (modifiedRow.empty()) + return false; + + // if all elements are empty then this is a new row + for (auto &c : modifiedRow) + if (c.has_value()) + return false; + + return true; + } + private: + std::vector modifiedRow; }; + using RowMappingVector = std::vector; std::shared_ptr m_database; @@ -238,76 +208,52 @@ private: bool callLoadData = false; std::shared_ptr m_roData; - /// \brief Total number of rows to show. - /// - /// The first n rows are the rows in m_roData. - /// While a row is being edited the modifications are in the PendingRowList - /// When the edits have been submitted the new values are put in the ModifiedRowList - /// - /// The last row is for creating new rows. There is no data for this row or it is - /// in the PendingRowList. As soon as the row has been inserted a new last empty row - /// will be created by increasing this variable. And the newly stored row will be in - /// the modified list. - /// - int m_rowCount = 0; PendingRowList m_pendingRowList; - /// \brief Maintains a list of all modified rows. - /// - /// The key values are the indexes of the row before any rows were deleted. - /// - ModifiedRowList m_modifiedRowList; /// Keeps track of mapping grid rows to data rows - RowMappingVector m_rowMapping; - /// call on initial load to fill in the mappings + RowMappingVector m_rowMapping; + + /// call on initial load to fill in the mappings void initRowMapping(); - Value getData(const QModelIndex &index) const; + Value getLatestData(int column, int row) const; + Value getLatestData(const QModelIndex &index) const + { + return getLatestData(index.column(), index.row()); + } + Value getSavedData(int columnIndex, int rowIndex) const; + Value getSavedData(const RowMapping &row_mapping, int columnIndex) const; + Oid getType(int column) const; - /// - /// \brief columnName - /// \param col - /// \return - /// QString columnName(int col) const; - /// - /// \brief getModifiedRow searches for the specified row in the modified set - /// \param row - /// \return Pointer to the modified element or nullptr if there is no modification - /// - const ModifiedRow* getModifiedRow(int row) const; - - /// - /// \brief getPKeyForRow retrieve the primary key of the specified row - /// - /// This function should not be called when there is no primarykey. (Editing should be disabled anyway in that case) - /// - /// \param row Actual result row not intermeddiat model row - /// \return The values of the primary key column. If this is a new row it will return an empty list - /// - PKeyValues getPKeyForRow(int row) const; - Pgsql::Params getPKeyParamsForRow(int row) const; + /// Get the PKey values from when the row was last restored or retrieved. + Pgsql::Params getPKeyParamsForRow(int row) const; bool savePendingChanges(); - std::tuple createUpdateQuery(const PKeyValues &pkey_values, const PendingRow &pending_row); + std::tuple createUpdateQuery(const Pgsql::Params &pkey_params, const PendingRow &pending_row); std::tuple createInsertQuery(const PendingRow &pending_row); std::tuple createDeleteStatement(const PKeyValues &pkey_values); QString createDeleteStatement() const; - std::tuple updateRow(const PendingRow &pending_row); + std::tuple> saveRow(const PendingRow &pending_row); void appendNewRow(); - int lastRowKey = 0; + int lastRowKey = -1; int allocNewRowKey() { return ++lastRowKey; } /// Convert an attnum from the database catalog to the corresponding column in the model /// /// \todo still assumes columns are in order, all being shown and no special column like oid shown. int attNumToCol(int attnum) const { return attnum - 1; } + + void RemoveRangesOfRowsFromModel(const std::set> &row_ranges); + void RemoveRangeOfRowsFromModel(IntegerRange row_range); + + bool IsLastRow(RowMappingVector::iterator mapping_iter) const; private slots: void loadIntoModel(std::shared_ptr data); diff --git a/pglab/CrudTab.cpp b/pglab/crud/CrudTab.cpp similarity index 99% rename from pglab/CrudTab.cpp rename to pglab/crud/CrudTab.cpp index c50d7c8..03091fc 100644 --- a/pglab/CrudTab.cpp +++ b/pglab/crud/CrudTab.cpp @@ -16,8 +16,8 @@ CrudTab::CrudTab(IDatabaseWindow *context, QWidget *parent) : QWidget(parent) - , m_context(context) , ui(new Ui::CrudTab) + , m_context(context) { ui->setupUi(this); diff --git a/pglab/CrudTab.h b/pglab/crud/CrudTab.h similarity index 100% rename from pglab/CrudTab.h rename to pglab/crud/CrudTab.h diff --git a/pglab/CrudTab.ui b/pglab/crud/CrudTab.ui similarity index 100% rename from pglab/CrudTab.ui rename to pglab/crud/CrudTab.ui diff --git a/pglab/pglab.pro b/pglab/pglab.pro index 84868c6..241c979 100644 --- a/pglab/pglab.pro +++ b/pglab/pglab.pro @@ -59,8 +59,8 @@ SOURCES += main.cpp\ ConstraintModel.cpp \ IconColumnDelegate.cpp \ IndexModel.cpp \ - CrudTab.cpp \ - CrudModel.cpp \ + crud/CrudTab.cpp \ + crud/CrudModel.cpp \ PgLabItemDelegate.cpp \ Module.cpp \ EditorGutter.cpp \ @@ -133,8 +133,8 @@ HEADERS += \ ConstraintModel.h \ IconColumnDelegate.h \ IndexModel.h \ - CrudTab.h \ - CrudModel.h \ + crud/CrudTab.h \ + crud/CrudModel.h \ PgLabItemDelegate.h \ Module.h \ EditorGutter.h \ @@ -176,7 +176,7 @@ FORMS += \ QueryTab.ui \ ProcessStdioWidget.ui \ NamespaceFilterWidget.ui \ - CrudTab.ui \ + crud/CrudTab.ui \ CodeGenerator.ui RESOURCES += \ diff --git a/pgsql/Pgsql_Params.cpp b/pgsql/Pgsql_Params.cpp index 7ac9c7c..a816ac0 100644 --- a/pgsql/Pgsql_Params.cpp +++ b/pgsql/Pgsql_Params.cpp @@ -12,7 +12,7 @@ Params::Params(const Params& rhs) , m_paramFormats(rhs.m_paramFormats) { //std::vector m_paramValues; - copyValues(rhs.m_paramValues); + appendValues(rhs.m_paramValues); } Params& Params::operator=(const Params& rhs) @@ -21,7 +21,7 @@ Params& Params::operator=(const Params& rhs) m_paramTypes = rhs.m_paramTypes; m_paramLengths = rhs.m_paramLengths; m_paramFormats = rhs.m_paramFormats; - copyValues(rhs.m_paramValues); + appendValues(rhs.m_paramValues); } return *this; } @@ -82,20 +82,21 @@ Param Params::add(const char *data, Oid oid) p[len] = 0; } return addText(p, oid); - -// m_paramTypes.push_back(oid); -// m_paramValues.push_back(p); -// m_paramLengths.push_back(len); -// m_paramFormats.push_back(0); } -//void Params::addBinary(const char *data, int length, Oid oid) -//{ -// m_paramTypes.push_back(oid); -// m_paramValues.push_back(data); -// m_paramLengths.push_back(length); -// m_paramFormats.push_back(1); -//} +template +void concatContainers(T &d, const U& s) +{ + d.insert(d.end(), s.begin(), s.end()); +} + +void Params::addParams(const Params ¶ms) +{ + concatContainers(m_paramTypes, params.m_paramTypes); + concatContainers(m_paramLengths, params.m_paramLengths); + concatContainers(m_paramFormats, params.m_paramFormats); + appendValues(params.m_paramValues); +} void Params::clear() { @@ -106,13 +107,14 @@ void Params::clear() m_paramFormats.clear(); } -void Params::copyValues(const t_paramValues &r) +void Params::appendValues(const t_paramValues &r) { - const int n = static_cast(m_paramTypes.size()); - m_paramValues.reserve(n); + const int n = static_cast(r.size()); + const int ofs = m_paramValues.size(); + m_paramValues.reserve(ofs + n); for (int i = 0; i < n; ++i) { if (r[i]) { - const int len = m_paramLengths[i]; + const int len = m_paramLengths[ofs + i]; char * p = new char[len+1]; std::memcpy(p, r[i], len+1); m_paramValues.push_back(p); diff --git a/pgsql/Pgsql_Params.h b/pgsql/Pgsql_Params.h index 77a03b9..3d110df 100644 --- a/pgsql/Pgsql_Params.h +++ b/pgsql/Pgsql_Params.h @@ -41,6 +41,8 @@ namespace Pgsql { { return add(s ? s->c_str() : nullptr, oid); } + + void addParams(const Params ¶ms); //void addBinary(const char *data, int length, Oid oid); void clear(); @@ -62,7 +64,7 @@ namespace Pgsql { } /* Assumes other lists already have been copied */ - void copyValues(const t_paramValues &r); + void appendValues(const t_paramValues &r); std::vector m_paramTypes; t_paramValues m_paramValues;