The crud now support inserting new rows
This commit is contained in:
parent
c51bd7594d
commit
09d5c1e41a
2 changed files with 154 additions and 61 deletions
|
|
@ -66,13 +66,18 @@ QVariant CrudModel::headerData(int section, Qt::Orientation orientation, int rol
|
|||
// Basic functionality:
|
||||
int CrudModel::rowCount(const QModelIndex &/*parent*/) const
|
||||
{
|
||||
// int row_count = m_roData ? m_roData->rows() : 0;
|
||||
|
||||
return 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;
|
||||
}
|
||||
|
||||
int CrudModel::columnCount(const QModelIndex &/*parent*/) const
|
||||
{
|
||||
return m_roData ? m_roData->cols() : 0;
|
||||
int col_count = m_roData ? m_roData->cols() : 0;
|
||||
return col_count;
|
||||
}
|
||||
|
||||
Oid CrudModel::getType(int column) const
|
||||
|
|
@ -84,30 +89,32 @@ CrudModel::Value CrudModel::getData(const QModelIndex &index) const
|
|||
{
|
||||
Value value;
|
||||
if (m_roData) {
|
||||
int rij = index.row();
|
||||
int row = index.row();
|
||||
int col = index.column();
|
||||
|
||||
const int last_row = rowCount() - 1;
|
||||
|
||||
//Oid o = m_roData->type(col);
|
||||
// First see if we have buffered editted values that still need saving
|
||||
boost::optional<Value> val = m_pendingRowList.getValue(col, rij);
|
||||
if (!val) {
|
||||
boost::optional<Value> val = m_pendingRowList.getValue(col, row);
|
||||
if (!val && 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(rij);
|
||||
if (find_res != m_modifiedRowList.end()) {
|
||||
auto find_res = m_modifiedRowList.find(row);
|
||||
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) {
|
||||
if (!val && row < m_roData->rows()) {
|
||||
// Then we are going to read the original data.
|
||||
if (!m_roData->null(col, rij)) {
|
||||
value = std::string(m_roData->val(col, rij));
|
||||
}
|
||||
if (!m_roData->null(col, row))
|
||||
value = std::string(m_roData->val(col, row));
|
||||
}
|
||||
else {
|
||||
value = *val;
|
||||
if (val)
|
||||
value = *val;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
|
|
@ -169,6 +176,7 @@ void CrudModel::loadIntoModel(std::shared_ptr<Pgsql::Result> data)
|
|||
{
|
||||
beginResetModel();
|
||||
m_roData = data;
|
||||
m_rowCount = data->rows() + 1;
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
|
|
@ -205,21 +213,14 @@ bool CrudModel::setData(const QModelIndex &index, const QVariant &value, int rol
|
|||
if (role == Qt::EditRole) {
|
||||
int row = index.row();
|
||||
int col = index.column();
|
||||
//m_pendingRowList.getRow(row);
|
||||
|
||||
Value val;
|
||||
// Oid o = m_roData->type(index.column());
|
||||
// if (o == Pgsql::bool_oid) {
|
||||
// val = value.Bool ? "t" : "f";
|
||||
// }
|
||||
// else {
|
||||
std::string s = value.toString().toUtf8().data();
|
||||
if (!s.empty()) {
|
||||
if (s == "''")
|
||||
s.clear();
|
||||
val = s;
|
||||
}
|
||||
// }
|
||||
std::string s = value.toString().toUtf8().data();
|
||||
if (!s.empty()) {
|
||||
if (s == "''")
|
||||
s.clear();
|
||||
val = s;
|
||||
}
|
||||
m_pendingRowList.setValue(col, row, val);
|
||||
|
||||
emit dataChanged(index, index, QVector<int>() << role);
|
||||
|
|
@ -249,7 +250,7 @@ CrudModel::PKeyValues CrudModel::getPKeyForRow(int row) const
|
|||
values.push_back(*(mod_row->data()[col]));
|
||||
}
|
||||
}
|
||||
else {
|
||||
else if (row < m_roData->rows()){
|
||||
for (auto attnum : m_primaryKey->key) {
|
||||
int col = attnum - 1; // Assume column ordering matches table, also we assume know special columns like oid are shown
|
||||
values.push_back(m_roData->get(col, row).c_str());
|
||||
|
|
@ -264,41 +265,104 @@ QString CrudModel::columnName(int col) const
|
|||
return m_roData->getColName(col);
|
||||
}
|
||||
|
||||
std::tuple<QString, Pgsql::Params> CrudModel::createUpdateQuery(const PKeyValues &pkey_values, const PendingRow &pending_row)
|
||||
{
|
||||
Pgsql::Params params;
|
||||
auto data = pending_row.data();
|
||||
QString table_name = genFQTableName(*m_database->catalogue(), m_table);
|
||||
QString buffer;
|
||||
QTextStream q(&buffer);
|
||||
q << "UPDATE " << table_name << " AS d\n SET ";
|
||||
int param = 0;
|
||||
for (auto e : data) {
|
||||
if (param > 0)
|
||||
q << ",";
|
||||
q << quoteIdent(columnName(e.first)) << "=$" << ++param;
|
||||
|
||||
// Add value to paramlist
|
||||
params.add(e.second, getType(e.first));
|
||||
}
|
||||
|
||||
q << "\nWHERE ";
|
||||
int i = 0;
|
||||
for (auto attnum : m_primaryKey->key) {
|
||||
int col = attnum - 1; // Assume column ordering matches table, also we assume know special columns like oid are shown
|
||||
if (i > 0)
|
||||
q << " AND ";
|
||||
q << quoteIdent(columnName(col)) << "=$" << ++param;
|
||||
|
||||
params.add(pkey_values[i].c_str(), getType(col));
|
||||
++i;
|
||||
}
|
||||
q << "\nRETURNING *";
|
||||
q.flush();
|
||||
return { buffer, params };
|
||||
}
|
||||
|
||||
std::tuple<QString, Pgsql::Params> CrudModel::createInsertQuery(const PendingRow &pending_row)
|
||||
{
|
||||
Pgsql::Params params;
|
||||
auto data = pending_row.data();
|
||||
QString table_name = genFQTableName(*m_database->catalogue(), m_table);
|
||||
QString buffer;
|
||||
QTextStream q(&buffer);
|
||||
q << "INSERT INTO " << table_name << " VALUES ($1";
|
||||
for (size_t p = 2; p <= data.size(); ++p)
|
||||
q << ",$" << p;
|
||||
q << ") RETURNING *";
|
||||
for (auto e : data) {
|
||||
// Add value to paramlist
|
||||
params.add(e.second, getType(e.first));
|
||||
}
|
||||
q.flush();
|
||||
return { buffer, params };
|
||||
}
|
||||
|
||||
std::tuple<bool, CrudModel::ModifiedRow> CrudModel::updateRow(const PendingRow &pending_row)
|
||||
{
|
||||
auto data = pending_row.data();
|
||||
Pgsql::Params params;
|
||||
|
||||
if (!data.empty()) {
|
||||
auto pkey_values = getPKeyForRow(pending_row.row());
|
||||
|
||||
QString table_name = genFQTableName(*m_database->catalogue(), m_table);
|
||||
// QString table_name = genFQTableName(*m_database->catalogue(), m_table);
|
||||
// QString buffer;
|
||||
// QTextStream q(&buffer);
|
||||
// q << "UPDATE " << table_name << " AS d\n SET ";
|
||||
// int param = 0;
|
||||
// for (auto e : data) {
|
||||
// if (param > 0)
|
||||
// q << ",";
|
||||
// q << quoteIdent(columnName(e.first)) << "=$" << ++param;
|
||||
|
||||
// // Add value to paramlist
|
||||
// params.add(e.second, getType(e.first));
|
||||
// }
|
||||
|
||||
// q << "\nWHERE ";
|
||||
// int i = 0;
|
||||
// for (auto attnum : m_primaryKey->key) {
|
||||
// int col = attnum - 1; // Assume column ordering matches table, also we assume know special columns like oid are shown
|
||||
// if (i > 0)
|
||||
// q << " AND ";
|
||||
// q << quoteIdent(columnName(col)) << "=$" << ++param;
|
||||
|
||||
// params.add(pkey_values[i].c_str(), getType(col));
|
||||
// ++i;
|
||||
// }
|
||||
// q << "\nRETURNING *";
|
||||
// q.flush();
|
||||
|
||||
QString buffer;
|
||||
QTextStream q(&buffer);
|
||||
q << "UPDATE " << table_name << " AS d\n SET ";
|
||||
int param = 0;
|
||||
for (auto e : data) {
|
||||
if (param > 0)
|
||||
q << ",";
|
||||
q << quoteIdent(columnName(e.first)) << "=$" << ++param;
|
||||
|
||||
// Add value to paramlist
|
||||
params.add(e.second, getType(e.first));
|
||||
Pgsql::Params params;
|
||||
if (pkey_values.empty()){
|
||||
// todo insert query
|
||||
std::tie(buffer, params) = createInsertQuery(pending_row);
|
||||
}
|
||||
else {
|
||||
std::tie(buffer, params) = createUpdateQuery(pkey_values, pending_row);
|
||||
}
|
||||
|
||||
q << "\nWHERE ";
|
||||
int i = 0;
|
||||
for (auto attnum : m_primaryKey->key) {
|
||||
int col = attnum - 1; // Assume column ordering matches table, also we assume know special columns like oid are shown
|
||||
if (i > 0)
|
||||
q << " AND ";
|
||||
q << quoteIdent(columnName(col)) << "=$" << ++param;
|
||||
|
||||
params.add(pkey_values[i].c_str(), getType(col));
|
||||
++i;
|
||||
}
|
||||
q << "\nRETURNING *";
|
||||
q.flush();
|
||||
|
||||
int row_number = pending_row.row();
|
||||
Pgsql::Connection db_update_conn;
|
||||
|
|
@ -307,9 +371,6 @@ std::tuple<bool, CrudModel::ModifiedRow> CrudModel::updateRow(const PendingRow &
|
|||
if (res) {
|
||||
auto result = db_update_conn.queryParam(buffer, params);
|
||||
if (result && result.rows() == 1) {
|
||||
// pending row should be removed
|
||||
|
||||
// and the result should be stored as a modified row
|
||||
|
||||
std::vector<Value> values;
|
||||
auto row = *result.begin();
|
||||
|
|
@ -360,8 +421,16 @@ bool CrudModel::savePendingChanges()
|
|||
auto iter = m_pendingRowList.m_rows.begin();
|
||||
auto [ok, modified_row] = updateRow(iter->second);
|
||||
if (ok) {
|
||||
m_modifiedRowList.insert_or_assign(iter->first, modified_row);
|
||||
int row = iter->first;
|
||||
m_modifiedRowList.insert_or_assign(row, modified_row);
|
||||
m_pendingRowList.m_rows.erase(iter);
|
||||
|
||||
if (row == m_rowCount - 1)
|
||||
appendNewRow();
|
||||
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -377,3 +446,11 @@ void CrudModel::revert()
|
|||
{
|
||||
|
||||
}
|
||||
|
||||
void CrudModel::appendNewRow()
|
||||
{
|
||||
int row = m_rowCount;
|
||||
beginInsertRows(QModelIndex(), row, row);
|
||||
++m_rowCount;
|
||||
endInsertRows();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -197,13 +197,25 @@ private:
|
|||
bool callLoadData = false;
|
||||
|
||||
std::shared_ptr<Pgsql::Result> 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;
|
||||
|
||||
/** Maintains a list of all modified rows.
|
||||
*
|
||||
* The key values are the indexes of the row before any rows were deleted.
|
||||
*/
|
||||
/// \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;
|
||||
|
||||
using RedirectVec = std::vector<int>;
|
||||
|
|
@ -236,13 +248,17 @@ private:
|
|||
/// 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
|
||||
/// \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;
|
||||
|
||||
bool savePendingChanges();
|
||||
|
||||
std::tuple<QString, Pgsql::Params> createUpdateQuery(const PKeyValues &pkey_values, const PendingRow &pending_row);
|
||||
std::tuple<QString, Pgsql::Params> createInsertQuery(const PendingRow &pending_row);
|
||||
std::tuple<bool, ModifiedRow> updateRow(const PendingRow &pending_row);
|
||||
|
||||
void appendNewRow();
|
||||
private slots:
|
||||
|
||||
void connectionStateChanged(ASyncDBConnection::State state);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue