Overview of triggers extended with function name and arguments.

Did a lot of refactoring on the catalog to keep things clean.
This commit is contained in:
eelke 2018-11-18 19:30:45 +01:00
parent 35813ae926
commit fcb191f2cc
44 changed files with 797 additions and 404 deletions

View file

@ -225,7 +225,7 @@ QVariant ColumnTableModel::data(const QModelIndex &index, int role) const
QVariant v; QVariant v;
const auto &t = m_columns[index.row()]; const auto &t = m_columns[index.row()];
auto c = m_catalog->types()->getByKey(t.typid); auto c = m_catalog->types()->getByKey(t.typid);
switch (c.category) { switch (c->category) {
case TypCategory::Boolean: case TypCategory::Boolean:
v = QBrush(Qt::darkGreen); v = QBrush(Qt::darkGreen);
break; break;

View file

@ -1,4 +1,4 @@
#include "CreateDatabaseDialog.h" #include "CreateDatabaseDialog.h"
#include "ui_CreateDatabaseDialog.h" #include "ui_CreateDatabaseDialog.h"
CreateDatabaseDialog::CreateDatabaseDialog(QWidget *parent) : CreateDatabaseDialog::CreateDatabaseDialog(QWidget *parent) :
@ -12,3 +12,14 @@ CreateDatabaseDialog::~CreateDatabaseDialog()
{ {
delete ui; delete ui;
} }
//CREATE DATABASE name
// [ [ WITH ] [ OWNER [=] user_name ]
// [ TEMPLATE [=] template ]
// [ ENCODING [=] encoding ]
// [ LC_COLLATE [=] lc_collate ]
// [ LC_CTYPE [=] lc_ctype ]
// [ TABLESPACE [=] tablespace_name ]
// [ ALLOW_CONNECTIONS [=] allowconn ]
// [ CONNECTION LIMIT [=] connlimit ]
// [ IS_TEMPLATE [=] istemplate ] ]

View file

@ -15,102 +15,117 @@
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<layout class="QFormLayout" name="formLayout"> <widget class="QTabWidget" name="">
<item row="0" column="0"> <property name="currentIndex">
<widget class="QLabel" name="label"> <number>0</number>
<property name="text"> </property>
<string>Name</string> <widget class="QWidget" name="Input" native="true">
</property> <attribute name="title">
</widget> <string>Input</string>
</item> </attribute>
<item row="0" column="1"> <layout class="QFormLayout" name="formLayout">
<widget class="QLineEdit" name="lineEdit"/> <item row="0" column="0">
</item> <widget class="QLabel" name="label">
<item row="1" column="0"> <property name="text">
<widget class="QLabel" name="label_2"> <string>Name</string>
<property name="text"> </property>
<string>Template</string> </widget>
</property> </item>
</widget> <item row="0" column="1">
</item> <widget class="QLineEdit" name="lineEdit"/>
<item row="1" column="1"> </item>
<widget class="QComboBox" name="comboBox"/> <item row="2" column="0">
</item> <widget class="QLabel" name="label_2">
<item row="2" column="0"> <property name="text">
<widget class="QLabel" name="label_3"> <string>Template</string>
<property name="text"> </property>
<string>Encoding</string> </widget>
</property> </item>
</widget> <item row="2" column="1">
</item> <widget class="QComboBox" name="comboBox"/>
<item row="2" column="1"> </item>
<widget class="QComboBox" name="comboBox_2"/> <item row="3" column="0">
</item> <widget class="QLabel" name="label_3">
<item row="3" column="0"> <property name="text">
<widget class="QLabel" name="label_4"> <string>Encoding</string>
<property name="text"> </property>
<string>Collation order</string> </widget>
</property> </item>
</widget> <item row="3" column="1">
</item> <widget class="QComboBox" name="comboBox_2"/>
<item row="3" column="1"> </item>
<widget class="QComboBox" name="comboBox_3"/> <item row="4" column="0">
</item> <widget class="QLabel" name="label_4">
<item row="4" column="0"> <property name="text">
<widget class="QLabel" name="label_5"> <string>Collation order</string>
<property name="text"> </property>
<string>Character classification</string> </widget>
</property> </item>
</widget> <item row="4" column="1">
</item> <widget class="QComboBox" name="comboBox_3"/>
<item row="4" column="1"> </item>
<widget class="QComboBox" name="comboBox_4"/> <item row="5" column="0">
</item> <widget class="QLabel" name="label_5">
<item row="5" column="0"> <property name="text">
<widget class="QLabel" name="label_6"> <string>Character classification</string>
<property name="text"> </property>
<string>Connection limit</string> </widget>
</property> </item>
</widget> <item row="5" column="1">
</item> <widget class="QComboBox" name="comboBox_4"/>
<item row="5" column="1"> </item>
<widget class="QSpinBox" name="spinBox"/> <item row="6" column="0">
</item> <widget class="QLabel" name="label_6">
<item row="6" column="0"> <property name="text">
<widget class="QLabel" name="label_8"> <string>Connection limit</string>
<property name="text"> </property>
<string>Tablespace</string> </widget>
</property> </item>
</widget> <item row="6" column="1">
</item> <widget class="QSpinBox" name="spinBox"/>
<item row="6" column="1"> </item>
<widget class="QComboBox" name="comboBox_5"/> <item row="7" column="0">
</item> <widget class="QLabel" name="label_8">
<item row="7" column="0"> <property name="text">
<widget class="QLabel" name="label_7"> <string>Tablespace</string>
<property name="text"> </property>
<string>Owner</string> </widget>
</property> </item>
</widget> <item row="7" column="1">
</item> <widget class="QComboBox" name="comboBox_5"/>
<item row="7" column="1"> </item>
<widget class="QComboBox" name="comboBox_6"/> <item row="1" column="1">
</item> <widget class="QComboBox" name="comboBox_6"/>
<item row="8" column="0"> </item>
<widget class="QCheckBox" name="checkBox"> <item row="1" column="0">
<property name="text"> <widget class="QLabel" name="label_7">
<string>Template database</string> <property name="text">
</property> <string>Owner</string>
</widget> </property>
</item> </widget>
<item row="9" column="0"> </item>
<widget class="QCheckBox" name="checkBox_2"> <item row="8" column="1">
<property name="text"> <widget class="QCheckBox" name="checkBox">
<string>Allow connections</string> <property name="text">
</property> <string>Template database</string>
</widget> </property>
</item> </widget>
</layout> </item>
<item row="9" column="1">
<widget class="QCheckBox" name="checkBox_2">
<property name="text">
<string>Allow connections</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>SQL</string>
</attribute>
</widget>
</widget>
</item> </item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">

View file

@ -4,6 +4,7 @@
#include "MainWindow.h" #include "MainWindow.h"
#include "ResultTableModelUtil.h" #include "ResultTableModelUtil.h"
#include "PgLabItemDelegate.h" #include "PgLabItemDelegate.h"
#include <QMenu>
CrudTab::CrudTab(MainWindow *parent) CrudTab::CrudTab(MainWindow *parent)
@ -23,6 +24,12 @@ CrudTab::CrudTab(MainWindow *parent)
ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection); ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);
ui->tableView->addAction(ui->actionRemove_rows); ui->tableView->addAction(ui->actionRemove_rows);
auto horizontal_header = ui->tableView->horizontalHeader();
horizontal_header->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
connect(horizontal_header, &QHeaderView::customContextMenuRequested,
this, &CrudTab::headerCustomContextMenu);
//auto selection_model = ui->tableView->selectionModel(); //auto selection_model = ui->tableView->selectionModel();
// connect(ui->tableView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, // connect(ui->tableView->selectionModel(), &QItemSelectionModel::currentRowChanged, this,
// &CrudTab::tableView_currentRowChanged); // &CrudTab::tableView_currentRowChanged);
@ -79,3 +86,16 @@ std::vector<QAction*> CrudTab::getToolbarActions()
} }
return actions; return actions;
} }
void CrudTab::headerCustomContextMenu(const QPoint &pos)
{
auto menu = new QMenu(this);
QAction *action = new QAction(QIcon(":/icons/script_go.png"), tr("Refresh"), this);
action->setShortcut(QKeySequence(Qt::Key_F5));
connect(action, &QAction::triggered, this, &CrudTab::refresh);
menu->addAction(action);
auto horizontal_header = ui->tableView->horizontalHeader();
menu->popup(horizontal_header->mapToGlobal(pos));
}

View file

@ -41,6 +41,7 @@ private:
private slots: private slots:
// void tableView_currentRowChanged(const QModelIndex &current, const QModelIndex &previous); // void tableView_currentRowChanged(const QModelIndex &current, const QModelIndex &previous);
void on_actionRemove_rows_triggered(); void on_actionRemove_rows_triggered();
void headerCustomContextMenu(const QPoint &pos);
}; };
#endif // CRUDTAB_H #endif // CRUDTAB_H

View file

@ -24,7 +24,7 @@ Pgsql::Params QueryParamListController::params() const
Pgsql::Params params; Pgsql::Params params;
auto types = m_openDatabase->catalogue()->types(); auto types = m_openDatabase->catalogue()->types();
for (auto e : m_paramList.GetParams()) { for (auto e : m_paramList.GetParams()) {
Oid oid = types->getByName(e.type).oid; Oid oid = types->getByName(e.type)->oid;
params.add(e.value, oid); params.add(e.value, oid);
} }
return params; return params;

View file

