New column page

Shows SQL for columns ALTER TABLE ... [ADD|DROP] COLUMN combines a selection
of multiple columns into a single alter table.
Show collation in list of columns.

(order of columns isn't what is should be but that should maybe be fixed
by a generic column selection and ordering mechanism that knows what the
default sort should be)
This commit is contained in:
eelke 2018-11-29 20:21:36 +01:00
parent 73c4cf4790
commit 57217974f4
19 changed files with 345 additions and 55 deletions

83
pglab/ColumnPage.cpp Normal file
View file

@ -0,0 +1,83 @@
#include "ColumnPage.h"
#include "ResultTableModelUtil.h"
#include "UserConfiguration.h"
#include <QTableView>
#include "PgClass.h"
#include "SqlCodePreview.h"
#include "ColumnTableModel.h"
#include "CustomFilterSortModel.h"
#include "CustomDataRole.h"
#include "PgLabItemDelegate.h"
#include <QStringBuilder>
#include <boost/container/flat_set.hpp>
#include "SqlFormattingUtils.h"
ColumnPage::ColumnPage(QWidget *parent)
: QSplitter(Qt::Vertical, parent)
{
m_tableView = new QTableView(this);
m_definitionView = new SqlCodePreview(this);
addWidget(m_tableView);
addWidget(m_definitionView);
SetTableViewDefault(m_tableView);
m_columnModel = new ColumnTableModel(this);
m_sortFilterProxy = new CustomFilterSortModel(this);
m_sortFilterProxy->setSourceModel(m_columnModel);
m_tableView->setModel(m_sortFilterProxy);
m_tableView->setSortingEnabled(true);
m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
// With this delegate the coloring of the datatype column doesn't work....
// auto item_delegate = new PgLabItemDelegate(this);
// m_tableView->setItemDelegate(item_delegate);
//auto icon_delegate = new IconColumnDelegate(this);
connect(m_tableView->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &ColumnPage::tableView_selectionChanged);
}
void ColumnPage::setData(std::shared_ptr<const PgDatabaseCatalog> cat, const std::optional<PgClass> &cls)
{
m_catalog = cat;
m_definitionView->setCatalog(cat);
m_columnModel->setData(cat, cls);
m_Class = cls;
}
//void ColumnPage::setFilter(const std::optional<PgClass> &cls)
//{
// m_sortFilterProxy->setOidFilterTable(cls ? cls->oid() : InvalidOid, FirstHiddenValue);
//}
void ColumnPage::tableView_selectionChanged(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/)
{
auto&& indexes = m_tableView->selectionModel()->selectedIndexes();
boost::container::flat_set<int> rijen;
for (const auto &e : indexes)
rijen.insert(m_sortFilterProxy->mapToSource(e).row());
QString drops;
QString addsql;
auto iter = rijen.begin();
if (iter != rijen.end()) {
auto && col = m_columnModel->column(*iter);
drops = "ALTER TABLE " % m_Class->fullyQualifiedQuotedObjectName() % "\n DROP COLUMN " % quoteIdent(col.name);
addsql = "ALTER TABLE " % m_Class->fullyQualifiedQuotedObjectName() % "\n ADD COLUMN " % col.columnDefinition(*m_catalog);
for (++iter; iter != rijen.end(); ++iter) {
auto && col = m_columnModel->column(*iter);
drops += ",\n DROP COLUMN " % quoteIdent(col.name);
addsql += ",\n ADD COLUMN " % col.columnDefinition(*m_catalog);
}
drops += ";";
addsql += ";";
m_definitionView->setPlainText(drops % "\n\n" % addsql);
}
else {
m_definitionView->setPlainText("");
}
}

42
pglab/ColumnPage.h Normal file
View file

@ -0,0 +1,42 @@
#ifndef COLUMNPAGE_H
#define COLUMNPAGE_H
#include "PgClass.h"
#include <QSplitter>
#include <memory>
#include <optional>
class QTableView;
class SqlCodePreview;
class PgDatabaseCatalog;
class ColumnTableModel;
class CustomFilterSortModel;
class QItemSelection;
class QAbstractItemModel;
class ColumnPage : public QSplitter
{
Q_OBJECT
public:
explicit ColumnPage(QWidget *parent = nullptr);
void setData(std::shared_ptr<const PgDatabaseCatalog> cat, const std::optional<PgClass> &cls);
//void setFilter(const std::optional<PgClass> &cls);
signals:
public slots:
private:
QTableView *m_tableView = nullptr;
SqlCodePreview *m_definitionView = nullptr;
ColumnTableModel *m_columnModel = nullptr;
CustomFilterSortModel *m_sortFilterProxy = nullptr;
std::shared_ptr<const PgDatabaseCatalog> m_catalog;
std::optional<PgClass> m_Class;
private slots:
void tableView_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
};
#endif // COLUMNPAGE_H

View file

@ -4,6 +4,8 @@
#include "PgAttributeContainer.h"
#include "PgClassContainer.h"
#include "PgConstraintContainer.h"
#include "PgCollation.h"
#include "PgCollationContainer.h"
#include "PgType.h"
#include "PgTypeContainer.h"
#include "PgIndexContainer.h"
@ -197,31 +199,34 @@ QVariant ColumnTableModel::getData(const QModelIndex &index) const
}
}
else {
QString s;
switch (col) {
case AttnumCol:
s = QString::asprintf("%d", (int) t.num);
break;
//s = QString::asprintf("%d", (int) t.num);
//break;
return static_cast<int>(t.num);
case NameCol:
s = t.name;
v = t.name;
break;
case TypeCol:
s = getTypeDisplayString(*m_catalog, t.typid, t.typmod);
v = getTypeDisplayString(*m_catalog, t.typid, t.typmod);
break;
case NullCol:
s = QString::fromStdU16String(t.notnull ? u"" : u"N");
v = QString::fromStdU16String(t.notnull ? u"" : u"N");
break;
case DefaultCol:
s = t.defaultValue;
v = t.defaultValue;
break;
case ForeignKeyCol:
s = getFKey(t);
v = getFKey(t);
break;
case CollationCol:
s = ""; //t.collation;
if (t.collation != InvalidOid) {
auto&& col = m_catalog->collations()->getByKey(t.collation);
if (col)
v = col->objectName();
}
break;
}
v = s;
}
return v;
@ -290,3 +295,8 @@ QVariant ColumnTableModel::data(const QModelIndex &index, int role) const
}
return BaseTableModel::data(index, role);
}
const PgAttribute& ColumnTableModel::column(int row) const
{
return m_columns.at(static_cast<size_t>(row));
}

View file

@ -2,6 +2,7 @@
#define COLUMNTABLEMODEL_H
#include "BaseTableModel.h"
#include "PgAttribute.h"
#include "PgClass.h"
#include "PgIndex.h"
#include <memory>
@ -36,6 +37,7 @@ public:
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
const PgAttribute& column(int row) const;
protected:
virtual Oid getType(int column) const override;
virtual QVariant getData(const QModelIndex &index) const override;

View file

@ -3,6 +3,7 @@
#include "PgAttribute.h"
#include "PgDatabaseCatalog.h"
#include "ColumnPage.h"
#include "ColumnTableModel.h"
#include "ConstraintModel.h"
#include "IconColumnDelegate.h"
@ -34,11 +35,6 @@ TablesPage::TablesPage(MainWindow *parent)
ui->tableListTable->sortByColumn(0, Qt::AscendingOrder);
ui->tableListTable->setSelectionBehavior(QAbstractItemView::SelectRows);
// Columns
SetTableViewDefault(ui->columnsTable);
m_columnsModel = new ColumnTableModel(this);
ui->columnsTable->setModel(m_columnsModel);
// Constraints
SetTableViewDefault(ui->constraintsTable);
m_constraintModel = new ConstraintModel(this);
@ -82,16 +78,21 @@ TablesPage::TablesPage(MainWindow *parent)
&TablesPage::indexesTable_modelReset);
// Non designer based code
// - Columns page
m_columnsPage = new ColumnPage(this);
ui->twDetails->insertTab(0, m_columnsPage, "");
// - Properties page
m_propertiesPage = new PropertiesPage(this);
m_propertiesPage->setSourceModel(m_tablesModel);
m_propertiesTab = addDetailTab(m_propertiesPage);
ui->twDetails->addTab(m_propertiesPage, "");
connect(ui->tableListTable->selectionModel(), &QItemSelectionModel::currentRowChanged,
m_propertiesPage, &PropertiesPage::setActiveRow);
// - Trigger page
m_triggerPage = new TriggerPage(this);
m_triggerTab = addDetailTab(m_triggerPage);
ui->twDetails->addTab(m_triggerPage, "");
retranslateUi(false);
}
@ -102,18 +103,9 @@ void TablesPage::retranslateUi(bool all)
if (all)
ui->retranslateUi(this);
ui->twDetails->setTabText(ui->twDetails->indexOf(m_propertiesTab), QApplication::translate("TablesPage", "Properties", nullptr));
ui->twDetails->setTabText(ui->twDetails->indexOf(m_triggerTab), QApplication::translate("TablesPage", "Triggers", nullptr));
}
QWidget* TablesPage::addDetailTab(QWidget *contents)
{
auto tab = new QWidget();
auto verticalLayout = new QVBoxLayout(tab);
verticalLayout->addWidget(contents);
ui->twDetails->addTab(tab, "");
return tab;
ui->twDetails->setTabText(ui->twDetails->indexOf(m_columnsPage), QApplication::translate("TablesPage", "Columns", nullptr));
ui->twDetails->setTabText(ui->twDetails->indexOf(m_propertiesPage), QApplication::translate("TablesPage", "Properties", nullptr));
ui->twDetails->setTabText(ui->twDetails->indexOf(m_triggerPage), QApplication::translate("TablesPage", "Triggers", nullptr));
}
TablesPage::~TablesPage()
@ -163,8 +155,8 @@ void TablesPage::tableListTable_layoutChanged(const QList<QPersistentModelIndex>
void TablesPage::selectedTableChanged(const std::optional<PgClass> &table)
{
m_columnsModel->setData(m_catalog, table);
ui->columnsTable->resizeColumnsToContents();
m_columnsPage->setData(m_catalog, table);
//ui->columnsTable->resizeColumnsToContents();
m_constraintModel->setData(m_catalog, table);
ui->constraintsTable->resizeColumnsToContents();

View file

@ -11,6 +11,7 @@ class TablesPage;
}
class TablesTableModel;
class ColumnPage;
class ColumnTableModel;
class ConstraintModel;
class PgDatabaseCatalog;
@ -33,9 +34,11 @@ public:
private:
Ui::TablesPage *ui;
MainWindow *m_window;
QWidget *m_propertiesTab;
// QWidget *m_columnsTab;
ColumnPage *m_columnsPage;
// QWidget *m_propertiesTab;
PropertiesPage *m_propertiesPage;
QWidget *m_triggerTab;
// QWidget *m_triggerTab;
TriggerPage *m_triggerPage;
std::shared_ptr<PgDatabaseCatalog> m_catalog;
TablesTableModel* m_tablesModel = nullptr;
@ -45,7 +48,7 @@ private:
//NamespaceFilterWidget* m_namespaceFilterWidget;
void retranslateUi(bool all = true);
QWidget* addDetailTab(QWidget *contents);
// QWidget* addDetailTab(QWidget *contents, bool infront = false);
void selectedTableChanged(const std::optional<PgClass> &table);
private slots:

View file

@ -38,18 +38,8 @@
</widget>
<widget class="QTabWidget" name="twDetails">
<property name="currentIndex">
<number>2</number>
<number>0</number>
</property>
<widget class="QWidget" name="columnsTab">
<attribute name="title">
<string>Columns</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTableView" name="columnsTable"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabConstraints">
<attribute name="title">
<string>Constraints</string>

View file

@ -79,7 +79,8 @@ PropertyProxyModel.cpp \
PropertiesPage.cpp \
PasswordPromptDialog.cpp \
ProcTableModel.cpp \
FunctionsPage.cpp
FunctionsPage.cpp \
ColumnPage.cpp
HEADERS += \
QueryResultModel.h \
@ -136,7 +137,8 @@ CustomDataRole.h \
PropertiesPage.h \
PasswordPromptDialog.h \
ProcTableModel.h \
FunctionsPage.h
FunctionsPage.h \
ColumnPage.h
FORMS += mainwindow.ui \
ConnectionManagerWindow.ui \

View file

@ -1,2 +1,56 @@
#include "PgAttribute.h"
#include "QStringBuilder"
#include "SqlFormattingUtils.h"
#include "PgClass.h"
#include "PgDatabaseCatalog.h"
#include "PgTypeContainer.h"
#include "PgCollation.h"
#include "PgCollationContainer.h"
QString PgAttribute::columnDefinition(const PgDatabaseCatalog &cat) const
{
// create: column_name data_type [ COLLATE collation ] [ column_constraint [ ... ]
// alter: column_name data_type [ COLLATE collation ] [ column_constraint [ ... ]
// constraints NULL/NOT NULL, DEFAULT, GENERATED other constraints will be ignored here a
auto&& type = cat.types()->getByKey(typid);
QString sql = quoteIdent(name) % " " % type->name;
if (collation != InvalidOid) {
auto&& col = cat.collations()->getByKey(collation);
QString oname = col->objectName();
if (oname != "default")
sql += " COLLATE " % quoteIdent(oname);
}
if (notnull)
sql += " NOT NULL";
if (hasdef)
sql += " DEFAULT " % defaultValue;
if (identity != ' ') {
sql += " GENERATED ";
if (identity == 'a') sql += "ALWAYS";
else if (identity == 'd') sql += "BY DEFAULT";
sql += " AS IDENTITY";
}
// TODO sequence options might be missing
return sql;
}
QString PgAttribute::alterTableAddColumn(const PgDatabaseCatalog &cat, const PgClass &table) const
{
QString sql = "ALTER TABLE " % table.fullyQualifiedQuotedObjectName()
% " ADD COLUMN " % columnDefinition(cat) % ";";
return sql;
}
QString PgAttribute::alterTableDropColumn(const PgDatabaseCatalog &cat, const PgClass &table) const
{
QString sql = "ALTER TABLE " % table.fullyQualifiedQuotedObjectName()
% " DROP COLUMN " % quoteIdent(name) % ";";
return sql;
}

View file

@ -6,11 +6,13 @@
#include <libpq-fe.h>
#include <tuple>
class PgClass;
class PgDatabaseCatalog;
class PgAttribute {
public:
using Key = std::tuple<Oid, int16_t>;
// Oid oid = InvalidOid;
Oid relid = InvalidOid;
QString name;
Oid typid = InvalidOid;
@ -20,6 +22,7 @@ public:
int32_t typmod = -1;
bool notnull = false;
bool hasdef = false;
char identity = ' ';
bool isdropped = false;
Oid collation = InvalidOid;
QString acl;
@ -33,6 +36,10 @@ public:
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); }
/// Return the part of the SQL create statement that can be reused for both the CREATE TABLE and ALTER TABLE ADD COLUMN
QString columnDefinition(const PgDatabaseCatalog &cat) const;
QString alterTableAddColumn(const PgDatabaseCatalog &cat, const PgClass &table) const;
QString alterTableDropColumn(const PgDatabaseCatalog &cat, const PgClass &table) const;
};
#endif // PGATTRIBUTE_H

