From 628c16e2f408229f4b77870b9f8f8619f15fd2b7 Mon Sep 17 00:00:00 2001 From: eelke Date: Sun, 18 Feb 2018 07:15:43 +0100 Subject: [PATCH] Updating rows kinda works. Blocking calls are still used. --- pglab/CrudModel.cpp | 102 +++++++++++++++-------------- pglab/CrudModel.h | 12 +++- pglablib/PgConstraintContainer.cpp | 15 +++-- pgsql/Pgsql_Connection.cpp | 12 ++++ pgsql/Pgsql_Connection.h | 4 ++ pgsql/Pgsql_Params.h | 10 ++- pgsql/Pgsql_Result.cpp | 4 +- pgsql/Pgsql_Row.cpp | 5 +- pgsql/Pgsql_Row.h | 51 +++++++++++++++ pgsql/Pgsql_Value.h | 45 ++++++++----- 10 files changed, 179 insertions(+), 81 deletions(-) diff --git a/pglab/CrudModel.cpp b/pglab/CrudModel.cpp index e08a832..3e10332 100644 --- a/pglab/CrudModel.cpp +++ b/pglab/CrudModel.cpp @@ -40,7 +40,9 @@ void CrudModel::setConfig(std::shared_ptr db, const PgClass &table m_primaryKey = db->catalogue()->constraints()->getPrimaryForRelation(table.oid); //cat->attributes()->getColumnsForRelation() callLoadData = true; - m_dbConn.setupConnection(m_database->config()); + auto dbconfig = m_database->config(); + m_dbConn.setupConnection(dbconfig); + } QVariant CrudModel::headerData(int section, Qt::Orientation orientation, int role) const @@ -115,21 +117,10 @@ QVariant CrudModel::data(const QModelIndex &index, int role) const { QVariant v; if (role == Qt::EditRole) { -// int row = index.row(); -// if (m_pendingChanges.needsToSave(row)) { -//// if (savePendingChanges()) -//// startEditingRow(); -// } auto value = getData(index); if (value) { -// Oid o = m_roData->type(index.column()); -// if (o == Pgsql::bool_oid) { -// v = *value == "t"; //s = (s == "t") ? "TRUE" : "FALSE"; -// } -// else { - QString s = QString::fromUtf8(value->c_str()); - v = s; -// } + QString s = QString::fromUtf8(value->c_str()); + v = s; } } @@ -171,11 +162,6 @@ void CrudModel::loadData() if (res.valid()) { auto dbres = res.get(); if (dbres && *dbres) { -// WorkManager::getWorkManager()->addWork( -// [dbres, this] () -> void { -// std::shared_ptr rl = resultToRowList(dbres); -// m_asyncWindow->QueueTask([this, rl]() { loadIntoModel(rl); }); -// }); m_asyncWindow->QueueTask([this, dbres]() { loadIntoModel(dbres); }); } } @@ -285,54 +271,73 @@ QString CrudModel::columnName(int col) const } - -void CrudModel::updateRow(const PendingRow &pending_row) +std::tuple CrudModel::updateRow(const PendingRow &pending_row) { - auto pkey_values = getPKeyForRow(pending_row.row()); - - QString buffer; - QTextStream q(&buffer); - q << "UPDATE "; - q << genFQTableName(*m_database->catalogue(), m_table); - q << " AS d\n SET "; - 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 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; - q << quoteIdent(columnName(e.first)) - << "=$" << ++param; - - if (e.second) - params.add(e.second->c_str(), getType(e.first)); - else - params.add(nullptr, getType(e.first)); + // 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 - params.add(pkey_values[i].c_str(), getType(col)); 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; + auto dbconfig = m_database->config(); + bool res = db_update_conn.connect(dbconfig.getKeywords(), dbconfig.getValues(), false); + if (res) { + auto result = db_update_conn.queryParam(buffer, params); + if (result && result.rows() == 1) { + // pending row should be removed - m_dbConn.send(buffer.toUtf8().data(), params, - [this] (Expected> result, qint64) { + // and the result should be stored as a modified row - } ); + 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 }; + } + } + +// m_dbConn.send(buffer.toUtf8().data(), params, +// [row_number, this] (Expected> result, qint64) { + +// } ); } - + return { false, {} }; } bool CrudModel::savePendingChanges() @@ -357,15 +362,14 @@ bool CrudModel::savePendingChanges() // - for (const auto& row : m_pendingRowList) { -// int row_number = row.first; -// if (row_number >= 0) { -//// update -// } - updateRow(row.second); - + while (!m_pendingRowList.m_rows.empty()) { + auto iter = m_pendingRowList.m_rows.begin(); + auto [ok, modified_row] = updateRow(iter->second); + if (ok) { + m_modifiedRowList.emplace(iter->first, modified_row); + m_pendingRowList.m_rows.erase(iter); + } } - m_pendingRowList.clear(); return true; } diff --git a/pglab/CrudModel.h b/pglab/CrudModel.h index 2a5de63..84d8d03 100644 --- a/pglab/CrudModel.h +++ b/pglab/CrudModel.h @@ -3,6 +3,8 @@ #include #include "ASyncDBConnection.h" +#include "Pgsql_Connection.h" + #include "PgClass.h" #include "PgConstraint.h" #include "Pgsql_Connection.h" @@ -98,13 +100,17 @@ private: */ 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; + int m_row = -1; std::vector m_values; }; using ModifiedRowList = std::map; @@ -177,7 +183,6 @@ private: void clear() { m_rows.clear(); } - private: Map m_rows; }; @@ -188,6 +193,7 @@ private: PgClass m_table; boost::optional m_primaryKey; ASyncDBConnection m_dbConn; + bool callLoadData = false; std::shared_ptr m_roData; @@ -236,7 +242,7 @@ private: bool savePendingChanges(); - void updateRow(const PendingRow &pending_row); + std::tuple updateRow(const PendingRow &pending_row); private slots: void connectionStateChanged(ASyncDBConnection::State state); diff --git a/pglablib/PgConstraintContainer.cpp b/pglablib/PgConstraintContainer.cpp index 77efd1f..0f2d448 100644 --- a/pglablib/PgConstraintContainer.cpp +++ b/pglablib/PgConstraintContainer.cpp @@ -35,18 +35,19 @@ FROM pg_constraint)__"; PgConstraint PgConstraintContainer::loadElem(const Pgsql::Row &row) { - Pgsql::Col col(row); + using namespace Pgsql; + Col col(row); PgConstraint v; col >> v.oid >> v.name >> v.connamespace >> v.type >> v.deferrable >> v.deferred >> v.validated >> v.relid >> v.typid >> v.indid >> v.frelid >> v.fupdtype >> v.fdeltype >> v.fmatchtype >> v.islocal >> v.inhcount >> v.noinherit; - col.getAsArray(std::back_inserter(v.key)); - col.getAsArray(std::back_inserter(v.fkey)); - col.getAsArray(std::back_inserter(v.pfeqop)); - col.getAsArray(std::back_inserter(v.ppeqop)); - col.getAsArray(std::back_inserter(v.ffeqop)); - col.getAsArray(std::back_inserter(v.exclop)); + col.getAsArray(std::back_inserter(v.key), NullHandling::Ignore); + col.getAsArray(std::back_inserter(v.fkey), NullHandling::Ignore); + col.getAsArray(std::back_inserter(v.pfeqop), NullHandling::Ignore); + col.getAsArray(std::back_inserter(v.ppeqop), NullHandling::Ignore); + col.getAsArray(std::back_inserter(v.ffeqop), NullHandling::Ignore); + col.getAsArray(std::back_inserter(v.exclop), NullHandling::Ignore); col >> v.bin >> v.src >> v.definition; return v; } diff --git a/pgsql/Pgsql_Connection.cpp b/pgsql/Pgsql_Connection.cpp index 7911c84..728e18d 100644 --- a/pgsql/Pgsql_Connection.cpp +++ b/pgsql/Pgsql_Connection.cpp @@ -153,6 +153,18 @@ Result Connection::query(const char * command) return Result(result); } +Result Connection::queryParam(const char * command, const Params ¶ms) +{ + PGresult *result = PQexecParams(conn, command, params.size(), params.types(), + params.values(), params.lengths(), params.formats(), 0); + return Result(result); +} + +Result Connection::queryParam(const QString &command, const Params ¶ms) +{ + return queryParam(command.toUtf8().data(), params); +} + bool Connection::sendQuery(const char *query) { diff --git a/pgsql/Pgsql_Connection.h b/pgsql/Pgsql_Connection.h index 6207653..c9fd189 100644 --- a/pgsql/Pgsql_Connection.h +++ b/pgsql/Pgsql_Connection.h @@ -92,11 +92,15 @@ namespace Pgsql { std::string getErrorMessage() const; Result query(const char * command); + Result query(const QString &command) { return query(command.toUtf8().data()); } + Result queryParam(const char * command, const Params ¶ms); + Result queryParam(const QString &command, const Params ¶ms); + bool sendQuery(const char * query); bool sendQuery(const std::string &command) { diff --git a/pgsql/Pgsql_Params.h b/pgsql/Pgsql_Params.h index 5a3a801..36c0660 100644 --- a/pgsql/Pgsql_Params.h +++ b/pgsql/Pgsql_Params.h @@ -5,6 +5,8 @@ #include #include #include "Pgsql_declare.h" +#include "Pgsql_oids.h" +#include namespace Pgsql { @@ -18,8 +20,12 @@ namespace Pgsql { ~Params(); - void add(const QString &s, Oid oid=VARCHAROID); - void add(const char *data, Oid oid=VARCHAROID); + void add(const QString &s, Oid oid=varchar_oid); + void add(const char *data, Oid oid=varchar_oid); + void add(boost::optional s, Oid oid=varchar_oid) + { + add(s ? s->c_str() : nullptr, oid); + } //void addBinary(const char *data, int length, Oid oid); void clear(); diff --git a/pgsql/Pgsql_Result.cpp b/pgsql/Pgsql_Result.cpp index 0a1744a..1bcfa76 100644 --- a/pgsql/Pgsql_Result.cpp +++ b/pgsql/Pgsql_Result.cpp @@ -183,8 +183,10 @@ Value Result::get(int col, int row) const colRangeCheck(col); rowRangeCheck(row); + bool is_null = PQgetisnull(result, row, col); + char *ptr = is_null ? nullptr : PQgetvalue(result, row, col); return Value( - PQgetvalue(result, row, col), + ptr, PQftype(result, col) ); } diff --git a/pgsql/Pgsql_Row.cpp b/pgsql/Pgsql_Row.cpp index ed281ac..2dce846 100644 --- a/pgsql/Pgsql_Row.cpp +++ b/pgsql/Pgsql_Row.cpp @@ -33,4 +33,7 @@ Value Row::get(int col) const //{ //} - +Row::const_iterator Row::end() const +{ + return const_iterator(*this, m_result.cols()); +} diff --git a/pgsql/Pgsql_Row.h b/pgsql/Pgsql_Row.h index 81d5ae0..74e091a 100644 --- a/pgsql/Pgsql_Row.h +++ b/pgsql/Pgsql_Row.h @@ -14,6 +14,51 @@ namespace Pgsql { */ class Row { public: + class const_iterator { + public: + const_iterator(const Row &r, int col) + : m_row(r) + , m_col(col) + {} + + const_iterator operator++() + { + const_iterator t(*this); + ++m_col; + return t; + } + + const_iterator& operator++(int) + { + ++m_col; + return *this; + } + + bool operator==(const const_iterator &rhs) + { + return m_row == rhs.m_row && m_col == rhs.m_col; + } + + bool operator!=(const const_iterator &rhs) + { + return !operator==(rhs); + } + + const Value operator*() + { + return m_row.get(m_col); + } + + const Value operator->() + { + return m_row.get(m_col); + } + + private: + const Row &m_row; + int m_col; + }; + Row(const Result &result, int row); bool next(); @@ -22,6 +67,12 @@ namespace Pgsql { //Value get(const char *colname) const; //bool get(int col, QString &s); + const_iterator begin() const + { + return const_iterator(*this, 0); + } + + const_iterator end() const; private: const Result& m_result; diff --git a/pgsql/Pgsql_Value.h b/pgsql/Pgsql_Value.h index 8f2f9cf..2c28e2b 100644 --- a/pgsql/Pgsql_Value.h +++ b/pgsql/Pgsql_Value.h @@ -16,9 +16,12 @@ namespace Pgsql { */ class Value { public: + const char *empty_str = ""; Value(const char *val, Oid typ); QString asQString() const; - const char* c_str() const { return m_val; } + + bool null() const { return m_val == nullptr; } + const char* c_str() const { return m_val == nullptr ? empty_str : m_val; } operator QString() const; operator QDateTime() const; @@ -42,25 +45,31 @@ namespace Pgsql { template void getAsArray(I insert_iter, NullHandling nullhandling = NullHandling::Throw) const { - using value_type = E; - ArrayParser parser(m_val); - for (;;) { - auto res = parser.GetNextElem(); - if (res.ok) { - if (res.value) { - std::string str(res.value->data(), res.value->length()); - Value val(str.c_str(), OidFor::elem()); - value_type v; - v << val; - insert_iter = v; - } - else { - if (nullhandling == NullHandling::Throw) - throw std::runtime_error("Unexpected NULL value in array"); + if (m_val == nullptr) { + if (nullhandling == NullHandling::Throw) + throw std::runtime_error("Unexpected NULL value in array"); + } + else { + using value_type = E; + ArrayParser parser(m_val); + for (;;) { + auto res = parser.GetNextElem(); + if (res.ok) { + if (res.value) { + std::string str(res.value->data(), res.value->length()); + Value val(str.c_str(), OidFor::elem()); + value_type v; + v << val; + insert_iter = v; + } + else { + if (nullhandling == NullHandling::Throw) + throw std::runtime_error("Unexpected NULL value in array"); + } } + else + break; } - else - break; } }