@ -62,8 +62,8 @@ void TriggerPage::tableView_selectionChanged(const QItemSelection &/*selected*/,
QString creates; QString creates;
for (auto rij : rijen) { for (auto rij : rijen) {
auto&& t = m_model->trigger(rij); auto&& t = m_model->trigger(rij);
drops += t.dropSql(*m_catalog) % "\n"; drops += t.dropSql() % "\n";
creates += t.createSql(*m_catalog) % "\n"; creates += t.createSql() % "\n";
} }
m_definitionView->setPlainText(drops % "\n" % creates); m_definitionView->setPlainText(drops % "\n" % creates);
} }

View file

@ -20,7 +20,7 @@ QVariant TriggerTableModel::headerData(int section, Qt::Orientation orientation,
case InitiallyCol: return tr("ID"); case InitiallyCol: return tr("ID");
case ForCol: return tr("For"); case ForCol: return tr("For");
case CondCol: return tr("Cond."); case CondCol: return tr("Cond.");
case ReferencingCol: return tr("Referencing"); // case ReferencingCol: return tr("Referencing");
case ProcedureCol: return tr("Function"); case ProcedureCol: return tr("Function");
} }
} }
@ -91,9 +91,9 @@ QVariant TriggerTableModel::getData(const QModelIndex &index) const
case DeferrableCol: return t.deferrable; case DeferrableCol: return t.deferrable;
case InitiallyCol: return t.initdeferred; case InitiallyCol: return t.initdeferred;
case ForCol: return t.forEach(); case ForCol: return t.forEach();
// case CondCol: return tr("Cond."); case CondCol: return t.whenclause;
// case ReferencingCol: return tr("Referencing"); // case ReferencingCol: return tr("Referencing");
// case ProcedureCol: return tr("Function"); case ProcedureCol: return t.procedure();
} }
return QVariant(); return QVariant();

View file

@ -28,7 +28,7 @@ public:
InitiallyCol, InitiallyCol,
ForCol, ForCol,
CondCol, CondCol,
ReferencingCol, //ReferencingCol,
ProcedureCol, ProcedureCol,
colCount colCount
}; };

View file

@ -1,14 +1,12 @@
#include "PgAuthIdContainer.h" #include "PgAuthIdContainer.h"
#include "Pgsql_Connection.h" #include "Pgsql_Col.h"
#include "PgDatabaseCatalog.h"
std::string PgAuthIdContainer::getLoadQuery() const std::string PgAuthIdContainer::getLoadQuery() const
{ {
std::string result = std::string result =
"SELECT oid, rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, " "SELECT oid, rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, "
" rolcanlogin, rolreplication, rolconnlimit, rolvaliduntil"; " rolcanlogin, rolreplication, rolconnlimit, rolvaliduntil";
auto cat = m_catalogue.lock(); if (minimumVersion(90500))
if (cat && cat->serverVersion() >= 90500)
result += ", rolbypassrls"; result += ", rolbypassrls";
result += "\n" result += "\n"
@ -16,29 +14,14 @@ std::string PgAuthIdContainer::getLoadQuery() const
return result; return result;
} }
void PgAuthIdContainer::load(const Pgsql::Result &res) PgAuthId PgAuthIdContainer::loadElem(const Pgsql::Row &row)
{ {
const int n_rows = res.rows(); Pgsql::Col col(row);
m_container.clear(); PgAuthId v;
m_container.reserve(n_rows); col >> v.oid >> v.name >> v.super >> v.inherit >> v.createRole >> v.createDB
auto cat = m_catalogue.lock(); >> v.canlogin >> v.replication >> v.connLimit >> v.validUntil;
bool with_rls = (cat && cat->serverVersion() >= 90500); if (minimumVersion(90500))
for (auto row : res) { col >> v.bypassRls;
PgAuthId v;
v.oid << row.get(0); // InvalidOid;
v.name << row.get(1);
v.super << row.get(2);
v.inherit << row.get(3);
v.createRole << row.get(4);
v.createDB << row.get(5);
v.canlogin << row.get(6);
v.replication << row.get(7);
v.connLimit << row.get(8);
v.validUntil << row.get(9);
v.bypassRls = with_rls ? (bool)row.get(10) : false;
// QDateTime
m_container.push_back(v);
}
std::sort(m_container.begin(), m_container.end());
}
return v;
}

View file

@ -11,14 +11,14 @@ namespace Pgsql {
} }
class PgAuthIdContainer: public PgContainer<PgAuthId> { class PgAuthIdContainer: public PgContainer<PgAuthId> {
public: public:
using PgContainer<PgAuthId>::PgContainer; using PgContainer<PgAuthId>::PgContainer;
virtual std::string getLoadQuery() const override; virtual std::string getLoadQuery() const override;
virtual void load(const Pgsql::Result &res) override; protected:
private: PgAuthId loadElem(const Pgsql::Row &row) override;
private:
}; };

View file

@ -25,9 +25,10 @@ PgClass PgClassContainer::loadElem(const Pgsql::Row &row)
>> v.kind >> v.hasoids >> v.ispopulated >> v.frozenxid >> v.minmxid >> v.kind >> v.hasoids >> v.ispopulated >> v.frozenxid >> v.minmxid
>> v.acl >> v.options; >> v.acl >> v.options;
auto cat = m_catalogue.lock(); auto&& ns = m_catalog.namespaces()->getByKey(v.relnamespace);
auto ns = cat->namespaces()->getByKey(v.relnamespace); if (ns) {
v.relnamespace_name = ns.name; v.relnamespace_name = ns->name;
v.system_namespace = ns.isSystemCatalog(); v.system_namespace = ns->isSystemCatalog();
}
return v; return v;
} }

View file

@ -1,2 +1,11 @@
#include "PgContainer.h" #include "PgContainer.h"
#include "PgDatabaseCatalog.h"
IPgContainer::IPgContainer(PgDatabaseCatalog& cat)
: m_catalog(cat)
{}
bool IPgContainer::minimumVersion(int required_version) const
{
return m_catalog.serverVersion() >= required_version;
}

View file

@ -11,25 +11,30 @@
class PgDatabaseCatalog; class PgDatabaseCatalog;
class IPgContainter { class IPgContainer {
public: public:
virtual ~IPgContainter() = default; IPgContainer(PgDatabaseCatalog& cat);
virtual ~IPgContainer() = default;
virtual std::string getLoadQuery() const = 0; virtual std::string getLoadQuery() const = 0;
virtual void load(const Pgsql::Result &res) = 0; virtual void load(const Pgsql::Result &res) = 0;
bool minimumVersion(int required_version) const;
protected:
PgDatabaseCatalog& m_catalog;
}; };
template<typename T, typename K=Oid> template<typename T, typename K=Oid>
class PgContainer: public IPgContainter { class PgContainer: public IPgContainer {
public: public:
using t_Container = std::vector<T>; ///< Do not assume it will stay a vector only expect bidirectional access using t_Container = std::vector<T>; ///< Do not assume it will stay a vector only expect bidirectional access
PgContainer() = default; PgContainer() = default;
explicit PgContainer(std::weak_ptr<PgDatabaseCatalog> cat) explicit PgContainer(PgDatabaseCatalog& cat)
: m_catalogue(cat) : IPgContainer(cat)
{} {
}
typename t_Container::const_iterator begin() const typename t_Container::const_iterator begin() const
{ {
@ -46,30 +51,34 @@ public:
m_container.clear(); m_container.clear();
} }
int count() const size_t count() const
{ {
return (int)m_container.size(); return m_container.size();
} }
const T& getByKey(const K &key) const const T* getByKey(const K &key) const
{ {
auto lb_result = std::lower_bound(m_container.begin(), m_container.end(), key); auto lb_result = std::lower_bound(m_container.begin(), m_container.end(), key);
if (lb_result != m_container.end() && *lb_result == key) if (lb_result != m_container.end() && *lb_result == key)
return *lb_result; return &*lb_result;
return m_invalidInstance; return nullptr;
} }
const T& getByName(const QString &name) const const T* getByName(const QString &name) const
{ {
auto find_res = std::find(m_container.begin(), m_container.end(), name); auto find_res = std::find(m_container.begin(), m_container.end(), name);
if (find_res != m_container.end()) if (find_res != m_container.end())
return *find_res; return &*find_res;
return m_invalidInstance; return nullptr;
} }
/// Retrieve element by index
///
/// This function throws when idx is out of range
/// otherwise it always returns a valid object.
const T& getByIdx(int idx) const const T& getByIdx(int idx) const
{ {
return m_container.at(idx); return m_container.at(idx);
@ -97,22 +106,19 @@ public:
std::sort(m_container.begin(), m_container.end()); std::sort(m_container.begin(), m_container.end());
} }
protected: protected:
std::weak_ptr<PgDatabaseCatalog> m_catalogue;
t_Container m_container; t_Container m_container;
/** Override the implementation for this function to implement loading of single row. /** Override the implementation for this function to implement loading of single row.
* *
* When overriding this function there is no need to override load. * When overriding this function there is no need to override load.
*/ */
virtual T loadElem(const Pgsql::Row &) { return m_invalidInstance; } virtual T loadElem(const Pgsql::Row &) = 0;
private:
T m_invalidInstance;
}; };
template<typename T, typename K=Oid> template<typename T, typename K=Oid>
class PgSPtrContainer: public IPgContainter { class PgSPtrContainer: public IPgContainer {
public: public:
using t_Elem = std::shared_ptr<T>; using t_Elem = std::shared_ptr<T>;
using t_Container = std::vector<t_Elem>; ///< Do not assume it will stay a vector only expect bidirectional access using t_Container = std::vector<t_Elem>; ///< Do not assume it will stay a vector only expect bidirectional access
@ -148,7 +154,7 @@ public:
if (lb_result != m_container.end() && **lb_result == key) if (lb_result != m_container.end() && **lb_result == key)
return *lb_result; return *lb_result;
return m_invalidInstance; return nullptr;
} }
const t_Elem getByName(const QString name) const const t_Elem getByName(const QString name) const
@ -159,7 +165,7 @@ public:
if (find_res != m_container.end()) if (find_res != m_container.end())
return *find_res; return *find_res;
return m_invalidInstance; return nullptr;
} }
const t_Elem getByIdx(int idx) const const t_Elem getByIdx(int idx) const

View file

@ -27,9 +27,9 @@ QString getRoleNameFromOid(const PgDatabaseCatalog &cat, Oid oid)
QString name; QString name;
auto auth_ids = cat.authIds(); auto auth_ids = cat.authIds();
if (auth_ids) { if (auth_ids) {
const PgAuthId& auth_id = auth_ids->getByKey(oid); const PgAuthId* auth_id = auth_ids->getByKey(oid);
if (auth_id.valid()) { if (auth_id) {
name = auth_id.name; name = auth_id->name;
} }
} }
return name; return name;
@ -46,7 +46,8 @@ QString getNamespaceDisplayString(const PgDatabaseCatalog &cat, Oid oid)
QString result; QString result;
auto nss = cat.namespaces(); auto nss = cat.namespaces();
auto ns = nss->getByKey(oid); auto ns = nss->getByKey(oid);
result = ns.name; //QString("ns %1").arg(oid); if (ns)
result = ns->name; //QString("ns %1").arg(oid);
return result; return result;
} }
@ -55,7 +56,8 @@ QString getClassDisplayString(const PgDatabaseCatalog &cat, Oid oid)
QString result; QString result;
auto l = cat.classes(); auto l = cat.classes();
auto e = l->getByKey(oid); auto e = l->getByKey(oid);
result = e.name; if (e)
result = e->name;
return result; return result;
} }
@ -75,13 +77,13 @@ QString getTablespaceDisplayString(const PgDatabaseCatalog &cat, Oid oid)
// TODO load list and lookup name // TODO load list and lookup name
if (oid == 0) { if (oid == 0) {
auto dbname = cat.getDBName(); auto dbname = cat.getDBName();
oid = cat.databases()->getByName(dbname).tablespace; oid = cat.databases()->getByName(dbname)->tablespace;
auto ts = cat.tablespaces()->getByKey(oid); auto ts = cat.tablespaces()->getByKey(oid);
return ts.name + " (inherited)"; return ts->name + " (inherited)";
} }
else { else {
auto ts = cat.tablespaces()->getByKey(oid); auto ts = cat.tablespaces()->getByKey(oid);
return ts.name; return ts->name;
} }
} }
@ -93,18 +95,18 @@ QString getTypeDisplayString(const PgDatabaseCatalog &cat, Oid oid, int32_t typm
auto tc = cat.types(); auto tc = cat.types();
auto t = tc->getByKey(oid); auto t = tc->getByKey(oid);
if (t.oid == InvalidOid) { if (t == nullptr) {
return "(invalid/unknown)"; return "(invalid/unknown)";
} }
QString s; QString s;
if (t.category == TypCategory::Array) { if (t->category == TypCategory::Array) {
// auto et = tc->getByKey(t.elem); // auto et = tc->getByKey(t.elem);
// s = et.name; // s = et.name;
s = getTypeDisplayString(cat, t.elem, typmod); s = getTypeDisplayString(cat, t->elem, typmod);
s += "[]"; s += "[]";
} }
else { else {
s = t.name; s = t->name;
switch (oid) { switch (oid) {
case varchar_oid: case varchar_oid:
case char_oid: case char_oid:
@ -198,7 +200,7 @@ void PgDatabaseCatalog::loadInfo(Pgsql::Connection &conn)
m_dbName = conn.getDBName(); m_dbName = conn.getDBName();
} }
void load(Pgsql::Connection &conn, IPgContainter &pg_cont) void load(Pgsql::Connection &conn, IPgContainer &pg_cont)
{ {
//QThread::msleep(400); //QThread::msleep(400);
std::string q = pg_cont.getLoadQuery(); std::string q = pg_cont.getLoadQuery();

View file

@ -99,7 +99,7 @@ private:
void load2(std::shared_ptr<T> &ptr, Pgsql::Connection &conn) void load2(std::shared_ptr<T> &ptr, Pgsql::Connection &conn)
{ {
if (!ptr) if (!ptr)
ptr = std::make_shared<T>(shared_from_this()); ptr = std::make_shared<T>(*this);
load(conn, *ptr); load(conn, *ptr);
} }

View file

@ -1,26 +1,18 @@
#include "PgDatabaseContainer.h" #include "PgDatabaseContainer.h"
#include "Pgsql_Connection.h"
#include "Pgsql_Col.h" #include "Pgsql_Col.h"
std::string PgDatabaseContainer::getLoadQuery() const std::string PgDatabaseContainer::getLoadQuery() const
{ {
return "SELECT oid,datname,datdba,encoding,datcollate,datctype,datistemplate,datallowconn," return "SELECT oid,datname,datdba,encoding,datcollate,datctype,datistemplate,datallowconn,"
"datconnlimit,dattablespace,datacl FROM pg_database"; "datconnlimit,dattablespace,datacl FROM pg_database";
} }
void PgDatabaseContainer::load(const Pgsql::Result &res) PgDatabase PgDatabaseContainer::loadElem(const Pgsql::Row &row)
{ {
const int n_rows = res.rows(); Pgsql::Col col(row);
m_container.clear(); PgDatabase v;
m_container.reserve(n_rows); col >> v.oid >> v.name >> v.dba >> v.encoding >> v.collate >> v.ctype >> v.isTemplate
for (auto row : res) { >> v.allowConn >> v.connLimit >> v.tablespace >> v.acl;
Pgsql::Col col(row); return v;
PgDatabase v;
col >> v.oid >> v.name >> v.dba >> v.encoding >> v.collate >> v.ctype >> v.isTemplate
>> v.allowConn >> v.connLimit >> v.tablespace >> v.acl;
m_container.push_back(v);
}
std::sort(m_container.begin(), m_container.end());
} }

View file

@ -17,7 +17,8 @@ public:
using PgContainer<PgDatabase>::PgContainer; using PgContainer<PgDatabase>::PgContainer;
virtual std::string getLoadQuery() const override; virtual std::string getLoadQuery() const override;
virtual void load(const Pgsql::Result &res) override; protected:
PgDatabase loadElem(const Pgsql::Row &row) override;
private: private:
}; };

View file

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

View file

@ -0,0 +1,13 @@
#ifndef PGDATABASEOBJECT_H
#define PGDATABASEOBJECT_H
#include "PgServerObject.h"
/// Base class for objects that are part of a database
class PgDatabaseObject: public PgServerObject {
public:
using PgServerObject::PgServerObject;
};
#endif // PGDATABASEOBJECT_H

View file

@ -3,20 +3,20 @@
#include "PgClassContainer.h" #include "PgClassContainer.h"
#include "PgAmContainer.h" #include "PgAmContainer.h"
PgIndex::PgIndex() = default;
PgIndex::PgIndex(std::weak_ptr<PgDatabaseCatalog> cat)
: PgObject(cat)
{}
QString PgIndex::getAm() const QString PgIndex::getAm() const
{ {
auto cat = catalog.lock(); auto&& cat = catalog();
QString result; QString result;
if (cat) { auto idxcls = cat.classes()->getByKey(indexrelid);
auto idxcls = cat->classes()->getByKey(indexrelid); if (idxcls) {
auto am = cat->ams()->getByKey(idxcls.am); auto am = cat.ams()->getByKey(idxcls->am);
result = am.name; if (am)
result = am->name;
} }
return result; return result;
} }
QString PgIndex::objectName() const
{
return getAm();
}

View file

@ -1,12 +1,12 @@
#ifndef PGINDEX_H #ifndef PGINDEX_H
#define PGINDEX_H #define PGINDEX_H
#include "PgObject.h" #include "PgSchemaObject.h"
#include "Pgsql_declare.h" #include "Pgsql_declare.h"
#include <QString> #include <QString>
#include <vector> #include <vector>
class PgIndex : public PgObject { class PgIndex : public PgSchemaObject {
public: public:
Oid indexrelid = InvalidOid; // oid of pg_class for this index Oid indexrelid = InvalidOid; // oid of pg_class for this index
@ -30,10 +30,9 @@ public:
QString pred; QString pred;
QString definition; QString definition;
PgIndex(); using PgSchemaObject::PgSchemaObject;
explicit PgIndex(std::weak_ptr<PgDatabaseCatalog> cat);
QString getAm() const; QString getAm() const;
virtual QString objectName() const override;
bool operator==(Oid _oid) const { return indexrelid == _oid; } bool operator==(Oid _oid) const { return indexrelid == _oid; }
//bool operator==(const QString &n) const { return name == n; } //bool operator==(const QString &n) const { return name == n; }

View file

@ -12,8 +12,7 @@ SELECT indexrelid, indrelid, indnatts, indisunique, indisprimary,
indcollation, indclass, indoption, indexprs, indpred, indcollation, indclass, indoption, indexprs, indpred,
pg_get_indexdef(indexrelid))__"; pg_get_indexdef(indexrelid))__";
auto cat = m_catalogue.lock(); if (minimumVersion(90400))
if (cat && cat->serverVersion() >= 90400)
q += ", indisreplident "; q += ", indisreplident ";
q += "\nFROM pg_index"; q += "\nFROM pg_index";
return q; return q;
@ -22,7 +21,7 @@ SELECT indexrelid, indrelid, indnatts, indisunique, indisprimary,
PgIndex PgIndexContainer::loadElem(const Pgsql::Row &row) PgIndex PgIndexContainer::loadElem(const Pgsql::Row &row)
{ {
Pgsql::Col col(row); Pgsql::Col col(row);
PgIndex v(m_catalogue); PgIndex v(m_catalog);
col >> v.indexrelid >> v.relid >> v.natts >> v.isunique col >> v.indexrelid >> v.relid >> v.natts >> v.isunique
>> v.isprimary >> v.isexclusion >> v.immediate >> v.isclustered >> v.isprimary >> v.isexclusion >> v.immediate >> v.isclustered
>> v.isvalid >> v.checkxmin >> v.isready >> v.islive; >> v.isvalid >> v.checkxmin >> v.isready >> v.islive;
@ -31,8 +30,7 @@ PgIndex PgIndexContainer::loadElem(const Pgsql::Row &row)
col.getAsVector<Oid>(std::back_inserter(v.indclass)); col.getAsVector<Oid>(std::back_inserter(v.indclass));
col.getAsVector<int16_t>(std::back_inserter(v.option)); col.getAsVector<int16_t>(std::back_inserter(v.option));
col >> v.exprs >> v.pred >> v.definition; col >> v.exprs >> v.pred >> v.definition;
auto cat = m_catalogue.lock(); if (minimumVersion(90400))
if (cat && cat->serverVersion() >= 90400)
col >> v.isreplident; col >> v.isreplident;
return v; return v;

View file

@ -8,16 +8,11 @@ std::string PgNamespaceContainer::getLoadQuery() const
return "SELECT oid, nspname, nspowner, nspacl FROM pg_catalog.pg_namespace"; return "SELECT oid, nspname, nspowner, nspacl FROM pg_catalog.pg_namespace";
} }
void PgNamespaceContainer::load(const Pgsql::Result &res) PgNamespace PgNamespaceContainer::loadElem(const Pgsql::Row &row)
{ {
const int n_rows = res.rows(); Pgsql::Col col(row);
m_container.clear(); PgNamespace v;
m_container.reserve(n_rows); col >> v.oid >> v.name >> v.owner >> v.acl;
for (auto row : res) { return v;
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());
} }

View file

@ -15,8 +15,8 @@ public:
using PgContainer<PgNamespace>::PgContainer; using PgContainer<PgNamespace>::PgContainer;
virtual std::string getLoadQuery() const override; virtual std::string getLoadQuery() const override;
virtual void load(const Pgsql::Result &res) override; protected:
virtual PgNamespace loadElem(const Pgsql::Row &row) override;
private: private:
}; };

View file

@ -1,6 +1,19 @@
#include "PgObject.h" #include "PgObject.h"
#include "SqlFormattingUtils.h"
PgObject::PgObject() PgObject::PgObject(PgDatabaseCatalog& cat)
: m_catalog(&cat)
{}
PgObject::~PgObject()
{}
QString PgObject::quotedObjectName() const
{ {
return quoteIdent(objectName());
}
const PgDatabaseCatalog& PgObject::catalog() const
{
return *m_catalog;
} }

View file

@ -1,19 +1,25 @@
#ifndef PGOBJECT_H #ifndef PGOBJECT_H
#define PGOBJECT_H #define PGOBJECT_H
#include <memory> #include <QString>
class PgDatabaseCatalog; class PgDatabaseCatalog;
class PgObject class PgObject {
{
public: public:
PgObject(); explicit PgObject(PgDatabaseCatalog& cat);
explicit PgObject(std::weak_ptr<PgDatabaseCatalog> cat) virtual ~PgObject();
: catalog(cat)
{} virtual QString objectName() const = 0;
/// Default implementation uses objectName and add quotes when needed.
virtual QString quotedObjectName() const;
protected: protected:
std::weak_ptr<PgDatabaseCatalog> catalog; const PgDatabaseCatalog& catalog() const;
private:
PgDatabaseCatalog* m_catalog;
}; };
#endif // PGOBJECT_H #endif // PGOBJECT_H

View file

@ -1,2 +1,6 @@
#include "PgProc.h" #include "PgProc.h"
QString PgProc::objectName() const
{
return name;
}

View file

@ -1,48 +1,52 @@
#ifndef PGPROC_H #ifndef PGPROC_H
#define PGPROC_H #define PGPROC_H
#include "PgSchemaObject.h"
#include <QString> #include <QString>
#include <libpq-fe.h> #include <libpq-fe.h>
#include "Pgsql_Value.h" #include "Pgsql_Value.h"
class PgProc { class PgProc: public PgSchemaObject {
public: public:
using PgSchemaObject::PgSchemaObject;
Oid oid = InvalidOid; // oid Oid oid = InvalidOid; // oid
QString proname; // name QString name; // name
Oid pronamespace = InvalidOid; // oid // Oid pronamespace = InvalidOid; // oid, namespace
Oid proowner = InvalidOid; // oid Oid owner = InvalidOid; // oid
Oid prolang = InvalidOid; // oid Oid lang = InvalidOid; // oid
float procost = 0.f; // float4 float cost = 0.f; // float4
float prorows = 0.f; // float4 float rows = 0.f; // float4
Oid provariadic = InvalidOid; // oid Oid variadic = InvalidOid; // oid
QString protransform; // regproc QString transform; // regproc
bool proisagg = false; // bool bool isagg = false; // bool
bool proiswindow = false; // bool bool iswindow = false; // bool
bool prosecdef = false; // bool bool secdef = false; // bool
bool proleakproof = false; // bool bool leakproof = false; // bool
bool proisstrict = false; // bool bool isstrict = false; // bool
bool proretset = false; // bool bool retset = false; // bool
char provolatile = '\0'; // char char provolatile = '\0'; // char
char proparallel = '\0'; // char, version >= 9.6 char parallel = '\0'; // char, version >= 9.6
int16_t pronargs = 0; // int2 int16_t nargs = 0; // int2
int16_t pronargdefaults = 0; // int2 int16_t nargdefaults = 0; // int2
Oid prorettype = InvalidOid; // oid Oid rettype = InvalidOid; // oid
std::vector<Oid> proargtypes; // oid[] std::vector<Oid> argtypes; // oid[]
std::vector<Oid> proallargtypes; // oid[] std::vector<Oid> allargtypes; // oid[]
std::vector<char> proargmodes; // char[] std::vector<char> argmodes; // char[]
std::vector<QString> proargnames; // text[] std::vector<QString> argnames; // text[]
std::optional<QString> proargdefaults; // pg_node_tree std::optional<QString> argdefaults; // pg_node_tree
std::vector<Oid> protrftypes; // oid[], version >= 9.5 std::vector<Oid> trftypes; // oid[], version >= 9.5
QString prosrc; // text QString src; // text
QString probin; // text QString bin; // text
std::vector<QString> proconfig; // text[] std::vector<QString> config; // text[]
std::vector<QString> proacl; // aclitem[] std::vector<QString> acl; // aclitem[]
bool operator==(Oid _oid) const { return oid == _oid; } bool operator==(Oid _oid) const { return oid == _oid; }
bool operator==(const QString &n) const { return proname == n; } bool operator==(const QString &n) const { return name == n; }
bool operator<(Oid _oid) const { return oid < _oid; } bool operator<(Oid _oid) const { return oid < _oid; }
bool operator<(const PgProc &rhs) const { return oid < rhs.oid; } bool operator<(const PgProc &rhs) const { return oid < rhs.oid; }
virtual QString objectName() const override;
}; };
#endif // PGPROC_H #endif // PGPROC_H

View file

@ -13,12 +13,10 @@ std::string PgProcContainer::getLoadQuery() const
"proisstrict,proretset,provolatile,pronargs,pronargdefaults,prorettype," "proisstrict,proretset,provolatile,pronargs,pronargdefaults,prorettype,"
"proargtypes,proallargtypes,proargmodes,proargnames,proargdefaults," "proargtypes,proallargtypes,proargmodes,proargnames,proargdefaults,"
"prosrc,probin,proconfig,proacl"; "prosrc,probin,proconfig,proacl";
auto cat = m_catalogue.lock(); if (minimumVersion(90500)) {
int ver = cat->serverVersion();
if (ver >= 90500) {
column_list += ",protrftypes"; column_list += ",protrftypes";
} }
else if (cat->serverVersion() >= 90600) { if (minimumVersion(90600)) {
column_list += ",proparallel"; column_list += ",proparallel";
} }
@ -28,24 +26,25 @@ std::string PgProcContainer::getLoadQuery() const
PgProc PgProcContainer::loadElem(const Pgsql::Row &row) PgProc PgProcContainer::loadElem(const Pgsql::Row &row)
{ {
Pgsql::Col col(row); Pgsql::Col col(row);
PgProc v; PgProc v(m_catalog);
col >> v.oid >> v.proname >> v.pronamespace >> v.proowner >> v.prolang >> v.procost >> v.prorows Oid namespace_oid;
>> v.provariadic >> v.protransform >> v.proisagg >> v.proiswindow >> v.prosecdef >> v.proleakproof col >> v.oid >> v.name >> namespace_oid >> v.owner >> v.lang >> v.cost >> v.rows
>> v.proisstrict >> v.proretset >> v.provolatile >> v.pronargs >> v.pronargdefaults >> v.variadic >> v.transform >> v.isagg >> v.iswindow >> v.secdef >> v.leakproof
>> v.prorettype; >> v.isstrict >> v.retset >> v.provolatile >> v.nargs >> v.nargdefaults
col.getAsVector<Oid>(std::back_inserter(v.proargtypes)); >> v.rettype;
col >> v.proallargtypes >> v.proargmodes >> v.proargnames col.getAsVector<Oid>(std::back_inserter(v.argtypes));
>> v.proargdefaults; col >> v.allargtypes >> v.argmodes >> v.argnames
col >> v.prosrc; >> v.argdefaults;
col >> v.probin >> v.proconfig >> v.proacl; col >> v.src;
col >> v.bin >> v.config >> v.acl;
auto cat = m_catalogue.lock(); v.setSchemaOid(namespace_oid);
int ver = cat->serverVersion();
if (ver >= 90500) { if (minimumVersion(90500)) {
col >> v.protrftypes; col >> v.trftypes;
} }
else if (cat->serverVersion() >= 90600) { if (minimumVersion(90600)) {
col >> v.proparallel; col >> v.parallel;
} }
return v; return v;

View file

@ -0,0 +1,30 @@
#include "PgSchemaObject.h"
#include "PgDatabaseCatalog.h"
#include "PgNamespace.h"
#include "PgNamespaceContainer.h"
#include "SqlFormattingUtils.h"
Oid PgSchemaObject::schemaOid() const
{
return m_schemaOid;
}
void PgSchemaObject::setSchemaOid(Oid oid)
{
m_schemaOid = oid;
}
QString PgSchemaObject::quotedSchemaName() const
{
return quoteIdent(ns().name);
}
QString PgSchemaObject::fullyQualifiedQuotedObjectName() const
{
return quotedSchemaName() + "." + quotedObjectName();
}
const PgNamespace& PgSchemaObject::ns() const
{
return *catalog().namespaces()->getByKey(m_schemaOid);
}

26
pglablib/PgSchemaObject.h Normal file
View file

@ -0,0 +1,26 @@
#ifndef PGSCHEMAOBJECT_H
#define PGSCHEMAOBJECT_H
#include <QString>
#include "PgDatabaseObject.h"
#include <libpq-fe.h>
class PgNamespace;
/// Base class for database objects that are part of a specific schema
class PgSchemaObject: public PgDatabaseObject {
public:
using PgDatabaseObject::PgDatabaseObject;
Oid schemaOid() const;
void setSchemaOid(Oid oid);
QString quotedSchemaName() const;
/// Returns the schema name and object name with proper quotes
QString fullyQualifiedQuotedObjectName() const;
const PgNamespace& ns() const;
private:
Oid m_schemaOid = InvalidOid;
};
#endif // PGSCHEMAOBJECT_H

View file

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

15
pglablib/PgServerObject.h Normal file
View file

@ -0,0 +1,15 @@
#ifndef PGSERVEROBJECT_H
#define PGSERVEROBJECT_H
#include <QString>
#include "PgObject.h"
/// Base object for objects that belong to a server
class PgServerObject: public PgObject {
public:
using PgObject::PgObject;
};
#endif // PGSERVEROBJECT_H

View file

@ -1,33 +1,37 @@
#include "PgTrigger.h" #include "PgTrigger.h"
#include "PgClassContainer.h" #include "PgClassContainer.h"
#include "PgDatabaseCatalog.h" #include "PgDatabaseCatalog.h"
#include "PgProcContainer.h"
#include "SqlFormattingUtils.h" #include "SqlFormattingUtils.h"
#include <QStringBuilder> #include <QStringBuilder>
QString PgTrigger::objectName() const
{
return name;
}
QString PgTrigger::dropSql(const PgDatabaseCatalog &catalog) QString PgTrigger::dropSql()
{ {
if (m_dropSql.isEmpty()) { if (m_dropSql.isEmpty()) {
auto&& fqtablename = genFQTableName(catalog, catalog.classes()->getByKey(relid)); auto&& fqtablename = genFQTableName(catalog(), *catalog().classes()->getByKey(relid));
m_dropSql = "DROP TRIGGER " % quoteIdent(name) m_dropSql = "DROP TRIGGER " % quoteIdent(name)
% " ON " % fqtablename % ";"; % " ON " % fqtablename % ";";
} }
return m_dropSql; return m_dropSql;
} }
QString PgTrigger::createSql(const PgDatabaseCatalog &catalog) QString PgTrigger::createSql()
{ {
if (m_createSql.isEmpty()) { if (m_createSql.isEmpty()) {
auto&& fqtablename = genFQTableName(catalog, catalog.classes()->getByKey(relid)); auto&& fqtablename = genFQTableName(catalog(), *catalog().classes()->getByKey(relid));
// if (GetLanguage() == wxT("edbspl")) auto&& triggername = quoteIdent(name);
// sql += wxT("CREATE OR REPLACE TRIGGER ");
// else if (GetConnection()->BackendMinimumVersion(8, 2) && GetIsConstraint())
if (constraint != InvalidOid) if (constraint != InvalidOid)
m_createSql += "CREATE CONSTRAINT TRIGGER "; m_createSql += "CREATE CONSTRAINT TRIGGER ";
else else
m_createSql += "CREATE TRIGGER "; m_createSql += "CREATE TRIGGER ";
m_createSql += quoteIdent(name) + "\n " m_createSql += triggername + "\n "
+ typeFireWhen() + typeFireWhen()
+ " " + event(); + " " + event();
@ -41,29 +45,14 @@ QString PgTrigger::createSql(const PgDatabaseCatalog &catalog)
} }
m_createSql += "\n FOR EACH " + forEach(); m_createSql += "\n FOR EACH " + forEach();
// if (GetConnection()->BackendMinimumVersion(8, 5) // requires atleast 8.5 don;t think we have to support older
// && !GetWhen().IsEmpty()) if (!whenclause.isEmpty())
// sql += wxT("\n WHEN (") + GetWhen() + wxT(")"); m_createSql += "\n WHEN (" + whenclause + ")";
// if (GetLanguage() == wxT("edbspl")) m_createSql += QString("\n EXECUTE PROCEDURE %1;\n").arg(procedure());
// {
// sql += wxT("\n") + GetSource();
// if (!sql.Trim().EndsWith(wxT(";")))
// sql = sql.Trim() + wxT(";");
// sql += wxT("\n");
// }
// else
// {
// sql += wxT("\n EXECUTE PROCEDURE ") + triggerFunction->GetQuotedFullIdentifier()
// + wxT("(") + GetArguments() + wxT(")")
// + wxT(";\n");
// }
// if (!GetEnabled()) if (!enabled)
// { m_createSql += QString("ALTER TABLE %1 DISABLE TRIGGER %2;\n").arg(fqtablename, triggername);
// sql += wxT("ALTER TABLE ") + GetQuotedFullTable() + wxT(" ")
// + wxT("DISABLE TRIGGER ") + GetQuotedIdentifier() + wxT(";\n");
// }
// if (!GetComment().IsEmpty()) // if (!GetComment().IsEmpty())
// sql += wxT("COMMENT ON TRIGGER ") + GetQuotedIdentifier() + wxT(" ON ") + GetQuotedFullTable() // sql += wxT("COMMENT ON TRIGGER ") + GetQuotedIdentifier() + wxT(" ON ") + GetQuotedFullTable()
@ -124,3 +113,49 @@ QString PgTrigger::forEach() const
return "STATEMENT"; return "STATEMENT";
} }
QString PgTrigger::procedure() const
{
const PgProc *proc = catalog().procs()->getByKey(foid);
QString func_name = proc->fullyQualifiedQuotedObjectName();
return QString("%1(%2)").arg(func_name, arguments());
}
QString PgTrigger::arguments() const
{
QString arglist;
if (nargs > 0)
arglist = args;
QString output;
while (!arglist.isEmpty()) {
int pos = arglist.indexOf(QChar::Null);
if (pos != 0) {
QString arg;
if (pos > 0)
arg = arglist.left(pos);
else
arg = arglist;
if (!output.isEmpty())
output += ", ";
bool conversion_ok = false;
arg.toLongLong(&conversion_ok);
if (conversion_ok)
output += arg;
else
output += escapeLiteral(arg);
}
else {
if (!output.isEmpty())
output += ", ";
output += escapeLiteral(QString());
}
if (pos >= 0)
arglist = arglist.mid(pos + 4);
else
break;
}
return output;
}

View file

@ -1,13 +1,14 @@
#ifndef PGTRIGGER_H #ifndef PGTRIGGER_H
#define PGTRIGGER_H #define PGTRIGGER_H
#include "PgSchemaObject.h"
#include "Pgsql_Value.h" #include "Pgsql_Value.h"
#include <QString> #include <QString>
#include <libpq-fe.h> #include <libpq-fe.h>
class PgDatabaseCatalog; class PgDatabaseCatalog;
class PgTrigger { class PgTrigger: public PgSchemaObject {
public: public:
Oid oid = InvalidOid; Oid oid = InvalidOid;
Oid relid; Oid relid;
@ -24,7 +25,14 @@ public:
int16_t nargs; int16_t nargs;
QString attr; QString attr;
QString args; QString args;
QString qual; QString whenclause;
QString oldtable; // >= 10.0
QString newtable; // >= 10.0
using PgSchemaObject::PgSchemaObject;
virtual QString objectName() const override;
bool operator==(Oid _oid) const { return oid == _oid; } bool operator==(Oid _oid) const { return oid == _oid; }
bool operator==(const QString &n) const { return name == n; } bool operator==(const QString &n) const { return name == n; }
@ -39,20 +47,22 @@ public:
static constexpr int TriggerTypeTruncate = (1 << 5); static constexpr int TriggerTypeTruncate = (1 << 5);
static constexpr int TriggerTypeInstead = (1 << 6); static constexpr int TriggerTypeInstead = (1 << 6);
QString dropSql(const PgDatabaseCatalog &catalog); QString dropSql();
QString createSql(const PgDatabaseCatalog &catalog); QString createSql();
bool isRow() const { return type & TriggerTypeRow; } bool isRow() const { return type & TriggerTypeRow; }
bool isBefore() const { return type & TriggerTypeBefore; } bool isBefore() const { return type & TriggerTypeBefore; }
QString typeFireWhen() const; QString typeFireWhen() const;
QString eventAbbr() const; QString eventAbbr() const;
QString event() const; QString event() const;
QString forEach() const; QString forEach() const;
QString procedure() const;
//wxString pgTrigger::GetForEach() const //wxString pgTrigger::GetForEach() const
//{ //{
// return (triggerType & TRIGGER_TYPE_ROW) ? wxT("ROW") : wxT("STATEMENT"); // return (triggerType & TRIGGER_TYPE_ROW) ? wxT("ROW") : wxT("STATEMENT");
//} //}
QString arguments() const;
private: private:
mutable QString m_dropSql; // cache mutable QString m_dropSql; // cache
mutable QString m_createSql; // cache mutable QString m_createSql; // cache

View file

@ -6,18 +6,39 @@
std::string PgTriggerContainer::getLoadQuery() const std::string PgTriggerContainer::getLoadQuery() const
{ {
return R"(SELECT oid, * std::string q =
FROM pg_trigger "SELECT oid, tgrelid, tgname, tgfoid, tgtype, tgenabled, tgisinternal, tgconstrrelid, \n"
WHERE NOT tgisinternal)"; " tgconstrindid, tgconstraint, tgdeferrable, tginitdeferred, tgnargs, tgattr, \n"
" tgargs, COALESCE(substring(pg_get_triggerdef(oid), 'WHEN (.*) EXECUTE PROCEDURE'), substring(pg_get_triggerdef(oid), 'WHEN (.*) \\$trigger')) AS whenclause";
if (minimumVersion(90600)) {
q += ", tgoldtable, tgnewtable";
}
q +=
" FROM pg_trigger \n"
" WHERE NOT tgisinternal";
return q;
} }
PgTrigger PgTriggerContainer::loadElem(const Pgsql::Row &row) PgTrigger PgTriggerContainer::loadElem(const Pgsql::Row &row)
{ {
Pgsql::Col col(row); Pgsql::Col col(row);
PgTrigger v; PgTrigger v(m_catalog);
col >> v.oid >> v.relid >> v.name >> v.foid >> v.type >> v.enabled >> v.isinternal >> v.constrrelid col >> v.oid >> v.relid >> v.name >> v.foid >> v.type >> v.enabled >> v.isinternal >> v.constrrelid
>> v.constrindid >> v.constraint >> v.deferrable >> v.initdeferred >> v.nargs >> v.attr >> v.constrindid >> v.constraint >> v.deferrable >> v.initdeferred >> v.nargs >> v.attr;
>> v.args >> v.qual; const unsigned char * args_bytea = reinterpret_cast<const unsigned char *>(col.nextValue().c_str());
if (args_bytea) {
size_t to_length;
unsigned char *result = PQunescapeBytea(args_bytea, &to_length);
if (result) {
v.args = QString::fromUtf8(reinterpret_cast<const char*>(result), static_cast<int>(to_length));
PQfreemem(result);
}
}
col >> v.whenclause;
if (minimumVersion(90600)) {
col >> v.oldtable >> v.newtable;
}
return v; return v;
} }

View file

@ -36,45 +36,39 @@ std::string PgTypeContainer::getLoadQuery() const
"FROM pg_type"; "FROM pg_type";
} }
void PgTypeContainer::load(const Pgsql::Result &res) PgType PgTypeContainer::loadElem(const Pgsql::Row &row)
{ {
const int n_rows = res.rows(); PgType v;
m_container.clear(); v.oid << row.get(0); // InvalidOid;
m_container.reserve(n_rows); v.name << row.get(1); //. operator QString(); // "name";"NO"
for (auto row : res) { v.typnamespace << row.get(2); // InvalidOid;//"oid";"NO"
PgType v; v.owner << row.get(3); // InvalidOid;//"oid";"NO"
v.oid << row.get(0); // InvalidOid; v.len << row.get(4); // -1;//"smallint";"NO"
v.name << row.get(1); //. operator QString(); // "name";"NO" v.byval << row.get(5); // false;//"boolean";"NO"
v.typnamespace << row.get(2); // InvalidOid;//"oid";"NO" v.type << row.get(6);//""char"";"NO"
v.owner << row.get(3); // InvalidOid;//"oid";"NO" v.category << row.get(7);//""char"";"NO"
v.len << row.get(4); // -1;//"smallint";"NO" v.ispreferred << row.get(8); //false;//"boolean";"NO"
v.byval << row.get(5); // false;//"boolean";"NO" v.isdefined << row.get(9); //false;//"boolean";"NO"
v.type << row.get(6);//""char"";"NO" v.delim << row.get(10); //""char"";"NO"
v.category << row.get(7);//""char"";"NO" v.relid << row.get(11); // InvalidOid;//"oid";"NO"
v.ispreferred << row.get(8); //false;//"boolean";"NO" v.elem << row.get(12); // InvalidOid;//"oid";"NO"
v.isdefined << row.get(9); //false;//"boolean";"NO" v.array << row.get(13); // InvalidOid;//"oid";"NO"
v.delim << row.get(10); //""char"";"NO" v.input << row.get(14);//regproc";"NO"
v.relid << row.get(11); // InvalidOid;//"oid";"NO" v.output << row.get(15);//"regproc";"NO"
v.elem << row.get(12); // InvalidOid;//"oid";"NO" v.receive << row.get(16);//"regproc";"NO"
v.array << row.get(13); // InvalidOid;//"oid";"NO" v.send << row.get(17);//"regproc";"NO"
v.input << row.get(14);//regproc";"NO" v.modin << row.get(18);//"regproc";"NO"
v.output << row.get(15);//"regproc";"NO" v.modout << row.get(19);//"regproc";"NO"
v.receive << row.get(16);//"regproc";"NO" v.analyze << row.get(20);//"regproc";"NO"
v.send << row.get(17);//"regproc";"NO" v.align << row.get(21); // //""char"";"NO"
v.modin << row.get(18);//"regproc";"NO" v.storage << row.get(22); //""char"";"NO"
v.modout << row.get(19);//"regproc";"NO" v.notnull << row.get(23); //"boolean";"NO"
v.analyze << row.get(20);//"regproc";"NO" v.basetype << row.get(24); //"oid";"NO"
v.align << row.get(21); // //""char"";"NO" v.typmod << row.get(25); //-1;//"integer";"NO"
v.storage << row.get(22); //""char"";"NO" v.ndims << row.get(26); //"integer";"NO"
v.notnull << row.get(23); //"boolean";"NO" v.collation << row.get(27); //InvalidOid;//"oid";"NO"
v.basetype << row.get(24); //"oid";"NO" v.defaultbin << row.get(28);//"pg_node_tree";"YES"
v.typmod << row.get(25); //-1;//"integer";"NO" v.typdefault << row.get(29);//"text";"YES"
v.ndims << row.get(26); //"integer";"NO" v.acl << row.get(30);//"ARRAY";"YES"
v.collation << row.get(27); //InvalidOid;//"oid";"NO" return v;
v.defaultbin << row.get(28);//"pg_node_tree";"YES"
v.typdefault << row.get(29);//"text";"YES"
v.acl << row.get(30);//"ARRAY";"YES"
m_container.push_back(v);
}
std::sort(m_container.begin(), m_container.end());
} }

View file

@ -15,7 +15,6 @@ public:
using PgContainer<PgType>::PgContainer; using PgContainer<PgType>::PgContainer;
virtual std::string getLoadQuery() const override; virtual std::string getLoadQuery() const override;
virtual void load(const Pgsql::Result &res) override;
/** Searches for the type matching the specified oid. /** Searches for the type matching the specified oid.
* *
@ -24,6 +23,8 @@ public:
// const PgType& getTypeByOid(Oid oid) const; // const PgType& getTypeByOid(Oid oid) const;
// const PgType& getTypeByName(const QString &name) const; // const PgType& getTypeByName(const QString &name) const;
// const PgType& getTypeByIdx(int idx) const; // const PgType& getTypeByIdx(int idx) const;
protected:
virtual PgType loadElem(const Pgsql::Row &row) override;
private: private:
// PgType m_invalidType; ///< default constructed object for when a non existent type is being retrieved. // PgType m_invalidType; ///< default constructed object for when a non existent type is being retrieved.
// t_Types m_types; // Keep sorted by Oid // t_Types m_types; // Keep sorted by Oid

View file

@ -13,7 +13,182 @@
#include "PgNamespaceContainer.h" #include "PgNamespaceContainer.h"
#include "PgDatabaseCatalog.h" #include "PgDatabaseCatalog.h"
//inline QString u16(char16_t *utf16) namespace {
QString escapeInternal(const QString &input, bool as_ident)
{
int num_quotes = 0; /* single or double, depending on as_ident */
int num_backslashes = 0;
QChar quote_char = as_ident ? '"' : '\'';
// Doorloop input
// tel quotes
// tel backslashes
const int len = input.length();
for (int idx = 0; idx < len; ++idx) {
QChar c = input[idx];
if (c == quote_char)
++num_quotes;
else if (c == '\\')
++num_backslashes;
}
int output_size = len + num_quotes + 2; // + 2 for the quotes
if (!as_ident && num_backslashes > 0)
output_size += num_backslashes + 2; // +2 so whe can add the " E"
QString output;
output.reserve(output_size);
if (!as_ident && num_backslashes > 0)
output += " E";
output += quote_char;
if (num_quotes == 0 && (num_backslashes == 0 || as_ident)) {
output += input;
}
else {
for (int idx = 0; idx < len; ++idx) {
QChar c = input[idx];
output += c;
if (c == quote_char || (!as_ident && c == '\\'))
output += c;
}
}
output += quote_char;
return output;
}
}
QString escapeIdent(const QString &input)
{
return escapeInternal(input, true);
}
QString escapeLiteral(const QString &input)
{
return escapeInternal(input, false);
}
//char *
//PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
//{
// const char *s;
// char *result;
// char *rp;
// int num_quotes = 0; /* single or double, depending on as_ident */
// int num_backslashes = 0;
// int input_len;
// int result_size;
// char quote_char = as_ident ? '"' : '\'';
//// /* We must have a connection, else fail immediately. */
//// if (!conn)
//// return NULL;
// /* Scan the string for characters that must be escaped. */
// for (s = str; (s - str) < len && *s != '\0'; ++s)
// {
// if (*s == quote_char)
// ++num_quotes;
// else if (*s == '\\')
// ++num_backslashes;
// else if (IS_HIGHBIT_SET(*s))
// {
// int charlen;
// /* Slow path for possible multibyte characters */
// charlen = pg_encoding_mblen(conn->client_encoding, s);
// /* Multibyte character overruns allowable length. */
// if ((s - str) + charlen > len || memchr(s, 0, charlen) != NULL)
// {
// printfPQExpBuffer(&conn->errorMessage,
// libpq_gettext("incomplete multibyte character\n"));
// return NULL;
// }
// /* Adjust s, bearing in mind that for loop will increment it. */
// s += charlen - 1;
// }
// }
// /* Allocate output buffer. */
// input_len = s - str;
// result_size = input_len + num_quotes + 3; /* two quotes, plus a NUL */
// if (!as_ident && num_backslashes > 0)
// result_size += num_backslashes + 2;
// result = rp = (char *) malloc(result_size);
// if (rp == NULL)
// {
// printfPQExpBuffer(&conn->errorMessage,
// libpq_gettext("out of memory\n"));
// return NULL;
// }
// /*
// * If we are escaping a literal that contains backslashes, we use the
// * escape string syntax so that the result is correct under either value
// * of standard_conforming_strings. We also emit a leading space in this
// * case, to guard against the possibility that the result might be
// * interpolated immediately following an identifier.
// */
// if (!as_ident && num_backslashes > 0)
// {
// *rp++ = ' ';
// *rp++ = 'E';1
// }
// /* Opening quote. */
// *rp++ = quote_char;
// /*
// * Use fast path if possible.
// *
// * We've already verified that the input string is well-formed in the
// * current encoding. If it contains no quotes and, in the case of
// * literal-escaping, no backslashes, then we can just copy it directly to
// * the output buffer, adding the necessary quotes.
// *
// * If not, we must rescan the input and process each character
// * individually.
// */
// if (num_quotes == 0 && (num_backslashes == 0 || as_ident))
// {
// memcpy(rp, str, input_len);
// rp += input_len;
// }
// else
// {
// for (s = str; s - str < input_len; ++s)
// {
// if (*s == quote_char || (!as_ident && *s == '\\'))
// {
// *rp++ = *s;
// *rp++ = *s;
// }
// else if (!IS_HIGHBIT_SET(*s))
// *rp++ = *s;
// else
// {
// int i = pg_encoding_mblen(conn->client_encoding, s);
// while (1)
// {
// *rp++ = *s;
// if (--i == 0)
// break;
// ++s; /* for loop will provide the final increment */
// }
// }
// }
// }
// /* Closing quote and terminating NUL. */
// *rp++ = quote_char;
// *rp = '\0';
// return result;
//}
bool identNeedsQuotes(QString ident) bool identNeedsQuotes(QString ident)
{ {
@ -66,7 +241,7 @@ QString genFQTableName(const PgDatabaseCatalog &catalog, const PgClass &cls)
{ {
auto ns = catalog.namespaces()->getByKey(cls.relnamespace); auto ns = catalog.namespaces()->getByKey(cls.relnamespace);
return genSchemaPrefix(ns) % quoteIdent(cls.name); return genSchemaPrefix(*ns) % quoteIdent(cls.name);
} }
QString genAlterTable(const PgDatabaseCatalog &catalog, const PgClass &cls) QString genAlterTable(const PgDatabaseCatalog &catalog, const PgClass &cls)
@ -76,8 +251,8 @@ QString genAlterTable(const PgDatabaseCatalog &catalog, const PgClass &cls)
QString getDropConstraintDefinition(const PgDatabaseCatalog &catalog, const PgConstraint &constraint) QString getDropConstraintDefinition(const PgDatabaseCatalog &catalog, const PgConstraint &constraint)
{ {
PgClass cls = catalog.classes()->getByKey(constraint.relid); const PgClass *cls = catalog.classes()->getByKey(constraint.relid);
return genAlterTable(catalog, cls) % " DROP CONSTRAINT " % quoteIdent(constraint.name) % ";"; return genAlterTable(catalog, *cls) % " DROP CONSTRAINT " % quoteIdent(constraint.name) % ";";
} }
@ -101,7 +276,7 @@ QString getColumnNameList(const PgDatabaseCatalog &catalog, Oid relid, const Sma
const auto ac = catalog.attributes(); const auto ac = catalog.attributes();
for (auto an : attnums) { for (auto an : attnums) {
result.add(ac->getByKey({ relid, an }).name); result.add(ac->getByKey({ relid, an })->name);
} }
return result.str(); return result.str();
} }
@ -109,7 +284,7 @@ QString getColumnNameList(const PgDatabaseCatalog &catalog, Oid relid, const Sma
QString getForeignKeyConstraintDefinition(const PgDatabaseCatalog &catalog, const PgConstraint &constraint) QString getForeignKeyConstraintDefinition(const PgDatabaseCatalog &catalog, const PgConstraint &constraint)
{ {
//PgClass cls = catalog.classes()->getByKey(constraint.relid); //PgClass cls = catalog.classes()->getByKey(constraint.relid);
PgClass fcls = catalog.classes()->getByKey(constraint.frelid); const PgClass *fcls = catalog.classes()->getByKey(constraint.frelid);
QString deferrable; QString deferrable;
QString validated; QString validated;
if (!constraint.validated) if (!constraint.validated)
@ -120,7 +295,7 @@ QString getForeignKeyConstraintDefinition(const PgDatabaseCatalog &catalog, cons
return "\n FOREIGN KEY (" return "\n FOREIGN KEY ("
% getColumnNameList(catalog, constraint.relid, constraint.key) % ")\n REFERENCES " % getColumnNameList(catalog, constraint.relid, constraint.key) % ")\n REFERENCES "
% genFQTableName(catalog, fcls) % " (" % genFQTableName(catalog, *fcls) % " ("
% getColumnNameList(catalog, constraint.frelid, constraint.fkey) % ")\n MATCH " % getColumnNameList(catalog, constraint.frelid, constraint.fkey) % ")\n MATCH "
% ForeignKeyMatchToString(constraint.fmatchtype) % ForeignKeyMatchToString(constraint.fmatchtype)
% " ON UPDATE " % ForeignKeyActionToString(constraint.fupdtype) % " ON UPDATE " % ForeignKeyActionToString(constraint.fupdtype)
@ -130,7 +305,7 @@ QString getForeignKeyConstraintDefinition(const PgDatabaseCatalog &catalog, cons
QString getForeignKeyConstraintReferences(const PgDatabaseCatalog &catalog, const PgConstraint &constraint) QString getForeignKeyConstraintReferences(const PgDatabaseCatalog &catalog, const PgConstraint &constraint)
{ {
PgClass fcls = catalog.classes()->getByKey(constraint.frelid); const PgClass *fcls = catalog.classes()->getByKey(constraint.frelid);
QString deferrable; QString deferrable;
QString validated; QString validated;
if (!constraint.validated) if (!constraint.validated)
@ -140,7 +315,7 @@ QString getForeignKeyConstraintReferences(const PgDatabaseCatalog &catalog, cons
} }
return "REFERENCES " return "REFERENCES "
% genFQTableName(catalog, fcls) % " (" % genFQTableName(catalog, *fcls) % " ("
% getColumnNameList(catalog, constraint.frelid, constraint.fkey) % ") MATCH " % getColumnNameList(catalog, constraint.frelid, constraint.fkey) % ") MATCH "
% ForeignKeyMatchToString(constraint.fmatchtype) % ForeignKeyMatchToString(constraint.fmatchtype)
% " ON UPDATE " % ForeignKeyActionToString(constraint.fupdtype) % " ON UPDATE " % ForeignKeyActionToString(constraint.fupdtype)
@ -152,7 +327,7 @@ QString getForeignKeyConstraintReferences(const PgDatabaseCatalog &catalog, cons
QString getForeignKeyConstraintReferencesShort(const PgDatabaseCatalog &catalog, const PgConstraint &constraint) QString getForeignKeyConstraintReferencesShort(const PgDatabaseCatalog &catalog, const PgConstraint &constraint)
{ {
PgClass fcls = catalog.classes()->getByKey(constraint.frelid); const PgClass *fcls = catalog.classes()->getByKey(constraint.frelid);
QString deferrable; QString deferrable;
QString validated; QString validated;
if (!constraint.validated) if (!constraint.validated)
@ -164,7 +339,7 @@ QString getForeignKeyConstraintReferencesShort(const PgDatabaseCatalog &catalog,
QString on_delete = constraint.fdeltype == ForeignKeyAction::NoAction ? QString() : " ON DELETE " % ForeignKeyActionToString(constraint.fdeltype); QString on_delete = constraint.fdeltype == ForeignKeyAction::NoAction ? QString() : " ON DELETE " % ForeignKeyActionToString(constraint.fdeltype);
QString match_type = constraint.fmatchtype == ForeignKeyMatch::Simple ? QString() : " MATCH " % ForeignKeyMatchToString(constraint.fmatchtype); QString match_type = constraint.fmatchtype == ForeignKeyMatch::Simple ? QString() : " MATCH " % ForeignKeyMatchToString(constraint.fmatchtype);
return genFQTableName(catalog, fcls) % " (" return genFQTableName(catalog, *fcls) % " ("
% getColumnNameList(catalog, constraint.frelid, constraint.fkey) % ")" % getColumnNameList(catalog, constraint.frelid, constraint.fkey) % ")"
% match_type % match_type
% on_update % on_update
@ -191,10 +366,10 @@ QString getUniqueConstraintDefinition(const PgDatabaseCatalog &catalog, const Pg
QString getConstraintDefinition(const PgDatabaseCatalog &catalog, const PgConstraint &constraint) QString getConstraintDefinition(const PgDatabaseCatalog &catalog, const PgConstraint &constraint)
{ {
PgClass cls = catalog.classes()->getByKey(constraint.relid); const PgClass *cls = catalog.classes()->getByKey(constraint.relid);
// return genAlterTable(catalog, cls) % " ADD CONSTRAINT " // return genAlterTable(catalog, cls) % " ADD CONSTRAINT "
// % quoteIdent(constraint.name) % " " % constraint.definition % ";"; // % quoteIdent(constraint.name) % " " % constraint.definition % ";";
QString result = genAlterTable(catalog, cls) % "\n ADD CONSTRAINT " QString result = genAlterTable(catalog, *cls) % "\n ADD CONSTRAINT "
% quoteIdent(constraint.name); % quoteIdent(constraint.name);
switch (constraint.type) { switch (constraint.type) {
case ConstraintType::ForeignKey: case ConstraintType::ForeignKey:
@ -216,8 +391,8 @@ QString getConstraintDefinition(const PgDatabaseCatalog &catalog, const PgConstr
QString getIndexDefinition(const PgDatabaseCatalog &catalog, const PgIndex &index) QString getIndexDefinition(const PgDatabaseCatalog &catalog, const PgIndex &index)
{ {
PgClass table_class = catalog.classes()->getByKey(index.relid); // const PgClass *table_class = catalog.classes()->getByKey(index.relid);
PgClass index_class = catalog.classes()->getByKey(index.indexrelid); // const PgClass *index_class = catalog.classes()->getByKey(index.indexrelid);
return index.definition + ";"; return index.definition + ";";
@ -267,8 +442,8 @@ QString getIndexDefinition(const PgDatabaseCatalog &catalog, const PgIndex &inde
QString getDropIndexDefinition(const PgDatabaseCatalog &catalog, const PgIndex &index) QString getDropIndexDefinition(const PgDatabaseCatalog &catalog, const PgIndex &index)
{ {
PgClass table_class = catalog.classes()->getByKey(index.relid); // const PgClass *table_class = catalog.classes()->getByKey(index.relid);
PgClass index_class = catalog.classes()->getByKey(index.indexrelid); // const PgClass *index_class = catalog.classes()->getByKey(index.indexrelid);
QString result; QString result;
result = "DROP INDEX " result = "DROP INDEX "

View file

@ -8,6 +8,9 @@ class PgConstraint;
class PgDatabaseCatalog; class PgDatabaseCatalog;
class PgIndex; class PgIndex;
QString escapeIdent(const QString &input);
QString escapeLiteral(const QString &input);
bool identNeedsQuotes(QString ident); bool identNeedsQuotes(QString ident);
QString quoteIdent(QString ident); QString quoteIdent(QString ident);

View file

@ -26,22 +26,23 @@ TypeMappingResult TypeMappings::getTypeForOid(Oid oid) const
if (res != m_typeMap.end()) { if (res != m_typeMap.end()) {
QString dbtypename; QString dbtypename;
if (m_types) { if (m_types) {
PgType type = m_types->getByKey(oid); const PgType* type = m_types->getByKey(oid);
dbtypename = type.name; if (type)
dbtypename = type->name;
} }
return { res->second, dbtypename }; return { res->second, dbtypename };
} }
if (m_types) { if (m_types) {
PgType type = m_types->getByKey(oid); const PgType *type = m_types->getByKey(oid);
// Found a valid type? elem is set? then it is array type // Found a valid type? elem is set? then it is array type
if (type.oid != InvalidOid && type.elem != InvalidOid) { if (type && type->elem != InvalidOid) {
// Lookup what the element type is and wrap the mapping for that in the standard container type // Lookup what the element type is and wrap the mapping for that in the standard container type
// for the language config. If that isn't right the end user should create a specific mapping for // for the language config. If that isn't right the end user should create a specific mapping for
// that array type. // that array type.
res = m_typeMap.find(type.elem); res = m_typeMap.find(type->elem);
PgType elem_type = m_types->getByKey(type.elem); const PgType *elem_type = m_types->getByKey(type->elem);
QString type_string; QString type_string;
if (res == m_typeMap.end()) { if (res == m_typeMap.end()) {
type_string = m_defaultStringType; type_string = m_defaultStringType;
@ -51,11 +52,11 @@ TypeMappingResult TypeMappings::getTypeForOid(Oid oid) const
} }
return { return {
QString(m_defaultContainerType).arg(type_string), QString(m_defaultContainerType).arg(type_string),
elem_type.name + "[]" elem_type->name + "[]"
}; };
} }
else { else {
return { m_defaultStringType, type.name }; return { m_defaultStringType, type->name };
} }
} }
// We shouldn't get here unless m_types is empty // We shouldn't get here unless m_types is empty

View file

@ -67,7 +67,10 @@ codebuilder/StructureTemplate.cpp \
PgTrigger.cpp \ PgTrigger.cpp \
PgTriggerContainer.cpp \ PgTriggerContainer.cpp \
PgProc.cpp \ PgProc.cpp \
PgProcContainer.cpp PgProcContainer.cpp \
PgSchemaObject.cpp \
PgDatabaseObject.cpp \
PgServerObject.cpp
HEADERS += \ HEADERS += \
Pglablib.h \ Pglablib.h \
@ -117,7 +120,10 @@ codebuilder/StructureTemplate.h \
PgTrigger.h \ PgTrigger.h \
PgTriggerContainer.h \ PgTriggerContainer.h \
PgProc.h \ PgProc.h \
PgProcContainer.h PgProcContainer.h \
PgSchemaObject.h \
PgDatabaseObject.h \
PgServerObject.h
unix { unix {
target.path = /usr/lib target.path = /usr/lib

View file

@ -55,17 +55,17 @@ TEST_F(TypeMappingsTest, int4overideType)
// Need catalogue for the next test // Need catalogue for the next test
// Maybe we should mock this !? // Maybe we should mock this !?
TEST_F(TypeMappingsTest, int4arrayType) //TEST_F(TypeMappingsTest, int4arrayType)
{ //{
auto types= std::make_shared<PgTypeContainer>(); // auto types= std::make_shared<PgTypeContainer>();
PgType int4arr; // PgType int4arr;
int4arr.oid = Pgsql::int4_array_oid; // int4arr.oid = Pgsql::int4_array_oid;
int4arr.elem = Pgsql::int4_oid; // int4arr.elem = Pgsql::int4_oid;
types->add(int4arr); // types->add(int4arr);
tm.setTypes(types); // tm.setTypes(types);
QString result = tm.getTypeForOid(Pgsql::int4_array_oid).codeType(); // QString result = tm.getTypeForOid(Pgsql::int4_array_oid).codeType();
ASSERT_EQ(result, "std::vector<int>"); // ASSERT_EQ(result, "std::vector<int>");
} //}