View file

@ -1,5 +1,6 @@
#include "PgAttributeContainer.h"
#include "Pgsql_Col.h"
#include "PgDatabaseCatalog.h"
//SELECT attname, pg_get_expr(adbin, adrelid) AS def_value
//FROM pg_attribute
@ -8,12 +9,16 @@
std::string PgAttributeContainer::getLoadQuery() const
{
return R"__(
std::string q = R"__(
SELECT attrelid, attname, atttypid, attstattarget,
attnum, attndims, atttypmod, attnotnull, atthasdef, attisdropped,
attcollation, attacl, attoptions, pg_get_expr(adbin, adrelid) AS def_value
FROM pg_catalog.pg_attribute
LEFT JOIN pg_attrdef ON attrelid=adrelid AND attnum=adnum)__";
attcollation, attacl, attoptions, pg_get_expr(adbin, adrelid) AS def_value)__";
if (m_catalog.serverVersion() >= 100000)
q += ", attidentity";
q +=
"\n FROM pg_catalog.pg_attribute \n"
" LEFT JOIN pg_attrdef ON attrelid=adrelid AND attnum=adnum";
return q;
}
PgAttribute PgAttributeContainer::loadElem(const Pgsql::Row &row)
@ -23,6 +28,9 @@ PgAttribute PgAttributeContainer::loadElem(const Pgsql::Row &row)
col >> v.relid >> v.name >> v.typid >> v.stattarget
>> v.num >> v.ndims >> v.typmod >> v.notnull >> v.hasdef >> v.isdropped
>> v.collation >> v.acl >> v.options >> v.defaultValue;
if (m_catalog.serverVersion() >= 100000)
col >> v.identity;
return v;
}

2
pglablib/PgCollation.cpp Normal file
View file

@ -0,0 +1,2 @@
#include "PgCollation.h"

25
pglablib/PgCollation.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef PGCOLLATION_H
#define PGCOLLATION_H
#include "PgNamespaceObject.h"
#include "PgOwnedObject.h"
#include <QString>
#include <libpq-fe.h>
#include "Pgsql_Value.h"
//#include <vector>
class PgCollation: public PgNamespaceObject, public PgOwnedObject {
public:
using PgNamespaceObject::PgNamespaceObject;
// Oid oid; // oid
// QString collname; // name
// Oid collnamespace; // oid
// Oid collowner; // oid
int32_t collencoding; // integer
QString collcollate; // name
QString collctype; // name
};
#endif // PGCOLLATION_H

View file

@ -0,0 +1,28 @@
#include "PgCollationContainer.h"
#include "Pgsql_Connection.h"
#include "Pgsql_Col.h"
#include "PgDatabaseCatalog.h"
#include <iterator>
std::string PgCollationContainer::getLoadQuery() const
{
return "SELECT oid, collname, collnamespace, collowner, collencoding, \n"
" collcollate, collctype \n"
"FROM pg_collation";
}
PgCollation PgCollationContainer::loadElem(const Pgsql::Row &row)
{
Pgsql::Col col(row);
Oid class_oid = col.nextValue();
QString name = col.nextValue();
Oid ns_oid = col.nextValue();
PgCollation v(m_catalog, class_oid, name, ns_oid);
Oid owner ;
col >> owner >> v.collencoding >> v.collcollate >> v.collctype;
v.setOwnerOid(m_catalog, owner);
return v;
}

View file

@ -0,0 +1,25 @@
#ifndef PGCOLLATIONCONTAINER_H
#define PGCOLLATIONCONTAINER_H
#include "PgContainer.h"
#include "PgCollation.h"
namespace Pgsql {
class Result;
}
class PgCollationContainer: public PgContainer<PgCollation> {
public:
using PgContainer<PgCollation>::PgContainer;
virtual std::string getLoadQuery() const override;
protected:
PgCollation loadElem(const Pgsql::Row &row) override;
private:
};
#endif // PGCOLLATIONCONTAINER_H

View file

@ -13,6 +13,7 @@
#include "PgTriggerContainer.h"
#include "PgTypeContainer.h"
#include "PgProcContainer.h"
#include "PgCollationContainer.h"
#include "Pgsql_Connection.h"
#include "Pgsql_oids.h"
@ -160,6 +161,9 @@ void PgDatabaseCatalog::loadAll(Pgsql::Connection &conn,
// Load database objects
load2(m_namespaces, conn);
if (progress_callback && !progress_callback(++n, count))
return;
load2(m_collations, conn);
if (progress_callback && !progress_callback(++n, count))
return;
load2(m_classes, conn); // needs namespaces
@ -294,3 +298,8 @@ std::shared_ptr<const PgProcContainer> PgDatabaseCatalog::procs() const
{
return m_procs;
}
std::shared_ptr<const PgCollationContainer> PgDatabaseCatalog::collations() const
{
return m_collations;
}

View file

@ -27,6 +27,8 @@ class PgTablespaceContainer;
class PgTriggerContainer;
class PgTypeContainer;
class PgProcContainer;
class PgCollationContainer;
class PgDatabaseCatalog: public QObject, public std::enable_shared_from_this<PgDatabaseCatalog> {
Q_OBJECT
@ -57,6 +59,7 @@ public:
std::shared_ptr<const PgTriggerContainer> triggers() const;
std::shared_ptr<const PgTypeContainer> types() const;
std::shared_ptr<const PgProcContainer> procs() const;
std::shared_ptr<const PgCollationContainer> collations() const;
enum RefreshFlag {
Attributes = 1,
@ -94,6 +97,7 @@ private:
std::shared_ptr<PgTriggerContainer> m_triggers;
std::shared_ptr<PgTypeContainer> m_types;
std::shared_ptr<PgProcContainer> m_procs;
std::shared_ptr<PgCollationContainer> m_collations;
template <typename T>
void load2(std::shared_ptr<T> &ptr, Pgsql::Connection &conn)

View file

@ -195,7 +195,7 @@ bool identNeedsQuotes(QString ident)
if (ident[0].isDigit())
return true;
for (auto c : ident)
if ((c < 'a' || c > 'z') && c != '_')
if ((c < 'a' || c > 'z') && c != '_' && (c < '0' || c > '9'))
return true;
auto kw = getPgsqlKeyword(ident);

View file

@ -71,7 +71,9 @@ codebuilder/StructureTemplate.cpp \
PgDatabaseObject.cpp \
PgServerObject.cpp \
PgOwnedObject.cpp \
PgNamespaceObject.cpp
PgNamespaceObject.cpp \
PgCollation.cpp \
PgCollationContainer.cpp
HEADERS += \
Pglablib.h \
@ -125,7 +127,9 @@ codebuilder/StructureTemplate.h \
PgDatabaseObject.h \
PgServerObject.h \
PgOwnedObject.h \
PgNamespaceObject.h
PgNamespaceObject.h \
PgCollation.h \
PgCollationContainer.h
unix {
target.path = /usr/lib