From 14ab400ccb9510dec257b6ec07eddf9dc1434b17 Mon Sep 17 00:00:00 2001 From: eelke Date: Mon, 8 Jan 2018 20:45:52 +0100 Subject: [PATCH] Bezig met crudmodel/tab --- pglab/CrudModel.cpp | 31 +++++++++++ pglab/CrudModel.h | 67 +++++++++++++++++++++++ pglab/CrudTab.cpp | 14 +++++ pglab/CrudTab.h | 22 ++++++++ pglab/CrudTab.ui | 24 +++++++++ pglab/OpenDatabase.h | 1 + pglab/TablesTableModel.cpp | 11 ++-- pglab/pglab.pro | 11 ++-- pglablib/PgClass.h | 4 ++ pglablib/PgClassContainer.cpp | 7 +++ pglablib/PgConstraintContainer.cpp | 12 +++++ pglablib/PgConstraintContainer.h | 2 + pglablib/PgContainer.h | 85 ++++++++++++++++++++++++++++++ pglablib/PgDatabaseCatalog.cpp | 45 ++++++++-------- pglablib/PgNamespace.cpp | 5 +- 15 files changed, 308 insertions(+), 33 deletions(-) create mode 100644 pglab/CrudModel.cpp create mode 100644 pglab/CrudModel.h create mode 100644 pglab/CrudTab.cpp create mode 100644 pglab/CrudTab.h create mode 100644 pglab/CrudTab.ui diff --git a/pglab/CrudModel.cpp b/pglab/CrudModel.cpp new file mode 100644 index 0000000..9942ad8 --- /dev/null +++ b/pglab/CrudModel.cpp @@ -0,0 +1,31 @@ +#include "CrudModel.h" +#include "OpenDatabase.h" +#include "PgDatabaseCatalog.h" +#include "PgAttributeContainer.h" +#include "PgConstraintContainer.h" + +#include + +CrudModel::CrudModel() + : m_dbConn(*getGlobalAsioIoService()) +{} + +CrudModel::~CrudModel() +{ + m_dbConn.closeConnection(); +} + + +/* + * Strategy + * when ordered by primary key, offset and limit work very quickly so we can get away with not loading + * everything. + */ +void CrudModel::setData(std::shared_ptr db, const PgClass &table) +{ + m_database = db; + m_table = table; + m_primaryKey = db->catalogue()->constraints()->getPrimaryForRelation(table.oid); + //cat->attributes()->getColumnsForRelation() + m_dbConn.setupConnection(m_database.config()); +} diff --git a/pglab/CrudModel.h b/pglab/CrudModel.h new file mode 100644 index 0000000..df786f9 --- /dev/null +++ b/pglab/CrudModel.h @@ -0,0 +1,67 @@ +#ifndef CRUDMODEL_H +#define CRUDMODEL_H + +#include "BaseTableModel.h" +#include "ASyncDBConnection.h" +#include "PgClass.h" +#include "Pgsql_Connection.h" +#include +#include +#include + +class PgConstraint; +class OpenDatase; + +/** + * @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 + * + * How to load data? + * - 2D array of QVariants won't be efficient + * - 2D array of strings + * - We do know the type of a single column is fixed + * - Keep data in Result and only load data in replacement rows + * std::string has short string optimization so this probably means + * that a two dimensional array of std::string objects could actually be quite efficient! + */ +class CrudModel: public BaseTableModel { + Q_OBJECT +public: + CrudModel(); + ~CrudModel(); + + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + void setData(std::shared_ptr db, const PgClass &table); + + + // Basic functionality: + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + +protected: + virtual Oid getType(int column) const override; + virtual QVariant getData(const QModelIndex &index) const override; + +private: + std::shared_ptr m_database; + PgClass m_table; + boost::optional m_primaryKey; + ASyncDBConnection m_dbConn; + + + 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; + +}; + +#endif // CRUDMODEL_H diff --git a/pglab/CrudTab.cpp b/pglab/CrudTab.cpp new file mode 100644 index 0000000..abc61b2 --- /dev/null +++ b/pglab/CrudTab.cpp @@ -0,0 +1,14 @@ +#include "CrudTab.h" +#include "ui_CrudTab.h" + +CrudTab::CrudTab(QWidget *parent) : + QWidget(parent), + ui(new Ui::CrudTab) +{ + ui->setupUi(this); +} + +CrudTab::~CrudTab() +{ + delete ui; +} diff --git a/pglab/CrudTab.h b/pglab/CrudTab.h new file mode 100644 index 0000000..95fd199 --- /dev/null +++ b/pglab/CrudTab.h @@ -0,0 +1,22 @@ +#ifndef CRUDTAB_H +#define CRUDTAB_H + +#include + +namespace Ui { +class CrudTab; +} + +class CrudTab : public QWidget +{ + Q_OBJECT + +public: + explicit CrudTab(QWidget *parent = 0); + ~CrudTab(); + +private: + Ui::CrudTab *ui; +}; + +#endif // CRUDTAB_H diff --git a/pglab/CrudTab.ui b/pglab/CrudTab.ui new file mode 100644 index 0000000..a828845 --- /dev/null +++ b/pglab/CrudTab.ui @@ -0,0 +1,24 @@ + + + CrudTab + + + + 0 + 0 + 718 + 581 + + + + Form + + + + + + + + + + diff --git a/pglab/OpenDatabase.h b/pglab/OpenDatabase.h index d9c88e9..7e21889 100644 --- a/pglab/OpenDatabase.h +++ b/pglab/OpenDatabase.h @@ -26,6 +26,7 @@ public: std::shared_ptr catalogue(); TypeSelectionItemModel* typeSelectionModel(); + const ConnectionConfig& config() const { return m_config; } private: ConnectionConfig m_config; std::shared_ptr m_catalogue; diff --git a/pglab/TablesTableModel.cpp b/pglab/TablesTableModel.cpp index 7675a85..f4001bb 100644 --- a/pglab/TablesTableModel.cpp +++ b/pglab/TablesTableModel.cpp @@ -24,12 +24,12 @@ void TablesTableModel::setCatalog(std::shared_ptr cat) // How many? int n = 0; for (const auto &e : *classes) - if (e.kind == RelKind::Table) ++n; + if (e.kind == RelKind::Table && !e.system_namespace) ++n; m_tables.clear(); m_tables.reserve(n); // reserve space for (const auto &e : *classes) { - if (e.kind == RelKind::Table) { + if (e.kind == RelKind::Table && !e.system_namespace) { m_tables.push_back(e); } } @@ -49,8 +49,11 @@ void TablesTableModel::doSort(int so) { if (so == 1) std::sort(m_tables.begin(), m_tables.end(), - [] (auto l, auto r) -> bool { return l.relnamespace < r.relnamespace - || (l.relnamespace == r.relnamespace && l.name < r.name); }); + [] (auto l, auto r) -> bool { return l.relnamespace_name < r.relnamespace_name + || (l.relnamespace_name == r.relnamespace_name && l.name < r.name); }); + else + std::sort(m_tables.begin(), m_tables.end(), + [] (auto l, auto r) -> bool { return l.name < r.name; }); } diff --git a/pglab/pglab.pro b/pglab/pglab.pro index e1d3213..511edfb 100644 --- a/pglab/pglab.pro +++ b/pglab/pglab.pro @@ -66,7 +66,9 @@ SOURCES += main.cpp\ ApplicationWindow.cpp \ ConstraintModel.cpp \ IconColumnDelegate.cpp \ - IndexModel.cpp + IndexModel.cpp \ + CrudTab.cpp \ + CrudModel.cpp HEADERS += \ QueryResultModel.h \ @@ -104,7 +106,9 @@ HEADERS += \ ApplicationWindow.h \ ConstraintModel.h \ IconColumnDelegate.h \ - IndexModel.h + IndexModel.h \ + CrudTab.h \ + CrudModel.h FORMS += mainwindow.ui \ ConnectionManagerWindow.ui \ @@ -116,7 +120,8 @@ FORMS += mainwindow.ui \ ProcessStdioWidget.ui \ TablesPage.ui \ NamespaceFilterWidget.ui \ - ApplicationWindow.ui + ApplicationWindow.ui \ + CrudTab.ui RESOURCES += \ resources.qrc diff --git a/pglablib/PgClass.h b/pglablib/PgClass.h index 3dc4077..82f86b0 100644 --- a/pglablib/PgClass.h +++ b/pglablib/PgClass.h @@ -26,12 +26,15 @@ enum class RelKind { void operator<<(RelKind &s, const Pgsql::Value &v); + class PgClass { public: Oid oid = InvalidOid; QString name; Oid relnamespace = InvalidOid; + QString relnamespace_name; // Transient, cached value from relnamespace + bool system_namespace = false; // Transient, cached value from relnamespace Oid type = InvalidOid; Oid oftype = InvalidOid; Oid owner = InvalidOid; @@ -56,6 +59,7 @@ public: bool operator<(Oid _oid) const { return oid < _oid; } bool operator<(const PgClass &rhs) const { return oid < rhs.oid; } +private: }; #endif // PGCLASS_H diff --git a/pglablib/PgClassContainer.cpp b/pglablib/PgClassContainer.cpp index 85c86a2..2accb33 100644 --- a/pglablib/PgClassContainer.cpp +++ b/pglablib/PgClassContainer.cpp @@ -1,6 +1,8 @@ #include "PgClassContainer.h" #include "Pgsql_Connection.h" #include "Pgsql_Col.h" +#include "PgDatabaseCatalog.h" +#include "PgNamespaceContainer.h" std::string PgClassContainer::getLoadQuery() const { @@ -21,5 +23,10 @@ PgClass PgClassContainer::loadElem(const Pgsql::Row &row) >> v.tuples_est >> v.toastrelid >> v.isshared >> v.persistence >> v.kind >> v.hasoids >> v.ispopulated >> v.frozenxid >> v.minmxid >> v.acl >> v.options; + + auto cat = m_catalogue.lock(); + auto ns = cat->namespaces()->getByKey(v.relnamespace); + v.relnamespace_name = ns.name; + v.system_namespace = ns.isSystemCatalog(); return v; } diff --git a/pglablib/PgConstraintContainer.cpp b/pglablib/PgConstraintContainer.cpp index 33983e5..77efd1f 100644 --- a/pglablib/PgConstraintContainer.cpp +++ b/pglablib/PgConstraintContainer.cpp @@ -72,3 +72,15 @@ std::vector PgConstraintContainer::getConstraintsForRelation(Oid r result.push_back(e); return result; } + +boost::optional PgConstraintContainer::getPrimaryForRelation(Oid relid) const +{ + boost::optional result; + for (const auto &e : m_container) { + if (e.relid == relid && e.type == ConstraintType::PrimaryKey) { + result = e; + break; + } + } + return result; +} diff --git a/pglablib/PgConstraintContainer.h b/pglablib/PgConstraintContainer.h index fdab087..5910942 100644 --- a/pglablib/PgConstraintContainer.h +++ b/pglablib/PgConstraintContainer.h @@ -5,6 +5,7 @@ #include "PgConstraint.h" #include "Pgsql_declare.h" #include +#include class PgConstraintContainer : public PgContainer { public: @@ -15,6 +16,7 @@ public: const PgConstraint* getFKeyForTableColumn(Oid relid, int16_t attnum) const; std::vector getConstraintsForRelation(Oid relid) const; + boost::optional getPrimaryForRelation(Oid relid) const; protected: virtual PgConstraint loadElem(const Pgsql::Row &row) override; }; diff --git a/pglablib/PgContainer.h b/pglablib/PgContainer.h index 5283d11..b234296 100644 --- a/pglablib/PgContainer.h +++ b/pglablib/PgContainer.h @@ -101,4 +101,89 @@ private: }; + +template +class PgSPtrContainer: public IPgContainter { +public: + using t_Elem = std::shared_ptr; + using t_Container = std::vector; ///< Do not assume it will stay a vector only expect bidirectional access + + explicit PgSPtrContainer(std::weak_ptr cat) + : m_catalogue(cat) + {} + + + typename t_Container::const_iterator begin() const + { + return m_container.begin(); + } + + typename t_Container::const_iterator end() const + { + return m_container.end(); + } + + void clear() + { + m_container.clear(); + } + + int count() const + { + return (int)m_container.size(); + } + + const t_Elem getByKey(const K &key) const + { + auto lb_result = std::lower_bound(m_container.begin(), m_container.end(), key); + if (lb_result != m_container.end() && **lb_result == key) + return *lb_result; + + return m_invalidInstance; + } + + const t_Elem getByName(const QString name) const + { + auto find_res = std::find_if(m_container.begin(), m_container.end(), + [name](auto e) -> bool { return *e = name; } ); + + if (find_res != m_container.end()) + return *find_res; + + return m_invalidInstance; + } + + const t_Elem getByIdx(int idx) const + { + return m_container.at(idx); + } + + /** Override to implement complete loading logic. + * + * Do not override this function if you only want to implement + * the loading of a single element. Override loadElem instead. + */ + virtual void load(const Pgsql::Result &res) override + { + m_container.clear(); + m_container.reserve(res.rows()); + for (auto row : res) + m_container.push_back(loadElem(row)); + + std::sort(m_container.begin(), m_container.end()); + } +protected: + std::weak_ptr m_catalogue; + t_Container m_container; + + /** Override the implementation for this function to implement loading of single row. + * + * When overriding this function there is no need to override load. + */ + virtual t_Elem loadElem(const Pgsql::Row &) = 0; +private: + +}; + + #endif // PGCONTAINER_H diff --git a/pglablib/PgDatabaseCatalog.cpp b/pglablib/PgDatabaseCatalog.cpp index 3eb01f4..ab012f5 100644 --- a/pglablib/PgDatabaseCatalog.cpp +++ b/pglablib/PgDatabaseCatalog.cpp @@ -121,31 +121,32 @@ void PgDatabaseCatalog::loadAll(Pgsql::Connection &conn, std::function progress_callback) { loadInfo(conn); - if (progress_callback && !progress_callback(1, 9)) - return; - load2(m_attributes, conn); - if (progress_callback && !progress_callback(2, 9)) - return; - load2(m_authIds, conn); - if (progress_callback && !progress_callback(3, 9)) - return; - load2(m_classes, conn); - if (progress_callback && !progress_callback(4, 9)) - return; - load2(m_constraints, conn); - if (progress_callback && !progress_callback(5, 9)) - return; - load2(m_databases, conn); - if (progress_callback && !progress_callback(6, 9)) - return; - load2(m_indexes, conn); - if (progress_callback && !progress_callback(7, 9)) + int n = 0; + if (progress_callback && !progress_callback(++n, 9)) return; load2(m_namespaces, conn); - if (progress_callback && !progress_callback(8, 9)) + if (progress_callback && !progress_callback(++n, 9)) + return; + load2(m_classes, conn); // needs namespaces + if (progress_callback && !progress_callback(++n, 9)) + return; + load2(m_attributes, conn); + if (progress_callback && !progress_callback(++n, 9)) + return; + load2(m_authIds, conn); + if (progress_callback && !progress_callback(++n, 9)) + return; + load2(m_constraints, conn); + if (progress_callback && !progress_callback(++n, 9)) + return; + load2(m_databases, conn); + if (progress_callback && !progress_callback(++n, 9)) + return; + load2(m_indexes, conn); + if (progress_callback && !progress_callback(++n, 9)) return; load2(m_types, conn); - progress_callback && progress_callback(9, 9); + progress_callback && progress_callback(++n, 9); } void PgDatabaseCatalog::loadInfo(Pgsql::Connection &conn) @@ -167,7 +168,7 @@ void load(Pgsql::Connection &conn, IPgContainter &pg_cont) std::string q = pg_cont.getLoadQuery(); Pgsql::Result result = conn.query(q.c_str()); if (result && result.resultStatus() == PGRES_TUPLES_OK) { - boost::timer::auto_cpu_timer t; + //boost::timer::auto_cpu_timer t; pg_cont.load(result); } else { diff --git a/pglablib/PgNamespace.cpp b/pglablib/PgNamespace.cpp index abe2859..90b4017 100644 --- a/pglablib/PgNamespace.cpp +++ b/pglablib/PgNamespace.cpp @@ -4,9 +4,6 @@ PgNamespace::PgNamespace() = default; bool PgNamespace::isSystemCatalog() const { - return name == "pg_catalog" - || name == "pg_toast" - || name == "pg_temp_1" - || name == "pg_toast_temp_1" + return name.startsWith("pg_") || name == "information_schema"; }