Improve CrudModel code quality
This commit is contained in:
parent
82cc9f14bc
commit
550049ceff
6 changed files with 870 additions and 205 deletions
|
|
@ -6,7 +6,6 @@
|
|||
#include "catalog/PgConstraintContainer.h"
|
||||
#include "catalog/PgClassContainer.h"
|
||||
#include "SqlFormattingUtils.h"
|
||||
#include "Pgsql_oids.h"
|
||||
#include "CustomDataRole.h"
|
||||
#include <QtConcurrent>
|
||||
#include <QFuture>
|
||||
|
|
@ -98,45 +97,46 @@ Oid CrudModel::getType(int column) const
|
|||
|
||||
CrudModel::Value CrudModel::getLatestData(int columnIndex, int rowIndex) const
|
||||
{
|
||||
if (m_roData)
|
||||
if (!m_roData)
|
||||
return {};
|
||||
|
||||
const RowMapping &row_mapping = m_rowMapping[rowIndex];
|
||||
if (row_mapping.pending)
|
||||
{
|
||||
auto row_mapping = m_rowMapping[rowIndex];
|
||||
|
||||
std::optional<Value> val;
|
||||
if (row_mapping.pending)
|
||||
val = m_pendingRowList.getValue(columnIndex, row_mapping.rowKey);
|
||||
|
||||
if (!val.has_value())
|
||||
val = getSavedData(row_mapping, columnIndex);
|
||||
|
||||
return val.value_or(Value());
|
||||
// an edited cell can be NULL, that is different from not having been edited
|
||||
if (auto edited = m_pendingRowList.getValue(columnIndex, row_mapping.rowKey))
|
||||
return *edited;
|
||||
}
|
||||
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 {};
|
||||
return getSavedData(row_mapping, columnIndex);
|
||||
}
|
||||
|
||||
CrudModel::Value CrudModel::getSavedData(const RowMapping &row_mapping, int columnIndex) const
|
||||
{
|
||||
std::optional<Value> val;
|
||||
if (!val.has_value() && row_mapping.isModified())
|
||||
val = row_mapping.modifiedValue(columnIndex);
|
||||
// rows that have been saved (or are new) have their own copy of the data
|
||||
if (row_mapping.isModified())
|
||||
return row_mapping.modifiedValue(columnIndex);
|
||||
|
||||
if (!val.has_value() && row_mapping.rowKey < m_roData->rows())
|
||||
if (!m_roData->null(columnIndex, row_mapping.rowKey))
|
||||
val = std::string(m_roData->val(columnIndex, row_mapping.rowKey));
|
||||
if (row_mapping.rowKey < m_roData->rows() && !m_roData->null(columnIndex, row_mapping.rowKey))
|
||||
return std::string(m_roData->val(columnIndex, row_mapping.rowKey));
|
||||
|
||||
return val.value_or(Value());
|
||||
return {};
|
||||
}
|
||||
|
||||
CrudModel::RowMapping* CrudModel::findRowMapping(int rowKey)
|
||||
{
|
||||
auto iter = std::find_if(m_rowMapping.begin(), m_rowMapping.end(),
|
||||
[rowKey](const RowMapping &rhs) { return rhs.rowKey == rowKey; });
|
||||
return iter != m_rowMapping.end() ? &*iter : nullptr;
|
||||
}
|
||||
|
||||
void CrudModel::updatePendingState(bool was_dirty)
|
||||
{
|
||||
if (was_dirty != hasPendingChanges())
|
||||
emit pendingChangesChanged(hasPendingChanges());
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
QVariant StringToVariant(const std::string &str, Oid oid)
|
||||
{
|
||||
Pgsql::Value v(str.c_str(), oid);
|
||||
|
|
@ -174,9 +174,10 @@ QVariant StringToVariant(const std::string &str, Oid oid)
|
|||
case Pgsql::float8_oid:
|
||||
return v.operator double();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QVariant CrudModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (index.column() < PreColumnCount)
|
||||
|
|
@ -254,8 +255,7 @@ void CrudModel::loadIntoModel(std::shared_ptr<Pgsql::Result> data)
|
|||
if (m_wholeTable)
|
||||
appendNewRowInternal();
|
||||
endResetModel();
|
||||
if (was_dirty)
|
||||
emit pendingChangesChanged(false);
|
||||
updatePendingState(was_dirty);
|
||||
}
|
||||
|
||||
void CrudModel::initRowMapping()
|
||||
|
|
@ -269,26 +269,11 @@ void CrudModel::initRowMapping()
|
|||
|
||||
void CrudModel::connectionStateChanged()
|
||||
{
|
||||
switch (m_dbConn.state()) {
|
||||
case ASyncDBConnection::State::NotConnected:
|
||||
break;
|
||||
case ASyncDBConnection::State::Connecting:
|
||||
break;
|
||||
case ASyncDBConnection::State::Connected:
|
||||
if (callLoadData)
|
||||
{
|
||||
callLoadData = false;
|
||||
loadData();
|
||||
}
|
||||
break;
|
||||
case ASyncDBConnection::State::QuerySend:
|
||||
|
||||
break;
|
||||
case ASyncDBConnection::State::CancelSend:
|
||||
break;
|
||||
case ASyncDBConnection::State::Terminating:
|
||||
break;
|
||||
}
|
||||
if (m_dbConn.state() == ASyncDBConnection::State::Connected && callLoadData)
|
||||
{
|
||||
callLoadData = false;
|
||||
loadData();
|
||||
}
|
||||
}
|
||||
|
||||
Qt::ItemFlags CrudModel::flags(const QModelIndex &index) const
|
||||
|
|
@ -326,8 +311,7 @@ bool CrudModel::setData(const QModelIndex &index, const QVariant &value, int rol
|
|||
}
|
||||
bool was_dirty = hasPendingChanges();
|
||||
m_pendingRowList.setValue(col, row_mapping.rowKey, val);
|
||||
if (!was_dirty)
|
||||
emit pendingChangesChanged(true);
|
||||
updatePendingState(was_dirty);
|
||||
|
||||
emit dataChanged(index, index, QVector<int>() << role);
|
||||
return true;
|
||||
|
|
@ -337,8 +321,7 @@ bool CrudModel::setData(const QModelIndex &index, const QVariant &value, int rol
|
|||
|
||||
std::tuple<bool, std::vector<CrudModel::Value>> CrudModel::saveRow(const PendingRow &pending_row, const RowMapping &row_mapping)
|
||||
{
|
||||
auto data = pending_row.data();
|
||||
if (data.empty())
|
||||
if (pending_row.data().empty())
|
||||
return { true, {} };
|
||||
|
||||
if (row_mapping.isNew())
|
||||
|
|
@ -348,31 +331,16 @@ std::tuple<bool, std::vector<CrudModel::Value>> CrudModel::saveRow(const Pending
|
|||
std::tie(buffer, params) = createInsertQuery(pending_row);
|
||||
|
||||
Pgsql::Connection db_update_conn;
|
||||
auto dbconfig = m_database->config();
|
||||
db_update_conn.connect(dbconfig.connectionString());
|
||||
connectForUpdate(db_update_conn);
|
||||
try
|
||||
{
|
||||
auto result = db_update_conn.queryParam(buffer, params);
|
||||
if (result && result.rows() == 1)
|
||||
{
|
||||
std::vector<Value> 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 };
|
||||
}
|
||||
return { true, rowValues(*result.begin()) };
|
||||
}
|
||||
catch (const Pgsql::PgResultError &ex)
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText(ex.what());
|
||||
msgBox.exec();
|
||||
reportError(ex.what());
|
||||
}
|
||||
return { false, {} };
|
||||
}
|
||||
|
|
@ -389,8 +357,7 @@ std::tuple<bool, std::vector<CrudModel::Value>> CrudModel::saveUpdate(const Pend
|
|||
try
|
||||
{
|
||||
Pgsql::Connection db_update_conn;
|
||||
auto dbconfig = m_database->config();
|
||||
db_update_conn.connect(dbconfig.connectionString());
|
||||
connectForUpdate(db_update_conn);
|
||||
auto tx = Pgsql::Transaction::startTransaction(db_update_conn);
|
||||
for (Oid table_oid : tables)
|
||||
{
|
||||
|
|
@ -418,32 +385,47 @@ std::tuple<bool, std::vector<CrudModel::Value>> CrudModel::saveUpdate(const Pend
|
|||
}
|
||||
catch (const Pgsql::PgResultError &ex)
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText(ex.what());
|
||||
msgBox.exec();
|
||||
reportError(ex.what());
|
||||
}
|
||||
return { false, {} };
|
||||
}
|
||||
|
||||
void CrudModel::connectForUpdate(Pgsql::Connection &connection) const
|
||||
{
|
||||
auto dbconfig = m_database->config();
|
||||
connection.connect(dbconfig.connectionString());
|
||||
}
|
||||
|
||||
void CrudModel::reportError(const QString &message) const
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText(message);
|
||||
msgBox.exec();
|
||||
}
|
||||
|
||||
std::vector<CrudModel::Value> CrudModel::rowValues(const Pgsql::Row &row)
|
||||
{
|
||||
std::vector<Value> values;
|
||||
for (auto v : row)
|
||||
{
|
||||
if (v.null())
|
||||
values.push_back(Value());
|
||||
else
|
||||
values.push_back(std::string(v.c_str()));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
QString CrudModel::tableName(Oid table_oid) const
|
||||
{
|
||||
auto table = m_database->catalog()->classes()->getByKey(table_oid);
|
||||
return table ? table->fullyQualifiedQuotedObjectName() : QString();
|
||||
}
|
||||
|
||||
std::vector<int> CrudModel::resultColumnsOfTable(Oid table_oid) const
|
||||
{
|
||||
std::vector<int> cols;
|
||||
for (int col = 0; col < m_roData->cols(); ++col)
|
||||
if (m_roData->ftable(col) == table_oid && m_roData->ftableCol(col) > 0)
|
||||
cols.push_back(col);
|
||||
return cols;
|
||||
}
|
||||
|
||||
QString CrudModel::attributeName(int col) const
|
||||
{
|
||||
if (col < static_cast<int>(columnList.size()) && columnList[col])
|
||||
return columnList[col]->name;
|
||||
if (col < static_cast<int>(m_columnList.size()) && m_columnList[col])
|
||||
return m_columnList[col]->name;
|
||||
return QString::fromUtf8(m_roData->getColName(col));
|
||||
}
|
||||
|
||||
|
|
@ -493,7 +475,7 @@ std::tuple<QString, Pgsql::Params> CrudModel::createUpdateQuery(Oid table_oid, c
|
|||
std::tuple<QString, Pgsql::Params> CrudModel::createInsertQuery(const PendingRow &pending_row)
|
||||
{
|
||||
Pgsql::Params params;
|
||||
auto data = pending_row.data();
|
||||
const auto &data = pending_row.data();
|
||||
QString table_name = m_table->fullyQualifiedQuotedObjectName();
|
||||
QString buffer;
|
||||
QTextStream q(&buffer);
|
||||
|
|
@ -523,7 +505,6 @@ std::tuple<QString, Pgsql::Params> CrudModel::createInsertQuery(const PendingRow
|
|||
|
||||
QString CrudModel::createDeleteStatement() const
|
||||
{
|
||||
Pgsql::Params params;
|
||||
QString table_name = m_table->fullyQualifiedQuotedObjectName();
|
||||
QString buffer;
|
||||
QTextStream q(&buffer);
|
||||
|
|
@ -547,40 +528,25 @@ bool CrudModel::savePendingChanges()
|
|||
while (!m_pendingRowList.m_rows.empty())
|
||||
{
|
||||
auto iter = m_pendingRowList.m_rows.begin();
|
||||
int pendingKey = iter->first;
|
||||
auto mapping_iter = std::find_if(m_rowMapping.begin(), m_rowMapping.end(),
|
||||
[pendingKey](const RowMapping &rhs) -> bool { return rhs.rowKey == pendingKey; });
|
||||
if (mapping_iter == m_rowMapping.end())
|
||||
RowMapping *mapping = findRowMapping(iter->first);
|
||||
if (!mapping)
|
||||
return false;
|
||||
|
||||
auto [ok, modified_row] = saveRow(iter->second, *mapping_iter);
|
||||
if (ok)
|
||||
{
|
||||
int rowKey = iter->first;
|
||||
m_pendingRowList.m_rows.erase(iter);
|
||||
auto [ok, modified_row] = saveRow(iter->second, *mapping);
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
auto iter = std::find_if(m_rowMapping.begin(), m_rowMapping.end(),
|
||||
[rowKey](const RowMapping &rhs) -> bool { return rhs.rowKey == rowKey; });
|
||||
if (iter != m_rowMapping.end())
|
||||
{
|
||||
iter->setModifiedRowData(modified_row);
|
||||
iter->pending = false;
|
||||
if (IsLastRow(iter))
|
||||
appendNewRow();
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
m_pendingRowList.m_rows.erase(iter);
|
||||
mapping->setModifiedRowData(modified_row);
|
||||
mapping->pending = false;
|
||||
// appending invalidates mapping, so keep this last
|
||||
if (mapping == &m_rowMapping.back())
|
||||
appendNewRow();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CrudModel::IsLastRow(RowMappingVector::iterator mapping_iter) const
|
||||
{
|
||||
return mapping_iter == --m_rowMapping.end();
|
||||
}
|
||||
|
||||
bool CrudModel::columnIsReadOnly(int column_index) const
|
||||
{
|
||||
if (m_roData == nullptr)
|
||||
|
|
@ -590,30 +556,30 @@ bool CrudModel::columnIsReadOnly(int column_index) const
|
|||
if (!m_mapping || !m_mapping->isEditable(col))
|
||||
return true;
|
||||
|
||||
const auto &column = columnList[col];
|
||||
const auto &column = m_columnList[col];
|
||||
return !column || column->getIdentity() == PgAttribute::Identity::Always
|
||||
|| column->getGenerated() != PgAttribute::Generated::None;
|
||||
}
|
||||
|
||||
void CrudModel::initializeColumnList()
|
||||
{
|
||||
columnList.clear();
|
||||
columnList.reserve(m_roData->cols());
|
||||
m_columnList.clear();
|
||||
m_columnList.reserve(m_roData->cols());
|
||||
auto atts = m_database->catalog()->attributes();
|
||||
for (int col = 0; col < m_roData->cols(); ++col)
|
||||
{
|
||||
int attnum = m_roData->ftableCol(col);
|
||||
if (attnum <= 0)
|
||||
{
|
||||
columnList.emplace_back();
|
||||
m_columnList.emplace_back();
|
||||
continue;
|
||||
}
|
||||
auto columns = atts->getColumnsForRelation(m_roData->ftable(col));
|
||||
auto find_result = std::find_if(columns.begin(), columns.end(), [attnum](const PgAttribute &att) { return att.num == attnum; });
|
||||
if (find_result != columns.end())
|
||||
columnList.emplace_back(*find_result);
|
||||
m_columnList.emplace_back(*find_result);
|
||||
else
|
||||
columnList.emplace_back();
|
||||
m_columnList.emplace_back();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -621,8 +587,7 @@ bool CrudModel::saveChanges()
|
|||
{
|
||||
bool was_dirty = hasPendingChanges();
|
||||
bool ok = savePendingChanges();
|
||||
if (was_dirty != hasPendingChanges())
|
||||
emit pendingChangesChanged(hasPendingChanges());
|
||||
updatePendingState(was_dirty);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
|
@ -664,49 +629,42 @@ std::tuple<bool, QString> CrudModel::removeRows(const std::set<IntegerRange<int>
|
|||
return { false, "Range error" };
|
||||
if (!m_wholeTable || !m_table || m_mapping->primaryKeyColumns(m_table->oid()).empty())
|
||||
return { false, "Rows can only be removed from a table with a primary key" };
|
||||
// When removing rows there is no direct mapping anymore between the rows in the grid
|
||||
// and the rows in m_roData
|
||||
|
||||
// Therefor we need an indirection to keep track of which rows are visible and which
|
||||
// grid row maps to what data row. Maybe we can also keep track where the current data
|
||||
// of the row is located original data, pending data or modified data
|
||||
|
||||
// 1. Get PKEY and remove that row from table
|
||||
|
||||
// When removing rows there is no direct mapping anymore between the rows in the grid
|
||||
// and the rows in m_roData, that is why the grid rows are mapped through m_rowMapping.
|
||||
try
|
||||
{
|
||||
Pgsql::Connection db_update_conn;
|
||||
auto dbconfig = m_database->config();
|
||||
db_update_conn.connect(dbconfig.connectionString());
|
||||
|
||||
// First delete rows in table
|
||||
QString delete_statement = createDeleteStatement();
|
||||
{
|
||||
auto tx = Pgsql::Transaction::startTransaction(db_update_conn);
|
||||
for (auto range : row_ranges)
|
||||
{
|
||||
for (int current_row = range.start(); current_row < range.end(); ++current_row)
|
||||
{
|
||||
auto&& mapping = m_rowMapping[static_cast<size_t>(current_row)];
|
||||
auto params = getPKeyParamsForRow(mapping, m_table->oid());
|
||||
if (!params.empty())
|
||||
db_update_conn.queryParam(delete_statement, params);
|
||||
}
|
||||
}
|
||||
tx.commit();
|
||||
// If something goes wrong after this commit we should reload contents of model
|
||||
}
|
||||
// Then from model
|
||||
deleteRowsFromDatabase(row_ranges);
|
||||
// If something goes wrong after the commit we should reload contents of model
|
||||
bool was_dirty = hasPendingChanges();
|
||||
RemoveRangesOfRowsFromModel(row_ranges);
|
||||
if (was_dirty != hasPendingChanges())
|
||||
emit pendingChangesChanged(hasPendingChanges());
|
||||
return { true, "" };
|
||||
removeRangesFromModel(row_ranges);
|
||||
updatePendingState(was_dirty);
|
||||
return { true, "" };
|
||||
}
|
||||
catch (const Pgsql::PgResultError &error)
|
||||
{
|
||||
return { false, QString::fromUtf8(error.details().messageDetail.c_str()) };
|
||||
}
|
||||
return { false, QString::fromUtf8(error.details().messageDetail.c_str()) };
|
||||
}
|
||||
}
|
||||
|
||||
void CrudModel::deleteRowsFromDatabase(const std::set<IntegerRange<int>> &row_ranges)
|
||||
{
|
||||
Pgsql::Connection db_update_conn;
|
||||
connectForUpdate(db_update_conn);
|
||||
|
||||
QString delete_statement = createDeleteStatement();
|
||||
auto tx = Pgsql::Transaction::startTransaction(db_update_conn);
|
||||
for (const auto &range : row_ranges)
|
||||
{
|
||||
for (int current_row = range.start(); current_row < range.end(); ++current_row)
|
||||
{
|
||||
const auto &mapping = m_rowMapping[static_cast<size_t>(current_row)];
|
||||
auto params = getPKeyParamsForRow(mapping, m_table->oid());
|
||||
if (!params.empty())
|
||||
db_update_conn.queryParam(delete_statement, params);
|
||||
}
|
||||
}
|
||||
tx.commit();
|
||||
}
|
||||
|
||||
bool CrudModel::removeRows(int row, int count, const QModelIndex &)
|
||||
|
|
@ -719,18 +677,18 @@ bool CrudModel::removeRows(int row, int count, const QModelIndex &)
|
|||
return res;
|
||||
}
|
||||
|
||||
void CrudModel::RemoveRangesOfRowsFromModel(const std::set<IntegerRange<int>> &row_ranges)
|
||||
void CrudModel::removeRangesFromModel(const std::set<IntegerRange<int>> &row_ranges)
|
||||
{
|
||||
int rows_deleted = 0;
|
||||
for (auto range : row_ranges)
|
||||
{
|
||||
range.setStart(range.start() - rows_deleted);
|
||||
RemoveRangeOfRowsFromModel(range);
|
||||
removeRangeFromModel(range);
|
||||
rows_deleted += range.length();
|
||||
}
|
||||
}
|
||||
|
||||
void CrudModel::RemoveRangeOfRowsFromModel(IntegerRange<int> range)
|
||||
void CrudModel::removeRangeFromModel(IntegerRange<int> range)
|
||||
{
|
||||
beginRemoveRows(QModelIndex(), range.start(), range.end() - 1);
|
||||
SCOPE_EXIT { endRemoveRows(); };
|
||||
|
|
|
|||
|
|
@ -60,11 +60,10 @@ signals:
|
|||
/// Emitted when the model goes from clean to having pending changes or back
|
||||
void pendingChangesChanged(bool hasChanges);
|
||||
|
||||
public slots:
|
||||
|
||||
private:
|
||||
|
||||
const int PreColumnCount = 1;
|
||||
/// Number of grid columns in front of the result columns (the row number column)
|
||||
static constexpr int PreColumnCount = 1;
|
||||
|
||||
|
||||
class ColumnSort {
|
||||
|
|
@ -125,17 +124,6 @@ private:
|
|||
public:
|
||||
using Map = std::map<int, PendingRow>;
|
||||
|
||||
PendingRow& getRow(int row)
|
||||
{
|
||||
auto iter = m_rows.lower_bound(row);
|
||||
if (iter != m_rows.end() && iter->first == row) {
|
||||
return iter->second;
|
||||
}
|
||||
else {
|
||||
return m_rows.insert(iter, {row, PendingRow(row)})->second;
|
||||
}
|
||||
}
|
||||
|
||||
void setValue(int col, int row, const Value &value)
|
||||
{
|
||||
auto iter = m_rows.find(row);
|
||||
|
|
@ -226,13 +214,7 @@ private:
|
|||
|
||||
std::shared_ptr<Pgsql::Result> m_roData;
|
||||
|
||||
struct ColumnData
|
||||
{
|
||||
std::string name;
|
||||
// identity allways or generated
|
||||
};
|
||||
|
||||
std::vector<std::optional<PgAttribute>> columnList; // attribute per column in m_roData, empty for expression columns
|
||||
std::vector<std::optional<PgAttribute>> m_columnList; // attribute per column in m_roData, empty for expression columns
|
||||
|
||||
PendingRowList m_pendingRowList;
|
||||
|
||||
|
|
@ -242,47 +224,64 @@ private:
|
|||
/// call on initial load to fill in the mappings
|
||||
void initRowMapping();
|
||||
|
||||
/// Value as shown in the grid, pending edits take precedence over saved data
|
||||
Value getLatestData(int column, int row) const;
|
||||
Value getLatestData(const QModelIndex &index) const
|
||||
{
|
||||
return getLatestData(index.column() - PreColumnCount, index.row());
|
||||
}
|
||||
Value getSavedData(int columnIndex, int rowIndex) const;
|
||||
/// Value as last loaded from or saved to the database
|
||||
Value getSavedData(const RowMapping &row_mapping, int columnIndex) const;
|
||||
|
||||
/// Mapping of the row with the given key, nullptr when the row is not in the grid (anymore)
|
||||
RowMapping* findRowMapping(int rowKey);
|
||||
/// Call after changing the pending rows to emit pendingChangesChanged when the state toggled
|
||||
void updatePendingState(bool was_dirty);
|
||||
|
||||
Oid getType(int column) const;
|
||||
|
||||
|
||||
/// Get the PKey values from when the row was last restored or retrieved.
|
||||
Pgsql::Params getPKeyParamsForRow(const RowMapping &row_mapping, Oid table_oid) const;
|
||||
// ---- Saving, all of these only use the data of the pending row and the row mapping
|
||||
|
||||
bool savePendingChanges();
|
||||
/// Inserts the row when it is new otherwise updates it. Returns the values of the row as stored in the database.
|
||||
std::tuple<bool, std::vector<Value>> saveRow(const PendingRow &pending_row, const RowMapping &row_mapping);
|
||||
std::tuple<bool, std::vector<Value>> saveUpdate(const PendingRow &pending_row, const RowMapping &row_mapping);
|
||||
|
||||
std::tuple<QString, Pgsql::Params> createUpdateQuery(Oid table_oid, const PendingRow &pending_row, const RowMapping &row_mapping);
|
||||
/// Get the PKey values from when the row was last restored or retrieved.
|
||||
Pgsql::Params getPKeyParamsForRow(const RowMapping &row_mapping, Oid table_oid) const;
|
||||
std::tuple<QString, Pgsql::Params> createUpdateQuery(Oid table_oid, const PendingRow &pending_row, const RowMapping &row_mapping);
|
||||
std::tuple<QString, Pgsql::Params> createInsertQuery(const PendingRow &pending_row);
|
||||
|
||||
QString createDeleteStatement() const;
|
||||
std::tuple<bool, std::vector<Value>> saveRow(const PendingRow &pending_row, const RowMapping &row_mapping);
|
||||
std::tuple<bool, std::vector<Value>> saveUpdate(const PendingRow &pending_row, const RowMapping &row_mapping);
|
||||
|
||||
/// Opens a separate connection for writing so edits don't interfere with the loading connection
|
||||
void connectForUpdate(Pgsql::Connection &connection) const;
|
||||
/// Tells the user about a failed write, the one place where the model shows a dialog
|
||||
void reportError(const QString &message) const;
|
||||
static std::vector<Value> rowValues(const Pgsql::Row &row);
|
||||
|
||||
// ---- Rows
|
||||
|
||||
void appendNewRow();
|
||||
void appendNewRowInternal();
|
||||
void appendNewRowInternal();
|
||||
|
||||
int lastRowKey = -1;
|
||||
int lastRowKey = -1;
|
||||
int allocNewRowKey() { return ++lastRowKey; }
|
||||
|
||||
/// Deletes the rows from the database in one transaction, throws on failure
|
||||
void deleteRowsFromDatabase(const std::set<IntegerRange<int>> &row_ranges);
|
||||
/// Removes the rows from the grid and drops their pending edits
|
||||
void removeRangesFromModel(const std::set<IntegerRange<int>> &row_ranges);
|
||||
void removeRangeFromModel(IntegerRange<int> row_range);
|
||||
|
||||
// ---- Table and column information
|
||||
|
||||
/// Name of the table with the given oid, quoted and schema qualified
|
||||
QString tableName(Oid table_oid) const;
|
||||
/// Result columns that are user columns (attnum > 0) of the given table, in result order
|
||||
std::vector<int> resultColumnsOfTable(Oid table_oid) const;
|
||||
/// Catalog name of the attribute behind a result column, falls back to the result column name
|
||||
QString attributeName(int col) const;
|
||||
bool columnIsReadOnly(int column_index) const;
|
||||
void initializeColumnList();
|
||||
|
||||
void RemoveRangesOfRowsFromModel(const std::set<IntegerRange<int>> &row_ranges);
|
||||
void RemoveRangeOfRowsFromModel(IntegerRange<int> row_range);
|
||||
|
||||
bool IsLastRow(RowMappingVector::iterator mapping_iter) const;
|
||||
bool columnIsReadOnly(int column_index) const;
|
||||
void initializeColumnList();
|
||||
private slots:
|
||||
|
||||
void loadIntoModel(std::shared_ptr<Pgsql::Result> data);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue