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:
|
// Basic functionality:
|
||||||
int CrudModel::rowCount(const QModelIndex &/*parent*/) const
|
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
|
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
|
Oid CrudModel::getType(int column) const
|
||||||
|
|
@ -84,29 +89,31 @@ CrudModel::Value CrudModel::getData(const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
Value value;
|
Value value;
|
||||||
if (m_roData) {
|
if (m_roData) {
|
||||||
int rij = index.row();
|
int row = index.row();
|
||||||
int col = index.column();
|
int col = index.column();
|
||||||
|
|
||||||
|
const int last_row = rowCount() - 1;
|
||||||
|
|
||||||
//Oid o = m_roData->type(col);
|
//Oid o = m_roData->type(col);
|
||||||
// First see if we have buffered editted values that still need saving
|
// First see if we have buffered editted values that still need saving
|
||||||
boost::optional<Value> val = m_pendingRowList.getValue(col, rij);
|
boost::optional<Value> val = m_pendingRowList.getValue(col, row);
|
||||||
if (!val) {
|
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
|
// No pending save have a look if we have modified saved data in the modified list
|
||||||
auto find_res = m_modifiedRowList.find(rij);
|
auto find_res = m_modifiedRowList.find(row);
|
||||||
if (find_res != m_modifiedRowList.end()) {
|
if (find_res != m_modifiedRowList.end())
|
||||||
val = find_res->second.data()[col];
|
val = find_res->second.data()[col];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Value value;
|
//Value value;
|
||||||
// If we did not have pending or modified data
|
// 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.
|
// Then we are going to read the original data.
|
||||||
if (!m_roData->null(col, rij)) {
|
if (!m_roData->null(col, row))
|
||||||
value = std::string(m_roData->val(col, rij));
|
value = std::string(m_roData->val(col, row));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if (val)
|
||||||
value = *val;
|
value = *val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -169,6 +176,7 @@ void CrudModel::loadIntoModel(std::shared_ptr<Pgsql::Result> data)
|
||||||
{
|
{
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
m_roData = data;
|
m_roData = data;
|
||||||
|
m_rowCount = data->rows() + 1;
|
||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,21 +213,14 @@ bool CrudModel::setData(const QModelIndex &index, const QVariant &value, int rol
|
||||||
if (role == Qt::EditRole) {
|
if (role == Qt::EditRole) {
|
||||||
int row = index.row();
|
int row = index.row();
|
||||||
int col = index.column();
|
int col = index.column();
|
||||||
//m_pendingRowList.getRow(row);
|
|
||||||
|
|
||||||
Value val;
|
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();
|
std::string s = value.toString().toUtf8().data();
|
||||||
if (!s.empty()) {
|
if (!s.empty()) {
|
||||||
if (s == "''")
|
if (s == "''")
|
||||||
s.clear();
|
s.clear();
|
||||||
val = s;
|
val = s;
|
||||||
}
|
}
|
||||||
// }
|
|
||||||
m_pendingRowList.setValue(col, row, val);
|
m_pendingRowList.setValue(col, row, val);
|
||||||
|
|
||||||
emit dataChanged(index, index, QVector<int>() << role);
|
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]));
|
values.push_back(*(mod_row->data()[col]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else if (row < m_roData->rows()){
|
||||||
for (auto attnum : m_primaryKey->key) {
|
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
|
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());
|
values.push_back(m_roData->get(col, row).c_str());
|
||||||
|
|
@ -264,14 +265,10 @@ QString CrudModel::columnName(int col) const
|
||||||
return m_roData->getColName(col);
|
return m_roData->getColName(col);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::tuple<QString, Pgsql::Params> CrudModel::createUpdateQuery(const PKeyValues &pkey_values, const PendingRow &pending_row)
|
||||||
std::tuple<bool, CrudModel::ModifiedRow> CrudModel::updateRow(const PendingRow &pending_row)
|
|
||||||
{
|
{
|
||||||
auto data = pending_row.data();
|
|
||||||
Pgsql::Params params;
|
Pgsql::Params params;
|
||||||
if (!data.empty()) {
|
auto data = pending_row.data();
|
||||||
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;
|
QString buffer;
|
||||||
QTextStream q(&buffer);
|
QTextStream q(&buffer);
|
||||||
|
|
@ -299,6 +296,73 @@ std::tuple<bool, CrudModel::ModifiedRow> CrudModel::updateRow(const PendingRow &
|
||||||
}
|
}
|
||||||
q << "\nRETURNING *";
|
q << "\nRETURNING *";
|
||||||
q.flush();
|
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();
|
||||||
|
|
||||||
|
if (!data.empty()) {
|
||||||
|
auto pkey_values = getPKeyForRow(pending_row.row());
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int row_number = pending_row.row();
|
int row_number = pending_row.row();
|
||||||
Pgsql::Connection db_update_conn;
|
Pgsql::Connection db_update_conn;
|
||||||
|
|
@ -307,9 +371,6 @@ std::tuple<bool, CrudModel::ModifiedRow> CrudModel::updateRow(const PendingRow &
|
||||||
if (res) {
|
if (res) {
|
||||||
auto result = db_update_conn.queryParam(buffer, params);
|
auto result = db_update_conn.queryParam(buffer, params);
|
||||||
if (result && result.rows() == 1) {
|
if (result && result.rows() == 1) {
|
||||||
// pending row should be removed
|
|
||||||
|
|
||||||
// and the result should be stored as a modified row
|
|
||||||
|
|
||||||
std::vector<Value> values;
|
std::vector<Value> values;
|
||||||
auto row = *result.begin();
|
auto row = *result.begin();
|
||||||
|
|
@ -360,8 +421,16 @@ bool CrudModel::savePendingChanges()
|
||||||
auto iter = m_pendingRowList.m_rows.begin();
|
auto iter = m_pendingRowList.m_rows.begin();
|
||||||
auto [ok, modified_row] = updateRow(iter->second);
|
auto [ok, modified_row] = updateRow(iter->second);
|
||||||
if (ok) {
|
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);
|
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;
|
bool callLoadData = false;
|
||||||
|
|
||||||
std::shared_ptr<Pgsql::Result> m_roData;
|
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;
|
PendingRowList m_pendingRowList;
|
||||||
|
|
||||||
/** Maintains a list of all modified rows.
|
/// \brief Maintains a list of all modified rows.
|
||||||
*
|
///
|
||||||
* The key values are the indexes of the row before any rows were deleted.
|
/// The key values are the indexes of the row before any rows were deleted.
|
||||||
*/
|
///
|
||||||
ModifiedRowList m_modifiedRowList;
|
ModifiedRowList m_modifiedRowList;
|
||||||
|
|
||||||
using RedirectVec = std::vector<int>;
|
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)
|
/// 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
|
/// \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;
|
PKeyValues getPKeyForRow(int row) const;
|
||||||
|
|
||||||
bool savePendingChanges();
|
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);
|
std::tuple<bool, ModifiedRow> updateRow(const PendingRow &pending_row);
|
||||||
|
|
||||||
|
void appendNewRow();
|
||||||
private slots:
|
private slots:
|
||||||
|
|
||||||
void connectionStateChanged(ASyncDBConnection::State state);
|
void connectionStateChanged(ASyncDBConnection::State state);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue