diff --git a/.gitignore b/.gitignore index e293239..f6b7c07 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ build/* *.kdev4 .kdev4/* +DIST/ diff --git a/pglab/ColumnTableModel.cpp b/pglab/ColumnTableModel.cpp new file mode 100644 index 0000000..3030083 --- /dev/null +++ b/pglab/ColumnTableModel.cpp @@ -0,0 +1,149 @@ +#include "ColumnTableModel.h" +#include "PgDatabaseCatalog.h" +#include "PgAttribute.h" +#include "PgAttributeContainer.h" +#include "PgType.h" +#include "PgTypeContainer.h" +#include "ScopeGuard.h" +#include + +void ColumnTableModel::setData(std::shared_ptr cat, Oid table_oid) +{ + beginResetModel(); + SCOPE_EXIT { endResetModel(); }; + + m_catalog = cat; + m_columns = cat->attributes()->getColumnsForRelation(table_oid); + + // hide system columns + m_columns.erase(std::remove_if(m_columns.begin(), m_columns.end(), + [](auto &e) { return e.num <= 0; } ), + m_columns.end()); + + // sort remaining columns by order in table + std::sort(m_columns.begin(), m_columns.end(), + [] (auto &l, auto &r) -> bool { return l.num < r.num; }); +} + +QVariant ColumnTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + QVariant v; + if (orientation == Qt::Horizontal) { + if (role == Qt::DisplayRole) { + QString c; + switch (section) { + case NameCol: + c = tr("Name"); + break; + case TypeCol: + c = tr("Type"); + break; + case NullCol: + c = tr("Nullable"); + break; + case DefaultCol: + c = tr("Default"); + break; + case CollationCol: + c = tr("Collation"); + break; + } + v = c; + } + } + + return v; +} + +int ColumnTableModel::rowCount(const QModelIndex &parent) const +{ + return m_columns.size(); +} + +int ColumnTableModel::columnCount(const QModelIndex &parent) const +{ + return colCount; +} + +Oid ColumnTableModel::getType(int column) const +{ + Oid oid = Pgsql::VARCHAROID; +// switch (column) { +// case TypeCol: +// case NameCol: +// case NullCol: +// case DefaultCol: +// case CollationCol: +// oid = VARCHAROID; +// break; + +// c = tr("Collation"); +// break; +// } + return oid; +} + +QVariant ColumnTableModel::getData(const QModelIndex &index) const +{ + QVariant v; + const auto &t = m_columns[index.row()]; + QString s; + switch (index.column()) { + case NameCol: + s = t.name; + break; + case TypeCol: + s = getTypeDisplayString(*m_catalog, t.typid); + break; + case NullCol: + s = t.notnull ? "NOT NULL" : ""; + break; + case DefaultCol: + s = ""; + break; + case CollationCol: + s = ""; //t.collation; + break; + } + v = s; + + return v; +} + +QVariant ColumnTableModel::data(const QModelIndex &index, int role) const +{ + if (role == Qt::ForegroundRole && index.column() == TypeCol) { + QVariant v; + const auto &t = m_columns[index.row()]; + auto c = m_catalog->types()->getByKey(t.typid); + switch (c.category) { + case TypCategory::Boolean: + v = QBrush(Qt::darkGreen); + break; + case TypCategory::Numeric: + v = QBrush(Qt::darkBlue); + break; + case TypCategory::DateTime: + case TypCategory::Timespan: + v = QBrush(Qt::darkMagenta); + break; + case TypCategory::String: + v = QBrush(Qt::darkYellow); + break; + case TypCategory::Array: + case TypCategory::Composite: + case TypCategory::Enum: + case TypCategory::Geometric: + case TypCategory::NetworkAddress: + case TypCategory::Pseudo: + case TypCategory::Range: + case TypCategory::UserDefined: + case TypCategory::BitString: + case TypCategory::Unknown: + default: + v = QBrush(Qt::black); + } + return v; + } + return BaseTableModel::data(index, role); +} diff --git a/pglab/ColumnTableModel.h b/pglab/ColumnTableModel.h new file mode 100644 index 0000000..aff86ce --- /dev/null +++ b/pglab/ColumnTableModel.h @@ -0,0 +1,44 @@ +#ifndef COLUMNTABLEMODEL_H +#define COLUMNTABLEMODEL_H + +#include "BaseTableModel.h" +#include + +class PgDatabaseCatalog; +class PgAttribute; + +class ColumnTableModel: public BaseTableModel { +public: + enum e_Columns : int { + NameCol, /// + TypeCol, + NullCol, + DefaultCol, + CollationCol, + + colCount }; + + + using BaseTableModel::BaseTableModel; + void setData(std::shared_ptr cat, Oid table_oid); + + // Header: + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + + // Basic functionality: + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + + QVariant data(const QModelIndex &index, int role) const override; +protected: + virtual Oid getType(int column) const override; + virtual QVariant getData(const QModelIndex &index) const override; + + using t_Columns = std::vector; + + std::shared_ptr m_catalog; + t_Columns m_columns; + +}; + +#endif // COLUMNTABLEMODEL_H diff --git a/pglab/MainWindow.cpp b/pglab/MainWindow.cpp index d11c66d..2b34610 100644 --- a/pglab/MainWindow.cpp +++ b/pglab/MainWindow.cpp @@ -40,12 +40,6 @@ QueryTab* MainWindow::newSqlPage() ui->tabWidget->addTab(qt, "Tab"); ui->tabWidget->setCurrentWidget(qt); qt->newdoc(); - - auto tt = new TablesPage(this); - tt->setCatalog(m_database->catalogue()); - ui->tabWidget->addTab(tt, "Tables"); - ui->tabWidget->setCurrentWidget(tt); - return qt; } @@ -68,6 +62,12 @@ void MainWindow::setConfig(const ConnectionConfig &config) QString title = "pglab - "; title += m_config.name().c_str(); setWindowTitle(title); + + auto tt = new TablesPage(this); + tt->setCatalog(m_database->catalogue()); + ui->tabWidget->addTab(tt, "Tables"); + ui->tabWidget->setCurrentWidget(tt); + newSqlPage(); } diff --git a/pglab/PgAttribute.cpp b/pglab/PgAttribute.cpp new file mode 100644 index 0000000..ff3549e --- /dev/null +++ b/pglab/PgAttribute.cpp @@ -0,0 +1,2 @@ +#include "PgAttribute.h" + diff --git a/pglab/PgAttribute.h b/pglab/PgAttribute.h new file mode 100644 index 0000000..31334c7 --- /dev/null +++ b/pglab/PgAttribute.h @@ -0,0 +1,35 @@ +#ifndef PGATTRIBUTE_H +#define PGATTRIBUTE_H + +#include "Pgsql_declare.h" +#include +#include +#include + +class PgAttribute { +public: + using Key = std::tuple; + + // Oid oid = InvalidOid; + Oid relid = InvalidOid; + QString name; + Oid typid = InvalidOid; + int32_t stattarget = 0; + int16_t num = 0; + int32_t ndims = 0; // array dimensions + int32_t typmod = -1; + bool notnull = false; + bool hasdef = false; + Oid collation = InvalidOid; + QString acl; + QString options; + + + bool operator==(Key _k) const { return relid == std::get<0>(_k) && num == std::get<1>(_k); } + bool operator==(const QString &n) const { return name == n; } + bool operator<(Key _k) const { return relid < std::get<0>(_k) || (relid == std::get<0>(_k) && num < std::get<1>(_k)); } + bool operator<(const PgAttribute &rhs) const { return relid < rhs.relid || (relid == rhs.relid && num < rhs.num); } + +}; + +#endif // PGATTRIBUTE_H diff --git a/pglab/PgAttributeContainer.cpp b/pglab/PgAttributeContainer.cpp new file mode 100644 index 0000000..531dfd1 --- /dev/null +++ b/pglab/PgAttributeContainer.cpp @@ -0,0 +1,30 @@ +#include "PgAttributeContainer.h" +#include "Pgsql_Col.h" + +std::string PgAttributeContainer::getLoadQuery() const +{ + return + "SELECT attrelid, attname, atttypid, attstattarget, \n" + " attnum, attndims, atttypmod, attnotnull, atthasdef, \n" + " attcollation, attacl, attoptions \n" + " FROM pg_catalog.pg_attribute"; +} + +PgAttribute PgAttributeContainer::loadElem(const Pgsql::Row &row) +{ + Pgsql::Col col(row); + PgAttribute v; + col >> v.relid >> v.name >> v.typid >> v.stattarget + >> v.num >> v.ndims >> v.typmod >> v.notnull >> v.hasdef + >> v.collation >> v.acl >> v.options; + return v; +} + +std::vector PgAttributeContainer::getColumnsForRelation(Oid oid) const +{ + std::vector result; + for (const auto &e : m_container) + if (e.relid == oid) + result.push_back(e); + return result; +} diff --git a/pglab/PgAttributeContainer.h b/pglab/PgAttributeContainer.h new file mode 100644 index 0000000..5f2ffc4 --- /dev/null +++ b/pglab/PgAttributeContainer.h @@ -0,0 +1,20 @@ +#ifndef PGATTRIBUTECONTAINER_H +#define PGATTRIBUTECONTAINER_H + +#include "PgContainer.h" +#include "PgAttribute.h" +#include "Pgsql_declare.h" +#include + +class PgAttributeContainer : public PgContainer { +public: + using PgContainer::PgContainer; + + virtual std::string getLoadQuery() const override; + + std::vector getColumnsForRelation(Oid oid) const; +protected: + PgAttribute loadElem(const Pgsql::Row &row) override; +}; + +#endif // PGATTRIBUTECONTAINER_H diff --git a/pglab/PgClassContainer.cpp b/pglab/PgClassContainer.cpp index ff9d24e..d75023c 100644 --- a/pglab/PgClassContainer.cpp +++ b/pglab/PgClassContainer.cpp @@ -12,22 +12,14 @@ std::string PgClassContainer::getLoadQuery() const "FROM pg_catalog.pg_class"; } -void PgClassContainer::load(const Pgsql::Result &res) +PgClass PgClassContainer::loadElem(const Pgsql::Row &row) { - const int n_rows = res.rows(); - m_container.clear(); - m_container.reserve(n_rows); - for (auto row : res) { - Pgsql::Col col(row); - PgClass v; - col >> v.oid >> v.name >> v.relnamespace >> v.type >> v.oftype - >> v.owner >> v.am >> v.filenode >> v.tablespace >> v.pages_est - >> v.tuples_est >> v.toastrelid >> v.isshared >> v.persistence - >> v.kind >> v.hasoids >> v.ispopulated >> v.frozenxid >> v.minmxid - >> v.acl >> v.options; - - m_container.push_back(v); - } - std::sort(m_container.begin(), m_container.end()); - + Pgsql::Col col(row); + PgClass v; + col >> v.oid >> v.name >> v.relnamespace >> v.type >> v.oftype + >> v.owner >> v.am >> v.filenode >> v.tablespace >> v.pages_est + >> v.tuples_est >> v.toastrelid >> v.isshared >> v.persistence + >> v.kind >> v.hasoids >> v.ispopulated >> v.frozenxid >> v.minmxid + >> v.acl >> v.options; + return v; } diff --git a/pglab/PgClassContainer.h b/pglab/PgClassContainer.h index 60b5f37..32ef85c 100644 --- a/pglab/PgClassContainer.h +++ b/pglab/PgClassContainer.h @@ -16,8 +16,8 @@ public: using PgContainer::PgContainer; virtual std::string getLoadQuery() const override; - virtual void load(const Pgsql::Result &res) override; - +protected: + PgClass loadElem(const Pgsql::Row &row) override; private: }; diff --git a/pglab/PgContainer.cpp b/pglab/PgContainer.cpp new file mode 100644 index 0000000..66c499d --- /dev/null +++ b/pglab/PgContainer.cpp @@ -0,0 +1,2 @@ +#include "PgContainer.h" + diff --git a/pglab/PgContainer.h b/pglab/PgContainer.h index 6acece7..5283d11 100644 --- a/pglab/PgContainer.h +++ b/pglab/PgContainer.h @@ -2,6 +2,7 @@ #define PGCONTAINER_H #include "Pgsql_declare.h" +#include "Pgsql_Result.h" #include #include #include @@ -18,7 +19,7 @@ public: virtual void load(const Pgsql::Result &res) = 0; }; -template +template class PgContainer: public IPgContainter { public: using t_Container = std::vector; ///< Do not assume it will stay a vector only expect bidirectional access @@ -48,10 +49,10 @@ public: return (int)m_container.size(); } - const T& getByOid(Oid oid) const + const T& getByKey(const K &key) const { - auto lb_result = std::lower_bound(m_container.begin(), m_container.end(), oid); - if (lb_result != m_container.end() && lb_result->oid == oid) + 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; @@ -71,9 +72,30 @@ public: { 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 loadElem(const Pgsql::Row &) { return T(); } private: T m_invalidInstance; diff --git a/pglab/PgDatabaseCatalog.cpp b/pglab/PgDatabaseCatalog.cpp index 5c179bf..fd352d0 100644 --- a/pglab/PgDatabaseCatalog.cpp +++ b/pglab/PgDatabaseCatalog.cpp @@ -1,8 +1,11 @@ #include "PgDatabaseCatalog.h" -#include "PgTypeContainer.h" -#include "PgDatabaseContainer.h" + +#include "PgAttributeContainer.h" #include "PgAuthIdContainer.h" #include "PgClassContainer.h" +#include "PgDatabaseContainer.h" +#include "PgNamespaceContainer.h" +#include "PgTypeContainer.h" #include "Pgsql_Connection.h" @@ -11,7 +14,7 @@ QString getRoleNameFromOid(const PgDatabaseCatalog &cat, Oid oid) QString name; auto auth_ids = cat.authIds(); if (auth_ids) { - const PgAuthId& auth_id = auth_ids->getByOid(oid); + const PgAuthId& auth_id = auth_ids->getByKey(oid); if (auth_id.valid()) { name = auth_id.name; } @@ -22,13 +25,15 @@ QString getRoleNameFromOid(const PgDatabaseCatalog &cat, Oid oid) QString getRoleDisplayString(const PgDatabaseCatalog &cat, Oid oid) { QString name = getRoleNameFromOid(cat, oid); - return QString("%1 (%2)").arg(name).arg(oid); + return name; +// return QString("%1 (%2)").arg(name).arg(oid); } QString getNamespaceDisplayString(const PgDatabaseCatalog &cat, Oid oid) { //QString name; -// auto c = cat.(); + auto nss = cat.namespaces(); + auto ns = nss->getByKey(oid); // if (auth_ids) { // const PgAuthId& auth_id = auth_ids->getByOid(oid); // if (auth_id.valid()) { @@ -37,7 +42,7 @@ QString getNamespaceDisplayString(const PgDatabaseCatalog &cat, Oid oid) // } //return name; // TODO load list and lookup name - return QString("ns %1").arg(oid); + return ns.name; //QString("ns %1").arg(oid); } @@ -47,6 +52,13 @@ QString getTablespaceDisplayString(const PgDatabaseCatalog &cat, Oid oid) return QString("ts %1").arg(oid); } +QString getTypeDisplayString(const PgDatabaseCatalog &cat, Oid oid) +{ + auto tc = cat.types(); + auto t = tc->getByKey(oid); + return t.name; +} + PgDatabaseCatalog::PgDatabaseCatalog() { @@ -59,10 +71,13 @@ PgDatabaseCatalog::~PgDatabaseCatalog() void PgDatabaseCatalog::loadAll(Pgsql::Connection &conn) { loadInfo(conn); - loadTypes(conn); - loadDatabases(conn); + + loadAttributes(conn); loadAuthIds(conn); loadClasses(conn); + loadDatabases(conn); + loadNamespaces(conn); + loadTypes(conn); } void PgDatabaseCatalog::loadInfo(Pgsql::Connection &conn) @@ -89,20 +104,12 @@ void load(Pgsql::Connection &conn, IPgContainter &pg_cont) } -void PgDatabaseCatalog::loadTypes(Pgsql::Connection &conn) +void PgDatabaseCatalog::loadAttributes(Pgsql::Connection &conn) { - if (!m_types) - m_types = std::make_shared(shared_from_this()); + if (!m_attributes) + m_attributes = std::make_shared(shared_from_this()); - load(conn, *m_types); -} - -void PgDatabaseCatalog::loadDatabases(Pgsql::Connection &conn) -{ - if (!m_databases) - m_databases = std::make_shared(shared_from_this()); - - load(conn, *m_databases); + load(conn, *m_attributes); } void PgDatabaseCatalog::loadAuthIds(Pgsql::Connection &conn) @@ -121,6 +128,31 @@ void PgDatabaseCatalog::loadClasses(Pgsql::Connection &conn) load(conn, *m_classes); } +void PgDatabaseCatalog::loadDatabases(Pgsql::Connection &conn) +{ + if (!m_databases) + m_databases = std::make_shared(shared_from_this()); + + load(conn, *m_databases); +} + +void PgDatabaseCatalog::loadNamespaces(Pgsql::Connection &conn) +{ + if (!m_namespaces) + m_namespaces = std::make_shared(shared_from_this()); + + load(conn, *m_namespaces); +} + +void PgDatabaseCatalog::loadTypes(Pgsql::Connection &conn) +{ + if (!m_types) + m_types = std::make_shared(shared_from_this()); + + load(conn, *m_types); +} + + const QString& PgDatabaseCatalog::serverVersionString() const { return m_serverVersionString; @@ -131,14 +163,9 @@ int PgDatabaseCatalog::serverVersion() const return m_serverVersion; } -std::shared_ptr PgDatabaseCatalog::types() const +std::shared_ptr PgDatabaseCatalog::attributes() const { - return m_types; -} - -std::shared_ptr PgDatabaseCatalog::databases() const -{ - return m_databases; + return m_attributes; } std::shared_ptr PgDatabaseCatalog::authIds() const @@ -150,3 +177,19 @@ std::shared_ptr PgDatabaseCatalog::classes() const { return m_classes; } + +std::shared_ptr PgDatabaseCatalog::databases() const +{ + return m_databases; +} + +std::shared_ptr PgDatabaseCatalog::namespaces() const +{ + return m_namespaces; +} + +std::shared_ptr PgDatabaseCatalog::types() const +{ + return m_types; +} + diff --git a/pglab/PgDatabaseCatalog.h b/pglab/PgDatabaseCatalog.h index cdf80b7..0bdd7c4 100644 --- a/pglab/PgDatabaseCatalog.h +++ b/pglab/PgDatabaseCatalog.h @@ -12,9 +12,11 @@ namespace Pgsql { } +class PgAttributeContainer; class PgAuthIdContainer; class PgClassContainer; class PgDatabaseContainer; +class PgNamespaceContainer; class PgTypeContainer; class PgDatabaseCatalog: public std::enable_shared_from_this { @@ -27,32 +29,41 @@ public: void loadAll(Pgsql::Connection &conn); - void loadInfo(Pgsql::Connection &conn); - void loadTypes(Pgsql::Connection &conn); - void loadDatabases(Pgsql::Connection &conn); + + void loadAttributes(Pgsql::Connection &conn); void loadAuthIds(Pgsql::Connection &conn); void loadClasses(Pgsql::Connection &conn); + void loadDatabases(Pgsql::Connection &conn); + void loadInfo(Pgsql::Connection &conn); + void loadNamespaces(Pgsql::Connection &conn); + void loadTypes(Pgsql::Connection &conn); const QString& serverVersionString() const; int serverVersion() const; - std::shared_ptr types() const; - std::shared_ptr databases() const; + std::shared_ptr attributes() const; std::shared_ptr authIds() const; std::shared_ptr classes() const; + std::shared_ptr databases() const; + std::shared_ptr namespaces() const; + std::shared_ptr types() const; private: QString m_serverVersionString; int m_serverVersion; - std::shared_ptr m_types; - std::shared_ptr m_databases; + + std::shared_ptr m_attributes; std::shared_ptr m_authIds; std::shared_ptr m_classes; + std::shared_ptr m_databases; + std::shared_ptr m_namespaces; + std::shared_ptr m_types; }; QString getRoleNameFromOid(const PgDatabaseCatalog &cat, Oid oid); QString getRoleDisplayString(const PgDatabaseCatalog &cat, Oid oid); QString getNamespaceDisplayString(const PgDatabaseCatalog &cat, Oid oid); QString getTablespaceDisplayString(const PgDatabaseCatalog &cat, Oid oid); +QString getTypeDisplayString(const PgDatabaseCatalog &cat, Oid oid); #endif // PGSQLDATABASECATALOGUE_H diff --git a/pglab/PgNamespace.cpp b/pglab/PgNamespace.cpp index c395bf5..64fbfc8 100644 --- a/pglab/PgNamespace.cpp +++ b/pglab/PgNamespace.cpp @@ -2,3 +2,8 @@ PgNamespace::PgNamespace() = default; +bool PgNamespace::isSystemCatalog() const +{ + return name == "pg_catalog" + || name == "information_schema"; +} diff --git a/pglab/PgNamespace.h b/pglab/PgNamespace.h index f406c20..c642778 100644 --- a/pglab/PgNamespace.h +++ b/pglab/PgNamespace.h @@ -4,14 +4,21 @@ #include #include -class PgNamespace -{ +class PgNamespace { public: PgNamespace(); + Oid oid = InvalidOid; QString name; Oid owner = InvalidOid; QString acl; + + bool operator==(Oid _oid) const { return oid == _oid; } + bool operator==(const QString &n) const { return name == n; } + bool operator<(Oid _oid) const { return oid < _oid; } + bool operator<(const PgNamespace &rhs) const { return oid < rhs.oid; } + + bool isSystemCatalog() const; }; #endif // PGNAMESPACE_H diff --git a/pglab/PgNamespaceContainer.cpp b/pglab/PgNamespaceContainer.cpp new file mode 100644 index 0000000..4c17e2e --- /dev/null +++ b/pglab/PgNamespaceContainer.cpp @@ -0,0 +1,23 @@ +#include "PgNamespaceContainer.h" +#include "Pgsql_Col.h" +#include "Pgsql_Result.h" +#include "Pgsql_Value.h" + +std::string PgNamespaceContainer::getLoadQuery() const +{ + return "SELECT oid, nspname, nspowner, nspacl FROM pg_catalog.pg_namespace"; +} + +void PgNamespaceContainer::load(const Pgsql::Result &res) +{ + const int n_rows = res.rows(); + m_container.clear(); + m_container.reserve(n_rows); + for (auto row : res) { + Pgsql::Col col(row); + PgNamespace v; + col >> v.oid >> v.name >> v.owner >> v.acl; + m_container.push_back(v); + } + std::sort(m_container.begin(), m_container.end()); +} diff --git a/pglab/PgNamespaceContainer.h b/pglab/PgNamespaceContainer.h new file mode 100644 index 0000000..6996a53 --- /dev/null +++ b/pglab/PgNamespaceContainer.h @@ -0,0 +1,24 @@ +#ifndef PGNAMESPACECONTAINER_H +#define PGNAMESPACECONTAINER_H + +#include "PgNamespace.h" +#include "PgContainer.h" + +namespace Pgsql { + + class Result; + +} + +class PgNamespaceContainer: public PgContainer { +public: + using PgContainer::PgContainer; + + virtual std::string getLoadQuery() const override; + virtual void load(const Pgsql::Result &res) override; + +private: +}; + + +#endif // PGNAMESPACECONTAINER_H diff --git a/pglab/PgType.cpp b/pglab/PgType.cpp index ba81860..8308ea1 100644 --- a/pglab/PgType.cpp +++ b/pglab/PgType.cpp @@ -1,5 +1,56 @@ #include "PgType.h" #include "Pgsql_Connection.h" +void operator<<(TypCategory &s, const Pgsql::Value &v) +{ + //s = static_cast(v); + const char *c = v.c_str(); + switch (*c) { + case 'A': + s = TypCategory::Array; + break; + case 'B': + s = TypCategory::Boolean; + break; + case 'D': + s = TypCategory::DateTime; + break; + case 'E': + s = TypCategory::Enum; + break; + case 'G': + s = TypCategory::Geometric; + break; + case 'I': + s = TypCategory::NetworkAddress; + break; + case 'N': + s = TypCategory::Numeric; + break; + case 'P': + s = TypCategory::Pseudo; + break; + case 'R': + s = TypCategory::Range; + break; + case 'S': + s = TypCategory::String; + break; + case 'T': + s = TypCategory::Timespan; + break; + case 'U': + s = TypCategory::UserDefined; + break; + case 'V': + s = TypCategory::BitString; + break; + case 'X': + s = TypCategory::Unknown; + break; + } +} + + PgType::PgType() = default; diff --git a/pglab/PgType.h b/pglab/PgType.h index b39b42d..b46aaa2 100644 --- a/pglab/PgType.h +++ b/pglab/PgType.h @@ -3,19 +3,41 @@ #include #include +#include "Pgsql_Value.h" + +enum class TypCategory { + Array, + Boolean, + Composite, + DateTime, + Enum, + Geometric, + NetworkAddress, + Numeric, + Pseudo, + Range, + String, + Timespan, + UserDefined, + BitString, + Unknown +}; + +void operator<<(TypCategory &s, const Pgsql::Value &v); + class PgType { public: PgType(); Oid oid = InvalidOid; - QString typname;//"name";"NO" + QString name;//"name";"NO" Oid typnamespace = InvalidOid;//"oid";"NO" - Oid typowner = InvalidOid;//"oid";"NO" - short typlen = -1;//"smallint";"NO" - bool typbyval = false;//"boolean";"NO" - QString typtype;//""char"";"NO" - QString typcategory;//""char"";"NO" + Oid owner = InvalidOid;//"oid";"NO" + short len = -1;//"smallint";"NO" + bool byval = false;//"boolean";"NO" + QString type;//""char"";"NO" + TypCategory category;//""char"";"NO" bool typispreferred = false;//"boolean";"NO" bool typisdefined = false;//"boolean";"NO" QString typdelim;//""char"";"NO" @@ -41,7 +63,7 @@ public: QString typacl;//"ARRAY";"YES" bool operator==(Oid _oid) const { return oid == _oid; } - bool operator==(const QString &n) const { return typname == n; } + bool operator==(const QString &n) const { return name == n; } bool operator<(Oid _oid) const { return oid < _oid; } bool operator<(const PgType &rhs) const { return oid < rhs.oid; } }; diff --git a/pglab/PgTypeContainer.cpp b/pglab/PgTypeContainer.cpp index b02c0e1..ed617c1 100644 --- a/pglab/PgTypeContainer.cpp +++ b/pglab/PgTypeContainer.cpp @@ -44,13 +44,13 @@ void PgTypeContainer::load(const Pgsql::Result &res) for (auto row : res) { PgType v; v.oid << row.get(0); // InvalidOid; - v.typname << row.get(1); //. operator QString(); // "name";"NO" + v.name << row.get(1); //. operator QString(); // "name";"NO" v.typnamespace << row.get(2); // InvalidOid;//"oid";"NO" - v.typowner << row.get(3); // InvalidOid;//"oid";"NO" - v.typlen << row.get(4); // -1;//"smallint";"NO" - v.typbyval << row.get(5); // false;//"boolean";"NO" - v.typtype << row.get(6);//""char"";"NO" - v.typcategory << row.get(7);//""char"";"NO" + v.owner << row.get(3); // InvalidOid;//"oid";"NO" + v.len << row.get(4); // -1;//"smallint";"NO" + v.byval << row.get(5); // false;//"boolean";"NO" + v.type << row.get(6);//""char"";"NO" + v.category << row.get(7);//""char"";"NO" v.typispreferred << row.get(8); //false;//"boolean";"NO" v.typisdefined << row.get(9); //false;//"boolean";"NO" v.typdelim << row.get(10); //""char"";"NO" diff --git a/pglab/PgTypeContainer.h b/pglab/PgTypeContainer.h index 145883f..5b5668e 100644 --- a/pglab/PgTypeContainer.h +++ b/pglab/PgTypeContainer.h @@ -3,7 +3,6 @@ #include "PgType.h" #include "PgContainer.h" -#include namespace Pgsql { diff --git a/pglab/ResultTableModelUtil.cpp b/pglab/ResultTableModelUtil.cpp index e50e285..2d3563a 100644 --- a/pglab/ResultTableModelUtil.cpp +++ b/pglab/ResultTableModelUtil.cpp @@ -1,4 +1,6 @@ #include "ResultTableModelUtil.h" +#include +#include using namespace Pgsql; @@ -55,3 +57,36 @@ QString FormatBoolForDisplay(bool v) return v ? "TRUE" : "FALSE"; } + +// +// +// +// Source Sans Pro +// 10 +// +// +// +// QAbstractItemView::NoEditTriggers +// +// +// QAbstractItemView::ScrollPerPixel +// +// +// QAbstractItemView::ScrollPerPixel +// +// +// false +// +// +// 20 +// +// +// 16 +// +// +void SetTableViewDefault(QTableView *tv) +{ + tv->setAlternatingRowColors(true); + tv->verticalHeader()->setMinimumSectionSize(16); + tv->verticalHeader()->setDefaultSectionSize(20); +} diff --git a/pglab/ResultTableModelUtil.h b/pglab/ResultTableModelUtil.h index 98543a6..df32aaf 100644 --- a/pglab/ResultTableModelUtil.h +++ b/pglab/ResultTableModelUtil.h @@ -21,3 +21,5 @@ inline QColor GetDefaultNumericColor() { return Qt::darkGreen; } QString FormatBoolForDisplay(bool v); +class QTableView; +void SetTableViewDefault(QTableView *tv); diff --git a/pglab/SqlSyntaxHighlighter.cpp b/pglab/SqlSyntaxHighlighter.cpp index 5eb5610..6e3af2f 100644 --- a/pglab/SqlSyntaxHighlighter.cpp +++ b/pglab/SqlSyntaxHighlighter.cpp @@ -130,7 +130,7 @@ void SqlSyntaxHighlighter::setTypes(const PgTypeContainer& types) { m_typeNames.clear(); for (auto &e : types) { - m_typeNames.insert(e.typname); + m_typeNames.insert(e.name); } } diff --git a/pglab/TablesPage.cpp b/pglab/TablesPage.cpp index c32bfa2..b796971 100644 --- a/pglab/TablesPage.cpp +++ b/pglab/TablesPage.cpp @@ -1,7 +1,10 @@ #include "TablesPage.h" #include "ui_TablesPage.h" +#include "PgAttribute.h" #include "TablesTableModel.h" +#include "ResultTableModelUtil.h" +#include "ColumnTableModel.h" TablesPage::TablesPage(QWidget *parent) : QWidget(parent), @@ -9,9 +12,22 @@ TablesPage::TablesPage(QWidget *parent) : { ui->setupUi(this); + SetTableViewDefault(ui->tableListTable); m_tablesModel = new TablesTableModel(this); ui->tableListTable->setModel(m_tablesModel); + SetTableViewDefault(ui->columnsTable); + m_columnsModel = new ColumnTableModel(this); + ui->columnsTable->setModel(m_columnsModel); + + connect(ui->tableListTable->selectionModel(), &QItemSelectionModel::currentRowChanged, this, + &TablesPage::on_tableListTable_currentRowChanged); + +} + +TablesPage::~TablesPage() +{ + delete ui; } void TablesPage::setCatalog(std::shared_ptr cat) @@ -20,7 +36,10 @@ void TablesPage::setCatalog(std::shared_ptr cat) m_tablesModel->setCatalog(cat); } -TablesPage::~TablesPage() +void TablesPage::on_tableListTable_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous) { - delete ui; + if (current.row() != previous.row()) { + Oid table_oid = m_tablesModel->getTableOid(current.row()); + m_columnsModel->setData(m_catalog, table_oid); + } } diff --git a/pglab/TablesPage.h b/pglab/TablesPage.h index 7678cbd..0cee048 100644 --- a/pglab/TablesPage.h +++ b/pglab/TablesPage.h @@ -9,6 +9,7 @@ class TablesPage; } class TablesTableModel; +class ColumnTableModel; class PgDatabaseCatalog; class TablesPage : public QWidget @@ -24,6 +25,11 @@ private: Ui::TablesPage *ui; std::shared_ptr m_catalog; TablesTableModel* m_tablesModel = nullptr; + ColumnTableModel* m_columnsModel = nullptr; + +private slots: + + void on_tableListTable_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous); }; #endif // TABLESPAGE_H diff --git a/pglab/TablesTableModel.cpp b/pglab/TablesTableModel.cpp index c524f9d..03d52fe 100644 --- a/pglab/TablesTableModel.cpp +++ b/pglab/TablesTableModel.cpp @@ -2,7 +2,10 @@ #include "PgDatabaseCatalog.h" #include "PgClass.h" #include "PgClassContainer.h" +#include "PgNamespace.h" +#include "PgNamespaceContainer.h" #include "Pgsql_declare.h" +#include TablesTableModel::TablesTableModel(QObject *parent) : BaseTableModel(parent) @@ -44,6 +47,9 @@ QVariant TablesTableModel::headerData(int section, Qt::Orientation orientation, case NameCol: v = tr("Name"); break; + case NamespaceCol: + v = tr("Schema"); + break; case OwnerCol: v = tr("Owner"); break; @@ -80,6 +86,7 @@ Oid TablesTableModel::getType(int column) const case TablespaceCol: case OwnerCol: case NameCol: + case NamespaceCol: case OptionsCol: case AclCol: default: @@ -94,7 +101,10 @@ QVariant TablesTableModel::getData(const QModelIndex &index) const const auto &t = m_tables[index.row()]; switch (index.column()) { case NameCol: - v = formatTableName(t); + v = t.name; //formatTableName(t); + break; + case NamespaceCol: + v = getNamespaceDisplayString(*m_catalog, t.relnamespace); break; case OwnerCol: v = getRoleDisplayString(*m_catalog, t.owner); @@ -113,9 +123,33 @@ QVariant TablesTableModel::getData(const QModelIndex &index) const return v; } +Oid TablesTableModel::getTableOid(int row) const +{ + return m_tables[row].oid; +} + QString TablesTableModel::formatTableName(const PgClass &cls) const { const char * format = "%2 (%1)"; QString ns_name = getNamespaceDisplayString(*m_catalog, cls.relnamespace); return QString(format).arg(ns_name).arg(cls.name); } + +QVariant TablesTableModel::data(const QModelIndex &index, int role) const +{ + + if (role == Qt::ForegroundRole) { + + const auto &t = m_tables[index.row()]; + auto ns = m_catalog->namespaces()->getByKey(t.relnamespace); + if (ns.isSystemCatalog()) { + switch (index.column()) { + case NameCol: + case NamespaceCol: + return QBrush(Qt::blue); + break; + } + } + } + return BaseTableModel::data(index, role); +} diff --git a/pglab/TablesTableModel.h b/pglab/TablesTableModel.h index 336ed68..2ac42c0 100644 --- a/pglab/TablesTableModel.h +++ b/pglab/TablesTableModel.h @@ -13,6 +13,7 @@ class TablesTableModel: public BaseTableModel public: enum e_Columns : int { NameCol, ///< either table, ns.table or table (ns) depending on settings/filters + NamespaceCol, OwnerCol, TablespaceCol, OptionsCol, @@ -30,6 +31,8 @@ public: int rowCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex &parent) const override; + virtual QVariant data(const QModelIndex &index, int role) const override; + Oid getTableOid(int row) const; protected: virtual Oid getType(int column) const override; virtual QVariant getData(const QModelIndex &index) const override; diff --git a/pglab/TypeSelectionItemModel.cpp b/pglab/TypeSelectionItemModel.cpp index 1e00289..997b591 100644 --- a/pglab/TypeSelectionItemModel.cpp +++ b/pglab/TypeSelectionItemModel.cpp @@ -61,8 +61,8 @@ void TypeSelectionItemModel::setTypeList(std::shared_ptr beginResetModel(); m_types.clear(); for (const auto &e : *types) { - if (e.typcategory != "A" && e.typtype != "c") { - m_types.push_back(e.typname); + if (e.category != TypCategory::Array && e.type != "c") { + m_types.push_back(e.name); } } std::sort(m_types.begin(), m_types.end()); diff --git a/pglab/pglab.pro b/pglab/pglab.pro index 4765b73..39842bd 100644 --- a/pglab/pglab.pro +++ b/pglab/pglab.pro @@ -71,7 +71,12 @@ SOURCES += main.cpp\ TablesPage.cpp \ PgClassContainer.cpp \ TablesTableModel.cpp \ - PgDatabaseCatalog.cpp + PgDatabaseCatalog.cpp \ + PgNamespaceContainer.cpp \ + ColumnTableModel.cpp \ + PgAttribute.cpp \ + PgContainer.cpp \ + PgAttributeContainer.cpp HEADERS += \ QueryResultModel.h \ @@ -117,7 +122,11 @@ HEADERS += \ TablesPage.h \ PgClassContainer.h \ TablesTableModel.h \ - PgDatabaseCatalog.h + PgDatabaseCatalog.h \ + PgNamespaceContainer.h \ + ColumnTableModel.h \ + PgAttribute.h \ + PgAttributeContainer.h FORMS += mainwindow.ui \ DatabaseWindow.ui \ @@ -136,12 +145,27 @@ RESOURCES += \ QMAKE_LFLAGS_WINDOWS = /SUBSYSTEM:WINDOWS,5.01 win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../core/release/ -lcore -else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../core/debug/ -L$$OUT_PWD/../pgsql/debug/ -lcore -lpgsql +else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../core/debug/ -lcore -INCLUDEPATH += $$PWD/../core $$PWD/../pgsql -DEPENDPATH += $$PWD/../core $$PWD/../pgsql +INCLUDEPATH += $$PWD/../core +DEPENDPATH += $$PWD/../core win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/release/libcore.a else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/debug/libcore.a else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/release/core.lib -else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/debug/core.lib $$OUT_PWD/../pgsql/debug/pgsql.lib +else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/debug/core.lib + + +win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../pgsql/release/ -lpgsql +else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../pgsql/debug/ -lpgsql +else:unix:!macx: LIBS += -L$$OUT_PWD/../pgsql/ -lpgsql + +INCLUDEPATH += $$PWD/../pgsql +DEPENDPATH += $$PWD/../pgsql + +win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../pgsql/release/libpgsql.a +else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../pgsql/debug/libpgsql.a +else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../pgsql/release/pgsql.lib +else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../pgsql/debug/pgsql.lib +else:unix:!macx: PRE_TARGETDEPS += $$OUT_PWD/../pgsql/libpgsql.a + diff --git a/pgsql/Pgsql_Col.h b/pgsql/Pgsql_Col.h index 87e2ad7..5003d79 100644 --- a/pgsql/Pgsql_Col.h +++ b/pgsql/Pgsql_Col.h @@ -7,7 +7,7 @@ namespace Pgsql { class Col { public: - explicit Col(Pgsql::Row &r) + explicit Col(const Pgsql::Row &r) : row(r) {} @@ -17,16 +17,10 @@ namespace Pgsql { return row.get(++col); } private: - Pgsql::Row &row; + const Pgsql::Row &row; int col = -1; }; -// template -// void operator<<(T &s, Col &c) -// { -// s << c.nextValue(); -// } - template Col& operator>>(Col &c, T &s) {