Reorganize files in pglablib

The enitities and containers of the catalog now go into catalog subfolder
Models go into model
This commit is contained in:
eelke 2018-12-16 10:17:59 +01:00
parent 56cbeea183
commit f0c1035378
121 changed files with 226 additions and 183 deletions

View file

@ -0,0 +1,6 @@
#include "PgAm.h"
PgAm::PgAm()
{
}

19
pglablib/catalog/PgAm.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef PGAM_H
#define PGAM_H
#include "Pgsql_declare.h"
#include <QString>
class PgAm {
public:
Oid oid;
QString name;
PgAm();
bool operator==(Oid rhs) const { return oid == rhs; }
bool operator<(Oid rhs) const { return oid < rhs; }
bool operator<(const PgAm &rhs) const { return oid < rhs.oid; }
};
#endif // PGAM_H

View file

@ -0,0 +1,17 @@
#include "PgAmContainer.h"
#include "Pgsql_Col.h"
std::string PgAmContainer::getLoadQuery() const
{
std::string q = "SELECT oid, amname FROM pg_am";
return q;
}
PgAm PgAmContainer::loadElem(const Pgsql::Row &row)
{
Pgsql::Col col(row);
PgAm v;
col >> v.oid >> v.name;
return v;
}

View file

@ -0,0 +1,18 @@
#ifndef PGAMCONTAINER_H
#define PGAMCONTAINER_H
#include "PgContainer.h"
#include "PgAm.h"
#include "Pgsql_declare.h"
#include <vector>
class PgAmContainer : public PgContainer<PgAm> {
public:
using PgContainer<PgAm>::PgContainer;
virtual std::string getLoadQuery() const override;
protected:
virtual PgAm loadElem(const Pgsql::Row &row) override;
};
#endif // PGAMCONTAINER_H

View file

@ -0,0 +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->objectName();
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

@ -0,0 +1,46 @@
#ifndef PGATTRIBUTE_H
#define PGATTRIBUTE_H
#include "Pgsql_declare.h"
#include <QString>
#include <libpq-fe.h>
#include <tuple>
class PgClass;
class PgDatabaseCatalog;
class PgAttribute {
public:
using Key = std::tuple<Oid, int16_t>;
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;
char identity = ' ';
bool isdropped = false;
bool islocal = true;
Oid collation = InvalidOid;
QString acl;
QString options;
QString defaultValue; ///< Comes from pg_attrdef table
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); }
/// 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

@ -0,0 +1,44 @@
#include "PgAttributeContainer.h"
#include "Pgsql_Col.h"
#include "PgDatabaseCatalog.h"
//SELECT attname, pg_get_expr(adbin, adrelid) AS def_value
//FROM pg_attribute
// JOIN pg_attrdef ON attrelid=adrelid AND attnum=adnum
//WHERE atthasdef=true
std::string PgAttributeContainer::getLoadQuery() const
{
std::string q = R"__(
SELECT attrelid, attname, atttypid, attstattarget,
attnum, attndims, atttypmod, attnotnull, atthasdef, attisdropped,
attislocal, 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)
{
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.isdropped
>> v.islocal >> v.collation >> v.acl >> v.options >> v.defaultValue;
if (m_catalog.serverVersion() >= 100000)
col >> v.identity;
return v;
}
std::vector<PgAttribute> PgAttributeContainer::getColumnsForRelation(Oid oid) const
{
std::vector<PgAttribute> result;
for (const auto &e : m_container)
if (e.relid == oid)
result.push_back(e);
return result;
}

View file

@ -0,0 +1,20 @@
#ifndef PGATTRIBUTECONTAINER_H
#define PGATTRIBUTECONTAINER_H
#include "PgContainer.h"
#include "PgAttribute.h"
#include "Pgsql_declare.h"
#include <vector>
class PgAttributeContainer : public PgContainer<PgAttribute, PgAttribute::Key> {
public:
using PgContainer<PgAttribute, PgAttribute::Key>::PgContainer;
virtual std::string getLoadQuery() const override;
std::vector<PgAttribute> getColumnsForRelation(Oid oid) const;
protected:
PgAttribute loadElem(const Pgsql::Row &row) override;
};
#endif // PGATTRIBUTECONTAINER_H

View file

@ -0,0 +1,3 @@
#include "PgAuthId.h"
PgAuthId::PgAuthId() = default;

View file

@ -0,0 +1,32 @@
#ifndef PGAUTHID_H
#define PGAUTHID_H
#include <libpq-fe.h>
#include <QString>
#include <QDateTime>
class PgAuthId {
public:
PgAuthId();
Oid oid = InvalidOid;
QString name;
bool super;
bool inherit;
bool createRole;
bool createDB;
bool canlogin;
bool replication;
bool bypassRls;
int connLimit;
QDateTime validUntil;
bool valid() const { return oid != InvalidOid; }
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 PgAuthId &rhs) const { return oid < rhs.oid; }
};
#endif // PGAUTHID_H

View file

@ -0,0 +1,27 @@
#include "PgAuthIdContainer.h"
#include "Pgsql_Col.h"
std::string PgAuthIdContainer::getLoadQuery() const
{
std::string result =
"SELECT oid, rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, "
" rolcanlogin, rolreplication, rolconnlimit, rolvaliduntil";
if (minimumVersion(90500))
result += ", rolbypassrls";
result += "\n"
"FROM pg_authid";
return result;
}
PgAuthId PgAuthIdContainer::loadElem(const Pgsql::Row &row)
{
Pgsql::Col col(row);
PgAuthId v;
col >> v.oid >> v.name >> v.super >> v.inherit >> v.createRole >> v.createDB
>> v.canlogin >> v.replication >> v.connLimit >> v.validUntil;
if (minimumVersion(90500))
col >> v.bypassRls;
return v;
}

View file

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

View file

@ -0,0 +1,13 @@
#ifndef PGCATALOGTYPES_H
#define PGCATALOGTYPES_H
#include <libpq-fe.h>
#include <vector>
#include <boost/container/small_vector.hpp>
using AttNumVec = std::vector<int16_t>;
template<int size>
using SmallAttNumVec = boost::container::small_vector<int16_t, size>;
using OidVec = std::vector<Oid>;
#endif // PGCATALOGTYPES_H

View file

@ -0,0 +1,136 @@
#include "PgClass.h"
#include "PgAttributeContainer.h"
#include "PgClassContainer.h"
#include "PgDatabaseCatalog.h"
#include "PgConstraintContainer.h"
#include "PgInheritsContainer.h"
#include <QStringBuilder>
#include "SqlFormattingUtils.h"
void operator<<(RelPersistence &s, const Pgsql::Value &v)
{
//s = static_cast<T>(v);
const char *c = v.c_str();
switch (*c) {
case 'p':
s = RelPersistence::Permanent;
break;
case 'u':
s = RelPersistence::Unlogged;
break;
case 't':
s = RelPersistence::Temporary;
break;
}
}
void operator<<(RelKind &s, const Pgsql::Value &v)
{
//s = static_cast<T>(v);
const char *c = v.c_str();
switch (*c) {
case 'r':
s = RelKind::Table;
break;
case 'i':
s = RelKind::Index;
break;
case 'S':
s = RelKind::Sequence;
break;
case 'v':
s = RelKind::View;
break;
case 'm':
s = RelKind::MaterializedView;
break;
case 'c':
s = RelKind::Composite;
break;
case 't':
s = RelKind::Toast;
break;
case 'f':
s = RelKind::ForeignTable;
break;
}
}
//QString PgClass::objectName() const
//{
// return name;
//}
QString PgClass::createSql() const
{
if (createSqlCache.isEmpty()) {
if (kind == RelKind::Table)
createSqlCache = createTableSql();
}
return createSqlCache;
}
QString PgClass::createTableSql() const
{
QString sql;
// CREATE [ TEMP | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name ( [
sql += "CREATE ";
if (persistence == RelPersistence::Unlogged)
sql += "UNLOGGED ";
else if (persistence == RelPersistence::Temporary)
sql += "TEMP ";
sql += "TABLE ";
sql += fullyQualifiedQuotedObjectName();
sql += " (\n ";
auto && cols = catalog().attributes()->getColumnsForRelation(oid());
bool first = true;
for (auto && col : cols) {
if (col.num > 0 && !col.isdropped) {
if (first) {
sql += "\n ";
first = false;
}
else sql += ",\n ";
if (!col.islocal) sql += "-- ";
sql += col.columnDefinition(catalog());
}
// { column_name data_type [ COLLATE collation ] [ column_constraint [ ... ] ]
// | table_constraint
// ] )
}
auto && constraints = catalog().constraints()->getConstraintsForRelation(oid());
for (auto && constraint: constraints) {
if (first) {
sql += "\n ";
first = false;
}
else sql += ",\n ";
sql += getConstraintDefinition(catalog(), constraint);
}
sql += ")";
{
// [ INHERITS ( parent_table [, ... ] ) ]
auto parents = catalog().inherits()->getParentsOf(oid());
if (!parents.empty()) {
sql += "\nINHERITS (";
bool first = true;
for (auto parent_oid : parents) {
if (first) first = false;
else sql += ", ";
sql += catalog().classes()->getByKey(parent_oid)->fullyQualifiedQuotedObjectName();
}
sql += ")";
}
}
// [ PARTITION BY { RANGE | LIST } ( { column_name | ( expression ) } [ COLLATE collation ] [ opclass ] [, ... ] ) ]
// [ WITH ( storage_parameter [= value] [, ... ] ) | WITH OIDS | WITHOUT OIDS ]
// [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
// [ TABLESPACE tablespace_name ]
sql += ";\n";
return sql;
}

View file

@ -0,0 +1,76 @@
#ifndef PGCLASS_H
#define PGCLASS_H
#include "Pgsql_Value.h"
#include "PgNamespaceObject.h"
#include "PgOwnedObject.h"
#include <QString>
#include <libpq-fe.h>
enum class RelPersistence {
Permanent, // p
Unlogged, // u
Temporary // t
};
void operator<<(RelPersistence &s, const Pgsql::Value &v);
enum class RelKind {
Table, // r
Index, // i
Sequence, // S
View, // v
MaterializedView, // m
Composite, // c
Toast, // t
ForeignTable // f
};
void operator<<(RelKind &s, const Pgsql::Value &v);
class PgClass: public PgNamespaceObject, public PgOwnedObject {
public:
// Oid oid = InvalidOid;
// QString name;
// Oid relnamespace = InvalidOid;
// QString relnamespace_name; // Transient, cached value from relnamespace
// bool system_namespace = false; // Transient, cached value from relnamespace
Oid type = InvalidOid;
Oid oftype = InvalidOid;
//Oid owner = InvalidOid;
Oid am = InvalidOid;
Oid filenode = InvalidOid;
Oid tablespace = InvalidOid;
int32_t pages_est = 0;
float tuples_est = 0.0f;
Oid toastrelid = InvalidOid;
bool isshared = false;
RelPersistence persistence;
RelKind kind;
bool hasoids = false;
bool ispopulated;
int frozenxid;
int minmxid;
QString acl;
std::vector<QString> options;
using PgNamespaceObject::PgNamespaceObject;
// virtual QString objectName() const override;
// bool operator==(Oid _oid) const { return oid == _oid; }
// bool operator==(const QString &n) const { return objectName() == n; }
// bool operator<(Oid _oid) const { return oid < _oid; }
// bool operator<(const PgClass &rhs) const { return oid < rhs.oid; }
QString createSql() const;
private:
mutable QString createSqlCache;
QString createTableSql() const;
};
#endif // PGCLASS_H

View file

@ -0,0 +1,39 @@
#include "PgClassContainer.h"
#include "Pgsql_Connection.h"
#include "Pgsql_Col.h"
#include "PgDatabaseCatalog.h"
#include "PgNamespaceContainer.h"
#include <iterator>
std::string PgClassContainer::getLoadQuery() const
{
return "SELECT oid, relname, relnamespace, reltype, reloftype, "
" relowner, relam, relfilenode, reltablespace, relpages, "
" reltuples, reltoastrelid, relisshared, relpersistence, "
" relkind, relhasoids, relispopulated, relfrozenxid, relminmxid, "
" relacl, reloptions \n"
"FROM pg_catalog.pg_class";
}
PgClass PgClassContainer::loadElem(const Pgsql::Row &row)
{
Pgsql::Col col(row);
Oid class_oid = col.nextValue();
QString name = col.nextValue();
Oid schema_oid = col.nextValue();
PgClass v(m_catalog, class_oid, name, schema_oid);
Oid owner ;
col >> v.type >> v.oftype
>> 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;
v.setOwnerOid(m_catalog, owner);
// auto&& ns = m_catalog.namespaces()->getByKey(v.relnamespace);
// if (ns) {
// v.relnamespace_name = ns->objectName();
// v.system_namespace = ns->isSystemCatalog();
// }
return v;
}

View file

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

View file

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

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

@ -0,0 +1,169 @@
#include "PgConstraint.h"
void operator<<(ConstraintType &s, const Pgsql::Value &v)
{
const char *c = v.c_str();
switch (*c) {
case 'c':
s = ConstraintType::Check;
break;
case 'f':
s = ConstraintType::ForeignKey;
break;
case 'p':
s = ConstraintType::PrimaryKey;
break;
case 'u':
s = ConstraintType::Unique;
break;
case 't':
s = ConstraintType::ConstraintTrigger;
break;
case 'x':
s = ConstraintType::ExclusionConstraint;
break;
}
}
QString ShortNameForConstraintType(ConstraintType ct)
{
QString s;
switch (ct) {
case ConstraintType::Check:
s = "C";
break;
case ConstraintType::ForeignKey:
s = "FK";
break;
case ConstraintType::PrimaryKey:
s = "PK";
break;
case ConstraintType::Unique:
s = "U";
break;
case ConstraintType::ConstraintTrigger:
s = "CT";
break;
case ConstraintType::ExclusionConstraint:
s = "XC";
break;
default:
s = "?";
break;
}
return s;
}
QString LongNameForConstraintType(ConstraintType ct)
{
QString s;
switch (ct) {
case ConstraintType::Check:
s = "check";
break;
case ConstraintType::ForeignKey:
s = "foreign key";
break;
case ConstraintType::PrimaryKey:
s = "primary key";
break;
case ConstraintType::Unique:
s = "unique";
break;
case ConstraintType::ConstraintTrigger:
s = "constraint trigger";
break;
case ConstraintType::ExclusionConstraint:
s = "exclusion constraint";
break;
default:
s = "?";
break;
}
return s;
}
void operator<<(ForeignKeyAction &s, const Pgsql::Value &v)
{
const char *c = v.c_str();
switch (*c) {
case 'a':
s = ForeignKeyAction::NoAction;
break;
case 'r':
s = ForeignKeyAction::Restrict;
break;
case 'c':
s = ForeignKeyAction::Cascade;
break;
case 'n':
s = ForeignKeyAction::SetNull;
break;
case 'd':
s = ForeignKeyAction::SetDefault;
break;
}
}
QString ForeignKeyActionToString(ForeignKeyAction fka)
{
QString result;
switch (fka) {
case ForeignKeyAction::NoAction:
result = "NO ACTION";
break;
case ForeignKeyAction::Restrict:
result = "RESTRICT";
break;
case ForeignKeyAction::Cascade:
result = "CASCADE";
break;
case ForeignKeyAction::SetNull:
result = "SET NULL";
break;
case ForeignKeyAction::SetDefault:
result = "SET DEFAULT";
break;
}
return result;
}
void operator<<(ForeignKeyMatch &s, const Pgsql::Value &v)
{
const char *c = v.c_str();
switch (*c) {
case 'f':
s = ForeignKeyMatch::Full;
break;
case 'p':
s = ForeignKeyMatch::Partial;
break;
case 's':
s = ForeignKeyMatch::Simple;
break;
}
}
QString ForeignKeyMatchToString(ForeignKeyMatch fkm)
{
QString result;
switch (fkm) {
case ForeignKeyMatch::Full :
result = "FULL";
break;
case ForeignKeyMatch::Partial:
result = "PARTIAL";
break;
case ForeignKeyMatch::Simple:
result = "SIMPLE";
break;
}
return result;
}
//PgConstraint::PgConstraint()
//{
//}

View file

@ -0,0 +1,81 @@
#ifndef PGCONSTRAINT_H
#define PGCONSTRAINT_H
#include "PgNamespaceObject.h"
#include "Pgsql_Value.h"
#include "PgCatalogTypes.h"
#include <QString>
#include <libpq-fe.h>
#include <cstdint>
#include <vector>
enum class ConstraintType {
PrimaryKey, // p
ForeignKey, // f
Unique, // u
Check, // c
ConstraintTrigger, // t
ExclusionConstraint, // x
};
void operator<<(ConstraintType &s, const Pgsql::Value &v);
QString ShortNameForConstraintType(ConstraintType ct);
QString LongNameForConstraintType(ConstraintType ct);
enum class ForeignKeyAction {
NoAction, // a
Restrict, // r
Cascade, // c
SetNull, // n
SetDefault // d
};
void operator<<(ForeignKeyAction &s, const Pgsql::Value &v);
QString ForeignKeyActionToString(ForeignKeyAction fka);
enum class ForeignKeyMatch {
Full, // f
Partial, // p
Simple // s
};
void operator<<(ForeignKeyMatch &s, const Pgsql::Value &v);
QString ForeignKeyMatchToString(ForeignKeyMatch fkm);
class PgConstraint: public PgNamespaceObject {
public:
ConstraintType type;
bool deferrable;
bool deferred;
bool validated;
Oid relid = InvalidOid; ///< the table this constraint is on
Oid typid = InvalidOid;
Oid indid = InvalidOid; ///< index supporting the constraint
Oid frelid = InvalidOid; ///< only for FK, referenced table pg_class
ForeignKeyAction fupdtype; // on update
ForeignKeyAction fdeltype; // on delete
ForeignKeyMatch fmatchtype; // match type
bool islocal;
int32_t inhcount;
bool noinherit;
SmallAttNumVec<5> key; // list of constraint columns attnum
SmallAttNumVec<5> fkey; // fkey list of referenced columns
OidVec pfeqop;
OidVec ppeqop;
OidVec ffeqop;
OidVec exclop;
QString bin;
QString src;
QString definition;
using PgNamespaceObject::PgNamespaceObject;
};
#endif // PGCONSTRAINT_H

View file

@ -0,0 +1,75 @@
#include "PgConstraintContainer.h"
#include "Pgsql_Col.h"
#include <algorithm>
std::string PgConstraintContainer::getLoadQuery() const
{
std::string q = R"__(
SELECT oid, conname, connamespace, contype, condeferrable,
condeferred, convalidated, conrelid, contypid, conindid,
confrelid, confupdtype, confdeltype, confmatchtype,
conislocal, coninhcount, connoinherit, conkey, confkey,
conpfeqop, conppeqop, conffeqop, conexclop, conbin, consrc,
pg_get_constraintdef(oid)
FROM pg_constraint)__";
// auto cat = m_catalog.lock();
// if (cat && cat->serverVersion() >= 90400)
// q += ", indisreplident ";
// q += "\nFROM pg_index";
return q;
}
PgConstraint PgConstraintContainer::loadElem(const Pgsql::Row &row)
{
using namespace Pgsql;
Col col(row);
Oid oid = col.nextValue();
QString name = col.nextValue();
Oid ns_oid = col.nextValue();
PgConstraint v(m_catalog, oid, name, ns_oid);
col >> v.type >> v.deferrable
>> v.deferred >> v.validated >> v.relid >> v.typid >> v.indid
>> v.frelid >> v.fupdtype >> v.fdeltype >> v.fmatchtype
>> v.islocal >> v.inhcount >> v.noinherit
>> v.key >> v.fkey >> v.pfeqop >> v.ppeqop >> v.ffeqop >> v.exclop
>> v.bin >> v.src >> v.definition;
return v;
}
std::vector<PgConstraint> PgConstraintContainer::getFKeyForTableColumn(Oid relid, int16_t attnum) const
{
//const PgConstraint *result = nullptr;
std::vector<PgConstraint> result;
// WHat do we want to find here? On ly single column constraints or all contstraints.
auto res = std::copy_if(m_container.begin(), m_container.end(), std::back_inserter(result),
[relid, attnum] (const auto &c) {
// the find on v.key may not look super efficient but remember it in general only has one or two elements.
return c.type == ConstraintType::ForeignKey && relid == c.relid &&
(std::find(c.key.begin(), c.key.end(), attnum) != c.key.end());
});
return result;
}
std::vector<PgConstraint> PgConstraintContainer::getConstraintsForRelation(Oid relid) const
{
std::vector<PgConstraint> result;
for (const auto &e : m_container)
if (e.relid == relid)
result.push_back(e);
return result;
}
std::optional<PgConstraint> PgConstraintContainer::getPrimaryForRelation(Oid relid) const
{
std::optional<PgConstraint> result;
for (const auto &e : m_container) {
if (e.relid == relid && e.type == ConstraintType::PrimaryKey) {
result = e;
break;
}
}
return result;
}

View file

@ -0,0 +1,25 @@
#ifndef PGCONSTRAINTCONTAINER_H
#define PGCONSTRAINTCONTAINER_H
#include "PgContainer.h"
#include "PgConstraint.h"
#include "Pgsql_declare.h"
#include <vector>
#include <optional>
class PgConstraintContainer : public PgContainer<PgConstraint> {
public:
using PgContainer<PgConstraint>::PgContainer;
virtual std::string getLoadQuery() const override;
//std::vector<PgConstraint> getIndexesForTable(Oid table_oid) const;
std::vector<PgConstraint> getFKeyForTableColumn(Oid relid, int16_t attnum) const;
std::vector<PgConstraint> getConstraintsForRelation(Oid relid) const;
std::optional<PgConstraint> getPrimaryForRelation(Oid relid) const;
protected:
virtual PgConstraint loadElem(const Pgsql::Row &row) override;
};
#endif // PGCONSTRAINTCONTAINER_H

View file

@ -0,0 +1,11 @@
#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

@ -0,0 +1,204 @@
#ifndef PGCONTAINER_H
#define PGCONTAINER_H
#include "Pgsql_declare.h"
#include "Pgsql_Result.h"
#include <QString>
#include <memory>
#include <vector>
#include <libpq-fe.h>
class PgDatabaseCatalog;
class IPgContainer {
public:
IPgContainer(PgDatabaseCatalog& cat);
virtual ~IPgContainer() = default;
virtual std::string getLoadQuery() const = 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>
class PgContainer: public IPgContainer {
public:
using t_Container = std::vector<T>; ///< Do not assume it will stay a vector only expect bidirectional access
PgContainer() = default;
explicit PgContainer(PgDatabaseCatalog& cat)
: IPgContainer(cat)
{
}
typename t_Container::const_iterator begin() const
{
return m_container.begin();
}
typename t_Container::const_iterator end() const
{
return m_container.end();
}
void clear()
{
m_container.clear();
}
size_t count() const
{
return m_container.size();
}
const T* getByKey(const K &key) const
{
auto lb_result = std::lower_bound(m_container.begin(), m_container.end(), key);
if (lb_result != m_container.end() && *lb_result == key)
return &*lb_result;
return nullptr;
}
const T* getByName(const QString &name) const
{
auto find_res = std::find(m_container.begin(), m_container.end(), name);
if (find_res != m_container.end())
return &*find_res;
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
{
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());
}
// Meant for mocking during testing
void add(const T &elem)
{
m_container.push_back(elem);
std::sort(m_container.begin(), m_container.end());
}
protected:
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 &) = 0;
};
template<typename T, typename K=Oid>
class PgSPtrContainer: public IPgContainer {
public:
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
explicit PgSPtrContainer(std::weak_ptr<PgDatabaseCatalog> cat)
: m_catalog(cat)
{}
typename t_Container::const_iterator begin() const
{
return m_container.begin();
}
typename t_Container::const_iterator end() const
{
return m_container.end();
}
void clear()
{
m_container.clear();
}
int count() const
{
return (int)m_container.size();
}
const t_Elem getByKey(const K &key) const
{
auto lb_result = std::lower_bound(m_container.begin(), m_container.end(), key);
if (lb_result != m_container.end() && **lb_result == key)
return *lb_result;
return nullptr;
}
const t_Elem getByName(const QString name) const
{
auto find_res = std::find_if(m_container.begin(), m_container.end(),
[name](auto e) -> bool { return *e = name; } );
if (find_res != m_container.end())
return *find_res;
return nullptr;
}
const t_Elem getByIdx(int idx) const
{
return m_container.at(idx);
}
/** Override to implement complete loading logic.
*
* Do not override this function if you only want to implement
* the loading of a single element. Override loadElem instead.
*/
virtual void load(const Pgsql::Result &res) override
{
m_container.clear();
m_container.reserve(res.rows());
for (auto row : res)
m_container.push_back(loadElem(row));
std::sort(m_container.begin(), m_container.end());
}
protected:
std::weak_ptr<PgDatabaseCatalog> m_catalog;
t_Container m_container;
/** Override the implementation for this function to implement loading of single row.
*
* When overriding this function there is no need to override load.
*/
virtual t_Elem loadElem(const Pgsql::Row &) = 0;
private:
};
#endif // PGCONTAINER_H

View file

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

View file

@ -0,0 +1,33 @@
#ifndef PGDATABASE_H
#define PGDATABASE_H
#include "PgServerObject.h"
#include <QString>
#include <libpq-fe.h>
class PgDatabase: public PgServerObject {
public:
// Oid oid = InvalidOid;
// QString name;
Oid dba; // owner?
int encoding;
QString collate;
QString ctype;
bool isTemplate;
bool allowConn;
int connLimit;
Oid tablespace;
QString acl;//"ARRAY";"YES"
using PgServerObject::PgServerObject;
bool isValid() const { return oid() != InvalidOid; }
// bool operator==(Oid _oid) const { return oid() == _oid; }
// bool operator==(const QString &n) const { return objectName() == n; }
// bool operator<(Oid _oid) const { return oid() < _oid; }
// bool operator<(const PgDatabase &rhs) const { return oid() < rhs.oid(); }
};
#endif // PGDATABASE_H

View file

@ -0,0 +1,304 @@
#include "PgDatabaseCatalog.h"
#include "ASyncDBConnection.h"
#include "PgAmContainer.h"
#include "PgAttributeContainer.h"
#include "PgAuthIdContainer.h"
#include "PgClassContainer.h"
#include "PgConstraintContainer.h"
#include "PgDatabaseContainer.h"
#include "PgIndexContainer.h"
#include "PgNamespaceContainer.h"
#include "PgTablespaceContainer.h"
#include "PgTriggerContainer.h"
#include "PgTypeContainer.h"
#include "PgProcContainer.h"
#include "PgCollationContainer.h"
#include "PgInheritsContainer.h"
#include "Pgsql_Connection.h"
#include "Pgsql_oids.h"
#include <QThread>
#include <boost/timer/timer.hpp>
#include <boost/chrono/system_clocks.hpp>
using namespace Pgsql;
QString getRoleNameFromOid(const PgDatabaseCatalog &cat, Oid oid)
{
QString name;
auto auth_ids = cat.authIds();
if (auth_ids) {
const PgAuthId* auth_id = auth_ids->getByKey(oid);
if (auth_id) {
name = auth_id->name;
}
}
return name;
}
QString getRoleDisplayString(const PgDatabaseCatalog &cat, Oid oid)
{
QString name = getRoleNameFromOid(cat, oid);
return name;
}
QString getClassDisplayString(const PgDatabaseCatalog &cat, Oid oid)
{
QString result;
auto l = cat.classes();
auto e = l->getByKey(oid);
if (e)
result = e->objectName();
return result;
}
QString getIndexDisplayString(const PgDatabaseCatalog &cat, Oid oid)
{
QString result;
// auto l = cat.indexes();
// auto e = l->getByKey(oid);
// if (e)
result = getClassDisplayString(cat, oid);
return result;
}
QString getTablespaceDisplayString(const PgDatabaseCatalog &cat, Oid oid)
{
// TODO load list and lookup name
if (oid == 0) {
auto dbname = cat.getDBName();
oid = cat.databases()->getByName(dbname)->tablespace;
auto ts = cat.tablespaces()->getByKey(oid);
return ts->name + " (inherited)";
}
else {
auto ts = cat.tablespaces()->getByKey(oid);
return ts->name;
}
}
QString getTypeDisplayString(const PgDatabaseCatalog &cat, Oid oid, int32_t typmod)
{
if (oid == 0) {
return QString();
}
auto tc = cat.types();
auto t = tc->getByKey(oid);
if (t == nullptr) {
return "(invalid/unknown)";
}
QString s;
if (t->category == TypCategory::Array) {
// auto et = tc->getByKey(t.elem);
// s = et.name;
s = getTypeDisplayString(cat, t->elem, typmod);
s += "[]";
}
else {
s = t->objectName();
switch (oid) {
case varchar_oid:
case char_oid:
case text_oid:
if (typmod > 4)
s += QString::asprintf("(%d)", typmod-4);
break;
case numeric_oid:
if (typmod > 4) {
int prec = (typmod - 4) / 65536;
int scale = (typmod - 4) % 65536;
if (scale > 0)
s += QString::asprintf("(%d,%d)", prec, scale);
else
s += QString::asprintf("(%d)", prec);
}
break;
}
}
return s;
}
PgDatabaseCatalog::PgDatabaseCatalog()
{
}
PgDatabaseCatalog::~PgDatabaseCatalog()
{
}
void PgDatabaseCatalog::loadAll(Pgsql::Connection &conn,
std::function<bool(int, int)> progress_callback)
{
loadInfo(conn);
const int count = 12;
int n = 0;
if (progress_callback && !progress_callback(++n, count))
return;
// First load server objects
load2(m_authIds, conn);
if (progress_callback && !progress_callback(++n, count))
return;
load2(m_tablespaces, conn);
if (progress_callback && !progress_callback(++n, count))
return;
load2(m_databases, conn);
if (progress_callback && !progress_callback(++n, count))
return;
// 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
if (progress_callback && !progress_callback(++n, count))
return;
load2(m_attributes, conn);
if (progress_callback && !progress_callback(++n, count))
return;
load2(m_constraints, conn);
if (progress_callback && !progress_callback(++n, count))
return;
load2(m_indexes, conn);
if (progress_callback && !progress_callback(++n, count))
return;
load2(m_ams, conn);
if (progress_callback && !progress_callback(++n, count))
return;
load2(m_triggers, conn);
if (progress_callback && !progress_callback(++n, count))
return;
load2(m_types, conn);
if (progress_callback && !progress_callback(++n, count))
return;
load2(m_procs, conn);
if (progress_callback && !progress_callback(++n, count))
return;
load2(m_inherits, conn);
progress_callback && progress_callback(++n, count);
refreshed(this, All);
}
void PgDatabaseCatalog::loadInfo(Pgsql::Connection &conn)
{
Pgsql::Result r = conn.query("SHOW server_version_num");
if (r && r.resultStatus() == PGRES_TUPLES_OK)
if (r.rows() == 1)
m_serverVersion << r.get(0, 0);
r = conn.query("SELECT version()");
if (r && r.resultStatus() == PGRES_TUPLES_OK)
if (r.rows() == 1)
m_serverVersionString = r.get(0, 0).asQString();
m_dbName = conn.getDBName();
}
void load(Pgsql::Connection &conn, IPgContainer &pg_cont)
{
//QThread::msleep(400);
std::string q = pg_cont.getLoadQuery();
Pgsql::Result result = conn.query(q.c_str());
if (result && result.resultStatus() == PGRES_TUPLES_OK) {
//boost::timer::auto_cpu_timer t;
pg_cont.load(result);
}
else {
auto details = result.diagDetails();
if (details.state == "42501") { // permission denied
// ignore this for now
}
else {
throw std::runtime_error("Query failed\n" + details.errorMessage);
}
}
}
const QString& PgDatabaseCatalog::serverVersionString() const
{
return m_serverVersionString;
}
int PgDatabaseCatalog::serverVersion() const
{
return m_serverVersion;
}
std::shared_ptr<const PgAttributeContainer> PgDatabaseCatalog::attributes() const
{
return m_attributes;
}
std::shared_ptr<const PgAuthIdContainer> PgDatabaseCatalog::authIds() const
{
return m_authIds;
}
std::shared_ptr<const PgClassContainer> PgDatabaseCatalog::classes() const
{
return m_classes;
}
std::shared_ptr<const PgConstraintContainer> PgDatabaseCatalog::constraints() const
{
return m_constraints;
}
std::shared_ptr<const PgDatabaseContainer> PgDatabaseCatalog::databases() const
{
return m_databases;
}
std::shared_ptr<const PgIndexContainer> PgDatabaseCatalog::indexes() const
{
return m_indexes;
}
std::shared_ptr<const PgAmContainer> PgDatabaseCatalog::ams() const
{
return m_ams;
}
std::shared_ptr<const PgNamespaceContainer> PgDatabaseCatalog::namespaces() const
{
return m_namespaces;
}
std::shared_ptr<const PgTablespaceContainer> PgDatabaseCatalog::tablespaces() const
{
return m_tablespaces;
}
std::shared_ptr<const PgTriggerContainer> PgDatabaseCatalog::triggers() const
{
return m_triggers;
}
std::shared_ptr<const PgTypeContainer> PgDatabaseCatalog::types() const
{
return m_types;
}
std::shared_ptr<const PgProcContainer> PgDatabaseCatalog::procs() const
{
return m_procs;
}
std::shared_ptr<const PgCollationContainer> PgDatabaseCatalog::collations() const
{
return m_collations;
}
std::shared_ptr<const PgInheritsContainer> PgDatabaseCatalog::inherits() const
{
return m_inherits;
}

View file

@ -0,0 +1,124 @@
#ifndef PGSQLDATABASECATALOGUE_H
#define PGSQLDATABASECATALOGUE_H
#include <libpq-fe.h>
#include <QObject>
#include <QString>
#include <functional>
#include <bitset>
#include <memory>
#include <vector>
namespace Pgsql {
class Connection;
}
class PgAttributeContainer;
class PgAuthIdContainer;
class PgClassContainer;
class PgConstraintContainer;
class PgDatabaseContainer;
class PgIndexContainer;
class PgNamespaceContainer;
class PgAmContainer;
class PgTablespaceContainer;
class PgTriggerContainer;
class PgTypeContainer;
class PgProcContainer;
class PgCollationContainer;
class PgInheritsContainer;
class PgDatabaseCatalog: public QObject, public std::enable_shared_from_this<PgDatabaseCatalog> {
Q_OBJECT
public:
PgDatabaseCatalog();
PgDatabaseCatalog(const PgDatabaseCatalog&) = delete;
PgDatabaseCatalog& operator = (const PgDatabaseCatalog&) = delete;
~PgDatabaseCatalog();
void loadAll(Pgsql::Connection &conn,
std::function<bool(int, int)> progress_callback);
void loadInfo(Pgsql::Connection &conn);
const QString& serverVersionString() const;
int serverVersion() const;
const QString& getDBName() const { return m_dbName; }
std::shared_ptr<const PgAttributeContainer> attributes() const;
std::shared_ptr<const PgAuthIdContainer> authIds() const;
std::shared_ptr<const PgClassContainer> classes() const;
std::shared_ptr<const PgConstraintContainer> constraints() const;
std::shared_ptr<const PgDatabaseContainer> databases() const;
std::shared_ptr<const PgIndexContainer> indexes() const;
std::shared_ptr<const PgAmContainer> ams() const;
std::shared_ptr<const PgNamespaceContainer> namespaces() const;
std::shared_ptr<const PgTablespaceContainer> tablespaces() const;
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;
std::shared_ptr<const PgInheritsContainer> inherits() const;
enum RefreshFlag {
Attributes = 1,
AuthIds = (1 << 1),
Classes = (1 << 2),
Constraints = (1 << 3),
Databases = (1 << 4),
Indexes = (1 << 5),
Ams = (1 << 6),
Namespaces = (1 << 7),
Tablespaces = (1 << 8),
Triggers = (1 << 9),
Types = (1 << 10),
Proc = (1 << 11),
All = 0xffffffff
};
using RefreshFlags = int;
signals:
void refreshed(const PgDatabaseCatalog *catalog, RefreshFlags flags);
private:
QString m_serverVersionString;
int m_serverVersion;
QString m_dbName;
std::shared_ptr<PgAttributeContainer> m_attributes;
std::shared_ptr<PgAuthIdContainer> m_authIds;
std::shared_ptr<PgClassContainer> m_classes;
std::shared_ptr<PgConstraintContainer> m_constraints;
std::shared_ptr<PgDatabaseContainer> m_databases;
std::shared_ptr<PgIndexContainer> m_indexes;
std::shared_ptr<PgAmContainer> m_ams;
std::shared_ptr<PgNamespaceContainer> m_namespaces;
std::shared_ptr<PgTablespaceContainer> m_tablespaces;
std::shared_ptr<PgTriggerContainer> m_triggers;
std::shared_ptr<PgTypeContainer> m_types;
std::shared_ptr<PgProcContainer> m_procs;
std::shared_ptr<PgCollationContainer> m_collations;
std::shared_ptr<PgInheritsContainer> m_inherits;
template <typename T>
void load2(std::shared_ptr<T> &ptr, Pgsql::Connection &conn)
{
if (!ptr)
ptr = std::make_shared<T>(*this);
load(conn, *ptr);
}
};
QString getRoleNameFromOid(const PgDatabaseCatalog &cat, Oid oid);
QString getRoleDisplayString(const PgDatabaseCatalog &cat, Oid oid);
QString getTablespaceDisplayString(const PgDatabaseCatalog &cat, Oid oid);
QString getTypeDisplayString(const PgDatabaseCatalog &cat, Oid oid, int32_t typmod = -1);
QString getIndexDisplayString(const PgDatabaseCatalog &cat, Oid oid);
QString getClassDisplayString(const PgDatabaseCatalog &cat, Oid oid);
#endif // PGSQLDATABASECATALOGUE_H

View file

@ -0,0 +1,21 @@
#include "PgDatabaseContainer.h"
#include "Pgsql_Col.h"
std::string PgDatabaseContainer::getLoadQuery() const
{
return "SELECT oid,datname,datdba,encoding,datcollate,datctype,datistemplate,datallowconn,"
"datconnlimit,dattablespace,datacl FROM pg_database";
}
PgDatabase PgDatabaseContainer::loadElem(const Pgsql::Row &row)
{
Pgsql::Col col(row);
Oid oid = col.nextValue();
QString name = col.nextValue();
PgDatabase v(m_catalog, oid, name);
col >> v.dba >> v.encoding >> v.collate >> v.ctype >> v.isTemplate
>> v.allowConn >> v.connLimit >> v.tablespace >> v.acl;
return v;
}

View file

@ -0,0 +1,26 @@
#ifndef PGDATABASECONTAINER_H
#define PGDATABASECONTAINER_H
#include "PgContainer.h"
#include "PgDatabase.h"
namespace Pgsql {
class Result;
}
class PgDatabaseContainer: public PgContainer<PgDatabase> {
public:
//explicit PgDatabaseContainer(PgDatabaseCatalog *cat);
using PgContainer<PgDatabase>::PgContainer;
virtual std::string getLoadQuery() const override;
protected:
PgDatabase loadElem(const Pgsql::Row &row) override;
private:
};
#endif // PGDATABASECONTAINER_H

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

@ -0,0 +1,77 @@
#include "PgIndex.h"
#include "PgDatabaseCatalog.h"
#include "PgClassContainer.h"
#include "PgAmContainer.h"
#include <QStringBuilder>
QString PgIndex::getAm() const
{
auto&& cat = catalog();
QString result;
auto idxcls = cat.classes()->getByKey(oid());
if (idxcls) {
auto am = cat.ams()->getByKey(idxcls->am);
if (am)
result = am->name; // objectName();
}
return result;
}
QString PgIndex::createSql() const
{
return definition + ";";
// const PgClass *table_class = catalog.classes()->getByKey(index.relid);
// const PgClass *index_class = catalog.classes()->getByKey(index.indexrelid);
// QString result;
// result = "CREATE ";
// if (index.isunique)
// result += "UNIQUE ";
// result += "INDEX "
//// % quoteIdent(getIndexDisplayString(catalog, index.indexrelid))
// % quoteIdent(index_class.name)
// % "\n ON " % genFQTableName(catalog, table_class);
//// % "\n USING " % index_class.am lookup in pg_am table
// return result;
#if 0
+ wxT("\n USING ") + GetIndexType()
+ wxT("\n (");
if (GetProcName().IsNull())
str += GetQuotedColumns();
else
{
str += GetQuotedSchemaPrefix(GetProcNamespace()) + qtIdent(GetProcName()) + wxT("(") + GetQuotedColumns() + wxT(")");
if (!this->GetOperatorClasses().IsNull())
str += wxT(" ") + GetOperatorClasses();
}
str += wxT(")");
if (GetConnection()->BackendMinimumVersion(8, 2) && GetFillFactor().Length() > 0)
str += wxT("\n WITH (FILLFACTOR=") + GetFillFactor() + wxT(")");
if (GetConnection()->BackendMinimumVersion(8, 0) && tablespace != GetDatabase()->GetDefaultTablespace())
str += wxT("\nTABLESPACE ") + qtIdent(tablespace);
AppendIfFilled(str, wxT("\n WHERE "), GetConstraint());
str += wxT(";\n");
if (GetConnection()->BackendMinimumVersion(7, 5))
if (GetIsClustered())
str += wxT("ALTER TABLE ") + GetQuotedSchemaPrefix(GetIdxSchema()) + qtIdent(GetIdxTable())
+ wxT(" CLUSTER ON ") + qtIdent(GetName())
+ wxT(";\n");
#endif
}
QString PgIndex::dropSql() const
{
QString result;
result = "DROP INDEX "
% fullyQualifiedQuotedObjectName()
% ";";
return result;
}

View file

@ -0,0 +1,45 @@
#ifndef PGINDEX_H
#define PGINDEX_H
#include "PgNamespaceObject.h"
#include "Pgsql_declare.h"
#include <QString>
#include <vector>
class PgIndex : public PgNamespaceObject {
public:
// Oid indexrelid = InvalidOid; // oid of pg_class for this index
Oid relid = InvalidOid; // oid of table (pg_class) where this is an index on
int16_t natts = 0;
bool isunique = false;
bool isprimary = false;
bool isexclusion = false;
bool immediate = false;
bool isclustered = false;
bool isvalid = false;
bool checkxmin = false;
bool isready = false;
bool islive = false;
bool isreplident = false;
std::vector<int16_t> key;
std::vector<Oid> collation;
std::vector<Oid> indclass;
std::vector<int16_t> option;
QString exprs;
QString pred;
QString definition;
using PgNamespaceObject::PgNamespaceObject;
QString getAm() const;
// bool operator==(Oid _oid) const { return indexrelid == _oid; }
// //bool operator==(const QString &n) const { return name == n; }
// bool operator<(Oid _oid) const { return indexrelid < _oid; }
// bool operator<(const PgIndex &rhs) const { return indexrelid < rhs.indexrelid; }
QString createSql() const;
QString dropSql() const;
};
#endif // PGINDEX_H

View file

@ -0,0 +1,53 @@
#include "PgIndexContainer.h"
#include "PgClassContainer.h"
#include "PgDatabaseCatalog.h"
#include "Pgsql_Col.h"
#include <iterator>
std::string PgIndexContainer::getLoadQuery() const
{
std::string q = R"__(
SELECT indexrelid, indrelid, indnatts, indisunique, indisprimary,
indisexclusion, indimmediate, indisclustered, indisvalid,
indcheckxmin, indisready, indislive, indkey,
indcollation, indclass, indoption, indexprs, indpred,
pg_get_indexdef(indexrelid))__";
if (minimumVersion(90400))
q += ", indisreplident ";
q += "\nFROM pg_index";
return q;
}
PgIndex PgIndexContainer::loadElem(const Pgsql::Row &row)
{
Pgsql::Col col(row);
Oid indexrelid = col.nextValue();
auto&& cls = m_catalog.classes()->getByKey(indexrelid);
auto&& name = cls->objectName();
auto&& nsoid = cls->nsOid();
PgIndex v(m_catalog, indexrelid, name, nsoid);
col >> v.relid >> v.natts >> v.isunique
>> v.isprimary >> v.isexclusion >> v.immediate >> v.isclustered
>> v.isvalid >> v.checkxmin >> v.isready >> v.islive;
col.getAsVector<int16_t>(std::back_inserter(v.key));
col.getAsVector<Oid>(std::back_inserter(v.collation));
col.getAsVector<Oid>(std::back_inserter(v.indclass));
col.getAsVector<int16_t>(std::back_inserter(v.option));
col >> v.exprs >> v.pred >> v.definition;
if (minimumVersion(90400))
col >> v.isreplident;
return v;
}
std::vector<PgIndex> PgIndexContainer::getIndexesForTable(Oid table_oid) const
{
std::vector<PgIndex> result;
for (const auto &e : m_container)
if (e.relid == table_oid)
result.push_back(e);
return result;
}

View file

@ -0,0 +1,19 @@
#ifndef PGINDEXCONTAINER_H
#define PGINDEXCONTAINER_H
#include "PgContainer.h"
#include "PgIndex.h"
#include "Pgsql_declare.h"
#include <vector>
class PgIndexContainer : public PgContainer<PgIndex> {
public:
using PgContainer<PgIndex>::PgContainer;
virtual std::string getLoadQuery() const override;
std::vector<PgIndex> getIndexesForTable(Oid table_oid) const;
protected:
virtual PgIndex loadElem(const Pgsql::Row &row) override;
};
#endif // PGINDEXCONTAINER_H

View file

@ -0,0 +1,3 @@
#include "PgInherits.h"
PgInherits::PgInherits() = default;

View file

@ -0,0 +1,25 @@
#ifndef PGINHERITS_H
#define PGINHERITS_H
#include "Pgsql_declare.h"
#include <QString>
#include <vector>
#include <tuple>
class PgInherits {
public:
using Key = std::tuple<Oid, int32_t>;
Oid relid = InvalidOid; // oid
Oid parent = InvalidOid; // oid
int32_t seqno = 0; // integer
PgInherits();
bool operator==(Key _k) const { return relid == std::get<0>(_k) && seqno == std::get<1>(_k); }
bool operator<(Key _k) const { return relid < std::get<0>(_k) || (relid == std::get<0>(_k) && seqno < std::get<1>(_k)); }
bool operator<(const PgInherits &rhs) const { return relid < rhs.relid || (relid == rhs.relid && seqno < rhs.seqno); }
};
#endif // PGINHERITS_H

View file

@ -0,0 +1,29 @@
#include "PgInheritsContainer.h"
#include "Pgsql_Col.h"
#include "PgDatabaseCatalog.h"
#include <algorithm>
std::vector<Oid> PgInheritsContainer::getParentsOf(Oid oid) const
{
std::vector<Oid> result;
auto&& iter = std::lower_bound(m_container.begin(), m_container.end(), PgInherits::Key{ oid, 1 });
for (;iter != m_container.end() && iter->relid == oid; ++iter)
result.push_back(iter->parent);
return result;
}
std::string PgInheritsContainer::getLoadQuery() const
{
return
"SELECT inhrelid, inhparent, inhseqno \n"
" FROM pg_inherits";
}
PgInherits PgInheritsContainer::loadElem(const Pgsql::Row &row)
{
Pgsql::Col col(row);
PgInherits v;
col >> v.relid >> v.parent >> v.seqno;
return v;
}

View file

@ -0,0 +1,22 @@
#ifndef PGINHERITSCONTAINER_H
#define PGINHERITSCONTAINER_H
#include "PgContainer.h"
#include "PgInherits.h"
#include "Pgsql_declare.h"
class PgInheritsContainer : public PgContainer<PgInherits, PgInherits::Key> {
public:
using PgContainer<PgInherits, PgInherits::Key>::PgContainer;
virtual std::string getLoadQuery() const override;
/// Returns the parents in the correct order
std::vector<Oid> getParentsOf(Oid oid) const;
protected:
PgInherits loadElem(const Pgsql::Row &row) override;
};
#endif // PGINHERITSCONTAINER_H

View file

@ -0,0 +1,116 @@
#include "PgKeywordList.h"
#include <boost/container/flat_set.hpp>
namespace {
using KeywordHT = boost::container::flat_set<Keyword>;
#define PG_KEYWORD(a,b,c) {a,c},
const KeywordHT _ScanKeywords = {
#include <server/parser/kwlist.h>
};
}
const Keyword* getPgsqlKeyword(QString s)
{
const Keyword *result = nullptr;
auto fr = _ScanKeywords.find(Keyword(s.toLower(), 0));
if (fr != _ScanKeywords.end())
result = &*fr;
return result;
}
//t_SymbolSet g_Keywords = {
// "a", "abort", "abs", "absent", "absolute", "access", "according", "action", "ada", "add",
// "admin", "after", "aggregate", "all", "allocate", "also", "alter", "analyse", "analyze", "and",
// "any", "are", "array", "array_agg", "array_max_cardinality", "as", "asc", "asensitive",
// "assetion", "assignment", "asymmetric", "at", "atomic", "attribute", "attributes", "authorization", "avg",
// "backward", "base64", "before", "begin", "begin_frame", "begin_partition", "bernoulli", "between", "bigint", "binary",
// "bit", "bit_length", "blob", "blocked", "bom", "boolean", "both", "breadth", "buffer", "by",
// "c", "cache", "call", "called", "cardinality", "cascade", "cascaded", "case", "cast",
// "catalog", "catalog_name", "ceil", "ceiling", "chain", "char", "character", "characteristics",
// "characters", "character_length", "character_set_catalog", "character_set_name", "character_set_schema",
// "char_length", "check", "checkpoint", "class", "class_origin", "clob", "close", "cluster",
// "coalesce", "cobol", "collate", "collation", "collation_catalog", "collation_name", "collation_schema",
// "collect", "column", "columns", "column_name", "command_function", "command_function_code",
// "comment", "comments", "commit", "committed", "concurrently", "condition", "condition_number",
// "configuration", "conflict", "connect", "connection", "connection_name", "constraint", "constraints",
// "constraint_catalog", "constraint_name", "constraint_schema", "constructor", "contains", "content",
// "continue", "control", "conversion", "convert", "copy", "corr", "corresponding", "cost", "count",
// "covar_pop", "covar_samp", "create", "cross", "csv", "cube", "cume_dist", "current", "current_catalog",
// "current_date", "current_default_transform_group", "current_path", "current_role", "current_row",
// "current_schema", "current_time", "current_timestamp", "current_transform_group_for_type",
// "current_user", "cursor", "cursor_name", "cycle",
// "data", "database", "datalink", "date", "datetime_interval_code", "datetime_interval_precision",
// "day", "db", "deallocate", "dec", "decimal", "declare", "default", "defaults", "deferrable", "deferred",
// "defined", "definer", "degree", "delete", "delimiter", "delimiters", "dense_rank", "depends", "depth",
// "deref", "derived", "desc", "describe", "descriptor", "deterministic", "diagnostics", "dictionary",
// "disable", "discard", "disconnect", "dispatch", "distinct", "dlnewcopy", "dlpreviouscopy", "dlurlcomplete",
// "dlurlcompleteonly", "dlurlcompletewrite", "dlurlpatch", "dlurlpathonly", "dlurlpathwrite", "dlurlscheme",
// "dlurlserver", "dlvalue", "do", "document", "domain", "double", "drop", "dynamic", "dynamic_function",
// "dynamic_function_code",
// "each", "element", "else", "empty", "enable", "encodign", "encrypted", "end", "end-exec", "end_frame",
// "end_partition", "enforced", "enum", "equals", "escape", "event", "every", "except", "exception", "exclude",
// "excluding", "exclusive", "exec", "execute", "exists", "exp", "explain", "expression", "extenstion",
// "external", "extract", "false", "family", "fetch", "file", "filter", "final", "first", "first_value",
// "flag", "float", "floor", "following", "for", "force", "foreign", "fortran", "forward", "found",
// "frame_row", "free", "freeze", "from", "fs", "full", "function", "functions", "fusion",
// "g", "general", "generated", "get", "global", "go" "goto", "grant", "granted", "greatest", "group",
// "grouping", "groups", "handler", "having", "header", "hex", "hierarchy", "hold", "hour", "id", "identity",
// "if", "ignore", "ilike", "immediate", "immediatly", "immutable", "implementation", "implicit", "import", "in",
// "including", "increment", "indent", "index", "indexes", "indicator", "inherit", "inherits", "initially", "inline",
// "inner", "inout", "input", "insensitive", "insert", "instance", "instantiable", "instead", "int", "integer",
// "integrity", "intersect", "intersection", "interval", "into", "invoker", "is", "isnull", "isolation",
// "join",
// "k", "key", "key_member", "key_type",
// "label", "lag", "language", "large", "last", "last_value", "lateral", "lead", "leading", "leakproof",
// "least", "left", "length", "level", "library", "like", "like_regex", "limit", "link", "listen", "ln", "load", "local",
// "localtime", "localtimestamp", "location", "locator", "lock", "locked", "logged", "lower",
// "m", "map", "mapping", "match", "matched", "materialized", "max", "maxvalue", "max_cardinality", "member",
// "merge", "message_length", "message_octet_length", "message_text", "method", "min", "minute", "minvalue",
// "mod", "mode", "modifies", "module", "month", "more", "move", "multiset", "mumps",
// "name", "namespace", "national", "natural", "nchar", "nclob", "nesting", "new", "next", "nfc", "nfd", "nfkc", "nkfd",
// "nil", "no", "none", "normalize", "normalize", "not", "nothing", "notify", "notnull", "nowait", "nth_value", "ntile",
// "null", "nullable", "nullif", "nulls", "number", "numeric",
// "object", "occurrences_regex", "octets", "octet_length", "of", "off", "offset", "oids", "old", "on", "only", "open",
// "operator", "option", "options", "or", "order", "ordering", "ordinality", "others", "out", "outer", "output", "over",
// "overlaps", "overlay", "overriding", "owned", "owner",
// "p", "pad", "parallel", "parameter", "parameter_mode", "parameter_name", "parameter_specific_catalog",
// "parameter_specific_name", "parameter_specific_schema", "parser",
// "partial", "partition", "pascal", "passing", "passthrough", "password", "path", "percent", "percentile_cont",
// "percentile_disc", "percent_rank", "period", "permission", "placing", "plans", "pli", "policy", "portion",
// "position", "position_regex", "power", "precedes", "preceding", "precision", "prepare", "prepared", "preserve",
// "primary", "prior", "privileges", "procedural", "procedure", "program", "public",
// "quote", "range", "rank", "read", "reads", "real", "reassign", "recheck", "recovery", "recursive", "ref",
// "references", "referencing", "refresh", "regr_avgx", "regr_avgy", "regr_count", "regr_intercept", "regr_r2",
// "regr_slope", "regr_sxx", "regr_sxy", "regr_syy", "reindex", "relative", "release", "rename", "repeatable",
// "replace", "replica", "requiring", "reset", "respect", "restart", "restore", "restrict", "result", "return",
// "returned_cardinality", "returned_length", "returned_octet_length", "returned_sqlstate", "returning", "returns",
// "revoke", "right", "role", "rollback", "rollup", "routine", "routine_catalog", "routine_name", "routine_schema",
// "row", "rows", "row_count", "row_number", "rule",
// "savepoint", "scale", "schema", "schema_name", "scope", "scope_catalog", "scope_name", "scope_schema", "scroll",
// "search", "second", "section", "security", "select", "selective", "self", "sensitive", "sequence", "sequences",
// "serializable", "server", "server_name", "session", "session_user", "set", "setof", "sets", "share", "show",
// "similar", "simple", "size", "skip", "smallint", "snapshot", "some", "source", "space", "specific", "specifictype",
// "specific_name", "sql", "sqlcode", "sqlerror", "sqlexception", "sqlstate", "sqlwarning", "sqrt", "stable",
// "standalone", "start", "state", "statement", "static", "statistics", "stddev_pop", "stddev_samp", "stdin", "stdout",
// "storage", "strict", "strip", "structure", "style", "subclass_origin", "submultiset", "substring", "substring_regex",
// "succeeds", "sum", "symmetric", "sysid", "system", "system_time", "system_user",
// "t", "table", "tables", "tablesample", "tablespace", "table_name", "temp", "template", "temporary", "text", "then",
// "ties", "time", "timestamp", "timezone_hour", "timezone_minute", "to", "token", "top_level_count", "trailing",
// "transaction", "transaction_committed", "transaction_rolled_back", "transaction_active", "transform", "transforms",
// "translate", "translate_regex", "translation", "treat", "trigger", "trigger_catalog", "trigger_name", "trigger_schema",
// "trim", "trim_array", "true", "truncate", "trusted", "type", "types", "uescape", "unbounded", "uncommitted", "under",
// "unencrypted", "union", "unique", "unknown", "unlink", "unlisten", "unlogged", "unnamed", "unnest", "until", "untyped",
// "update", "upper", "uri", "usage", "user", "user_defined_type_catalog", "user_defined_type_code",
// "user_defined_type_name", "user_defined_type_schema", "using",
// "vacuum", "valid", "validate", "validator", "value", "values", "value_of", "varbinary", "varchar", "variadic",
// "varying", "var_pop", "var_samp", "verbose", "version", "versioning", "view", "views", "volatile",
// "when", "whenever", "where", "whitespace", "width_bucket", "window", "with", "within", "without", "work", "wrapper",
// "write", "xml", "xmlagg", "xmlattributes", "xmlbinary", "xmlcast", "xmlcomment", "xmlconcat", "xmldeclaration",
// "xmldocument", "xmlelement", "xmlexists", "xmlforest", "xmliterate", "xmlnamespaces", "xmlparse", "xmlpi",
// "xmlquery", "xmlroot", "xmlschema", "xmlserialize", "xmltable", "xmltext", "xmlvalidate", "year", "yes", "zone"
//};

View file

@ -0,0 +1,51 @@
#ifndef PGKEYWORDLIST_H
#define PGKEYWORDLIST_H
#include <QString>
#include <unordered_set>
#include <functional>
#include "util.h"
#define UNRESERVED_KEYWORD 0
#define COL_NAME_KEYWORD 1
#define TYPE_FUNC_NAME_KEYWORD 2
#define RESERVED_KEYWORD 3
using t_SymbolSet = std::unordered_set<QString>;
class Keyword {
public:
Keyword(QString kw, int16_t cat)
: m_keyword(kw), m_category(cat)
{}
const QString& getKeyword() const { return m_keyword; }
int16_t getCategory() const { return m_category; }
//bool operator<(const QString &s) const { return m_keyword < s; }
bool operator<(const Keyword &kw) const { return m_keyword < kw.m_keyword; }
private:
QString m_keyword;
int16_t m_category;
};
namespace std {
template <>
struct hash<Keyword>
{
std::size_t operator()(const Keyword& s) const
{
return qHash(s.getKeyword());
}
};
}
const Keyword* getPgsqlKeyword(QString s);
#endif // PGKEYWORDLIST_H

View file

@ -0,0 +1,8 @@
#include "PgNamespace.h"
bool PgNamespace::isSystemCatalog() const
{
auto&& n = objectName();
return n.startsWith("pg_")
|| n == "information_schema";
}

View file

@ -0,0 +1,28 @@
#ifndef PGNAMESPACE_H
#define PGNAMESPACE_H
#include "PgDatabaseObject.h"
#include <QString>
#include <libpq-fe.h>
/// Object representing a namespace within a database
class PgNamespace: public PgDatabaseObject {
public:
// Oid oid = InvalidOid;
// QString name;
Oid owner = InvalidOid;
QString acl;
using PgDatabaseObject::PgDatabaseObject;
// bool operator==(Oid _oid) const { return oid == _oid; }
// bool operator==(const QString &n) const { return objectName() == 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

View file

@ -0,0 +1,21 @@
#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";
}
PgNamespace PgNamespaceContainer::loadElem(const Pgsql::Row &row)
{
Pgsql::Col col(row);
Oid oid = col.nextValue();
QString name = col.nextValue();
PgNamespace v(m_catalog, oid, name);
col >> v.owner >> v.acl;
return v;
}

View file

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

View file

@ -0,0 +1,40 @@
#include "PgNamespaceObject.h"
#include "PgDatabaseCatalog.h"
#include "PgNamespace.h"
#include "PgNamespaceContainer.h"
#include "SqlFormattingUtils.h"
PgNamespaceObject::PgNamespaceObject(PgDatabaseCatalog& cat, Oid oid, const QString &name, Oid schema_oid)
: PgDatabaseObject(cat, oid, name)
, m_schemaOid(schema_oid)
{}
Oid PgNamespaceObject::nsOid() const
{
return m_schemaOid;
}
//void PgSchemaObject::setSchemaOid(Oid oid)
//{
// m_schemaOid = oid;
//}
QString PgNamespaceObject::nsName() const
{
return ns().objectName();
}
QString PgNamespaceObject::quotedNsName() const
{
return quoteIdent(nsName());
}
QString PgNamespaceObject::fullyQualifiedQuotedObjectName() const
{
return quotedNsName() + "." + quotedObjectName();
}
const PgNamespace& PgNamespaceObject::ns() const
{
return *catalog().namespaces()->getByKey(m_schemaOid);
}

View file

@ -0,0 +1,28 @@
#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 PgNamespaceObject: public PgDatabaseObject {
public:
PgNamespaceObject(PgDatabaseCatalog& cat, Oid oid, const QString &name, Oid schema_oid);
Oid nsOid() const;
// void setSchemaOid(Oid oid);
QString nsName() const;
QString quotedNsName() 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,36 @@
#include "PgObject.h"
#include "SqlFormattingUtils.h"
PgObject::PgObject(PgDatabaseCatalog& cat, Oid oid, const QString &name)
: m_catalog(&cat)
, m_oid(oid)
, m_name(name)
{}
PgObject::~PgObject()
{}
Oid PgObject::oid() const
{
return m_oid;
}
const QString& PgObject::objectName() const
{
return m_name;
}
QString PgObject::quotedObjectName() const
{
return quoteIdent(objectName());
}
const PgDatabaseCatalog& PgObject::catalog() const
{
return *m_catalog;
}
void test(PgObject a, PgObject b)
{
a = b;
}

View file

@ -0,0 +1,32 @@
#ifndef PGOBJECT_H
#define PGOBJECT_H
#include <libpq-fe.h>
#include <QString>
class PgDatabaseCatalog;
class PgObject {
public:
explicit PgObject(PgDatabaseCatalog& cat, Oid oid, const QString &name);
virtual ~PgObject();
Oid oid() const;
const QString& objectName() const;
/// Default implementation uses objectName and add quotes when needed.
virtual QString quotedObjectName() const;
bool operator==(Oid _oid) const { return m_oid == _oid; }
bool operator==(const QString &n) const { return m_name == n; }
bool operator<(Oid _oid) const { return m_oid < _oid; }
bool operator<(const PgObject &rhs) const { return m_oid < rhs.m_oid; }
protected:
const PgDatabaseCatalog& catalog() const;
private:
PgDatabaseCatalog* m_catalog;
Oid m_oid;
QString m_name;
};
#endif // PGOBJECT_H

View file

@ -0,0 +1,25 @@
#include "PgOwnedObject.h"
#include "PgAuthId.h"
#include "PgAuthIdContainer.h"
#include "PgDatabaseCatalog.h"
void PgOwnedObject::setOwnerOid(PgDatabaseCatalog& cat, Oid oid)
{
m_ownerOid = oid;
m_owner = cat.authIds()->getByKey(oid);
}
Oid PgOwnedObject::ownerOid() const
{
return m_ownerOid;
}
QString PgOwnedObject::ownerName() const
{
return m_owner->name;
}
const PgAuthId* PgOwnedObject::owner() const
{
return m_owner;
}

View file

@ -0,0 +1,22 @@
#ifndef PGOWNEDOBJECT_H
#define PGOWNEDOBJECT_H
#include <QString>
#include <libpq-fe.h>
#include <memory>
class PgDatabaseCatalog;
class PgAuthId;
class PgOwnedObject {
public:
void setOwnerOid(PgDatabaseCatalog& cat, Oid oid);
Oid ownerOid() const;
QString ownerName() const;
const PgAuthId* owner() const;
private:
Oid m_ownerOid = InvalidOid;
const PgAuthId * m_owner;
};
#endif // PGOWNEDOBJECT_H

278
pglablib/catalog/PgProc.cpp Normal file
View file

@ -0,0 +1,278 @@
#include "PgProc.h"
#include "std_utils.h"
#include "SqlFormattingUtils.h"
#include "PgDatabaseCatalog.h"
#include "PgTypeContainer.h"
#include <boost/assert.hpp>
namespace {
Arg::Mode CharToArgMode(char c)
{
switch (c) {
case 'i': return Arg::In;
case 'o': return Arg::Out;
case 'b': return Arg::InOut;
case 'v': return Arg::Variadic;
case 't': return Arg::Table;
}
throw std::runtime_error("Unexpected value for pg_proc.proargmodes");
}
QString ArgModeToString(Arg::Mode m)
{
switch (m) {
case Arg::In: return "IN";
case Arg::Out: return "OUT";
case Arg::InOut: return "INOUT";
case Arg::Variadic: return "VARIADIC";
case Arg::Table: return "TABLE";
}
throw std::runtime_error("Unexpected value for Arg::Mode");
}
std::vector<QString> getArrayFromCommaSeparatedList(const QString &str)
{
std::vector<QString> res;
const int len = str.length();
if (len == 0)
return res;
// setup start state for parsing
int index = 0, brackets = 0, start_array = 0;
bool single_quote = false, double_quote = false;
for(; index < len; index++) {
QChar ch = str[index];
if (!double_quote && ch == L'\'')
single_quote = !single_quote;
else if (!single_quote && ch == L'"')
double_quote = !double_quote;
else if (!double_quote && !single_quote && ch == L'(')
brackets++;
else if (!double_quote && !single_quote && ch == L')')
brackets--;
else if (!double_quote && !single_quote && brackets == 0 && ch == L',') {
if (index != start_array)
res.push_back(str.mid(start_array, index - 1).trimmed());
else
res.push_back(QString());
start_array = index + 1;
}
}
if (double_quote || single_quote || brackets != 0)
throw std::runtime_error("Error parsing comma seperated array");
// Add last value to array
res.push_back(str.right(start_array).trimmed());
return res;
}
}
void PgProc::setArgs(
std::vector<Oid> argtypes,
std::vector<Oid> allargtypes,
std::vector<char> argmodes,
std::vector<QString> argnames,
std::optional<QString> argdefaults
)
{
// When all args are of type IN allargtypes is empty and only argtypes is filled
const std::vector<Oid> &types = allargtypes.empty() ? argtypes : allargtypes;
const size_t count = types.size();
m_args.clear();
m_args.reserve(count);
for (size_t index = 0; index < count; ++index) {
// we "forget" the default here because it is easier
// to apply them in reverse order when we have already
// filled the list
m_args.emplace_back(
types[index],
CharToArgMode(value_or(argmodes, index, 'i')),
value_or(argnames, index, QString()),
""
);
}
auto defaults_array = getArrayFromCommaSeparatedList(argdefaults.value_or(QString()));
// Apply defaults from end to start to IN en INOUT parameters (others can't have a default)
size_t arg_idx = m_args.size() - 1;
for (auto def_iter = defaults_array.rbegin(); def_iter != defaults_array.rend(); ++def_iter) {
// skip arguments with wrong mode
while (m_args[arg_idx].mode != Arg::In && m_args[arg_idx].mode != Arg::InOut)
if (arg_idx == 0) throw std::runtime_error("Error processing defaults");
else --arg_idx;
m_args[arg_idx].def = *def_iter;
}
}
const std::vector<Arg>& PgProc::args() const
{
return m_args;
}
QString PgProc::argListWithNames(bool multiline) const
{
QString args;
size_t nArgs = 0;
auto&& types = catalog().types();
for (auto&& arg_elem : m_args) {
// All Table arguments lies at the end of the list
// Do not include them as the part of the argument list
if (arg_elem.mode == Arg::Table)
break;
nArgs++;
if (args.length() > 0)
args += (multiline) ? ",\n " : ", ";
QString arg;
if (arg_elem.mode != Arg::In)
arg += ArgModeToString(arg_elem.mode);
if (!arg_elem.name.isEmpty()) {
if (!arg.isEmpty())
arg += " ";
arg += quoteIdent(arg_elem.name);
}
if (!arg.isEmpty())
arg += " ";
arg += types->getByKey(arg_elem.type)->objectName();
if (!arg_elem.def.isEmpty())
arg += " DEFAULT " + arg_elem.def;
args += arg;
}
if (multiline && nArgs > 1)
args = "\n " + args;
return args;
}
// Return the signature arguments list. If forScript = true, we format the list
// appropriately for use in a SELECT script.
QString PgProc::argSigList(const bool forScript) const
{
QString args;
auto&& types = catalog().types();
for (auto&& arg_elem : m_args) {
// OUT parameters are not considered part of the signature, except for EDB-SPL,
// although this is not true for EDB AS90 onwards..
if (arg_elem.mode != Arg::Out && arg_elem.mode != Arg::Table) {
if (args.length() > 0) {
if (forScript)
args += ",\n";
else
args += ", ";
}
QString typname = types->getByKey(arg_elem.type)->objectName();
if (forScript)
args += " <" + typname + ">";
else
args += typname;
}
}
return args;
}
QString PgProc::createSql() const
{
if (createSqlCache.isEmpty()) {
QString sql;
//wxString qtName = GetQuotedFullIdentifier() + wxT("(") + GetArgListWithNames(true) + wxT(")");
QString quoted_name = QString("%1(%2)").arg(fullyQualifiedQuotedObjectName(), argListWithNames(true));
// wxString qtSig = GetQuotedFullIdentifier() + wxT("(") + GetArgSigList() + wxT(")");
QString quoted_sig = QString("%1(%2)").arg(fullyQualifiedQuotedObjectName(), argSigList());
auto&& types = catalog().types();
QString return_type = types->getByKey(rettype)->objectName();
sql = QString("-- Function: %1\n\n"
"-- DROP FUNCTION %1;\n\n"
"CREATE OR REPLACE FUNCTION %2\n"
" RETURNS %3 AS\n"
).arg(quoted_sig, quoted_name, return_type);
// if (GetLanguage().IsSameAs(wxT("C"), false))
// {
// sql += qtDbString(GetBin()) + wxT(", ") + qtDbString(GetSource());
// }
// else
// {
// if (GetConnection()->BackendMinimumVersion(7, 5))
// sql += qtDbStringDollar(GetSource());
sql += dollarQuoteString(src);
// else
// sql += qtDbString(GetSource());
// }
// sql += wxT("\n LANGUAGE ") + GetLanguage() + wxT(" ");
// if (GetConnection()->BackendMinimumVersion(8, 4) && GetIsWindow())
// sql += wxT("WINDOW ");
// sql += GetVolatility();
// if (GetConnection()->BackendMinimumVersion(9, 2) && GetIsLeakProof())
// sql += wxT(" LEAKPROOF");
// if (GetIsStrict())
// sql += wxT(" STRICT");
// if (GetSecureDefiner())
// sql += wxT(" SECURITY DEFINER");
// // PostgreSQL 8.3+ cost/row estimations
// if (GetConnection()->BackendMinimumVersion(8, 3))
// {
// sql += wxT("\n COST ") + NumToStr(GetCost());
// if (GetReturnAsSet())
// sql += wxT("\n ROWS ") + NumToStr(GetRows());
// }
// if (!sql.Strip(wxString::both).EndsWith(wxT(";")))
// sql += wxT(";");
// size_t i;
// for (i = 0 ; i < configList.GetCount() ; i++)
// {
// if (configList.Item(i).BeforeFirst('=') != wxT("search_path") &&
// configList.Item(i).BeforeFirst('=') != wxT("temp_tablespaces"))
// sql += wxT("\nALTER FUNCTION ") + qtSig
// + wxT(" SET ") + configList.Item(i).BeforeFirst('=') + wxT("='") + configList.Item(i).AfterFirst('=') + wxT("';\n");
// else
// sql += wxT("\nALTER FUNCTION ") + qtSig
// + wxT(" SET ") + configList.Item(i).BeforeFirst('=') + wxT("=") + configList.Item(i).AfterFirst('=') + wxT(";\n");
// }
// sql += wxT("\n")
// + GetOwnerSql(8, 0, wxT("FUNCTION ") + qtSig)
// + GetGrant(wxT("X"), wxT("FUNCTION ") + qtSig);
// if (!GetComment().IsNull())
// {
// sql += wxT("COMMENT ON FUNCTION ") + qtSig
// + wxT(" IS ") + qtDbString(GetComment()) + wxT(";\n");
// }
// if (GetConnection()->BackendMinimumVersion(9, 1))
// sql += GetSeqLabelsSql();
createSqlCache = std::move(sql);
}
return createSqlCache;
}

87
pglablib/catalog/PgProc.h Normal file
View file

@ -0,0 +1,87 @@
#ifndef PGPROC_H
#define PGPROC_H
#include "PgNamespaceObject.h"
#include <QString>
#include <libpq-fe.h>
#include "Pgsql_Value.h"
#include <vector>
class Arg {
public:
enum Mode {
In, Out, InOut, Variadic, Table
};
Oid type;
Mode mode;
QString name;
QString def;
Arg(Oid t, Mode m, QString n, QString d)
: type(t), mode(m), name(n), def(d)
{}
};
class PgProc: public PgNamespaceObject {
public:
using PgNamespaceObject::PgNamespaceObject;
// Oid oid = InvalidOid; // oid
// QString name; // name
// Oid pronamespace = InvalidOid; // oid, namespace
Oid owner = InvalidOid; // oid
Oid lang = InvalidOid; // oid
float cost = 0.f; // float4
float rows = 0.f; // float4
Oid variadic = InvalidOid; // oid
QString transform; // regproc
bool isagg = false; // bool
bool iswindow = false; // bool
bool secdef = false; // bool
bool leakproof = false; // bool
bool isstrict = false; // bool
bool retset = false; // bool
char provolatile = '\0'; // char
char parallel = '\0'; // char, version >= 9.6
int16_t nargs = 0; // int2
int16_t nargdefaults; // = 0; // int2
Oid rettype = InvalidOid; // oid
std::vector<Oid> trftypes; // oid[], version >= 9.5
QString src; // text
QString bin; // text
std::vector<QString> config; // text[]
QString acl; // aclitem[]
/// Converts the collection of arrays about the arguments to a single list of arguments
/// making it much easier to work with them correctly
void setArgs(
std::vector<Oid> argtypes,
std::vector<Oid> allargtypes,
std::vector<char> argmodes,
std::vector<QString> argnames,
std::optional<QString> argdefaults
);
const std::vector<Arg>& args() const;
// 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 PgProc &rhs) const { return oid < rhs.oid; }
QString createSql() const;
QString argListWithNames(bool multiline = false) const;
QString argSigList(const bool forScript = false) const;
// bool isTrigger() const
// {
// return typname == wxT("\"trigger\"") || typname == wxT("trigger") || typname == wxT("event_trigger") || typname == wxT("\"event_trigger\""))
// }
private:
mutable QString createSqlCache;
std::vector<Arg> m_args;
};
#endif // PGPROC_H

View file

@ -0,0 +1,59 @@
#include "PgProcContainer.h"
#include "Pgsql_Connection.h"
#include "Pgsql_Col.h"
#include "PgDatabaseCatalog.h"
#include <algorithm>
#include <iterator>
std::string PgProcContainer::getLoadQuery() const
{
std::string column_list =
"oid,proname,pronamespace,proowner,prolang,procost,prorows,"
"provariadic,protransform,proisagg,proiswindow,prosecdef,proleakproof,"
"proisstrict,proretset,provolatile,pronargs,pronargdefaults,prorettype,"
"proargtypes,proallargtypes,proargmodes,proargnames,proargdefaults,"
"prosrc,probin,proconfig,proacl";
if (minimumVersion(90500)) {
column_list += ",protrftypes";
}
if (minimumVersion(90600)) {
column_list += ",proparallel";
}
return "SELECT " + column_list + " FROM pg_proc";
}
PgProc PgProcContainer::loadElem(const Pgsql::Row &row)
{
Pgsql::Col col(row);
Oid oid = col.nextValue();
QString name = col.nextValue();
Oid namespace_oid = col.nextValue();
PgProc v(m_catalog, oid, name, namespace_oid);
col >> v.owner >> v.lang >> v.cost >> v.rows
>> v.variadic >> v.transform >> v.isagg >> v.iswindow >> v.secdef >> v.leakproof
>> v.isstrict >> v.retset >> v.provolatile >> v.nargs >> v.nargdefaults
>> v.rettype;
std::vector<Oid> argtypes; // oid[]
std::vector<Oid> allargtypes; // oid[]
std::vector<char> argmodes; // char[]
std::vector<QString> argnames; // text[]
std::optional<QString> argdefaults; // pg_node_tree
col.getAsVector<Oid>(std::back_inserter(argtypes));
col >> allargtypes >> argmodes >> argnames >> argdefaults
>> v.src >> v.bin >> v.config >> v.acl;
v.setArgs(argtypes, allargtypes, argmodes, argnames, argdefaults);
if (minimumVersion(90500)) {
col >> v.trftypes;
}
if (minimumVersion(90600)) {
col >> v.parallel;
}
return v;
}

View file

@ -0,0 +1,26 @@
#ifndef PGPROCCONTAINER_H
#define PGPROCCONTAINER_H
#include "PgContainer.h"
#include "PgProc.h"
#include <vector>
namespace Pgsql {
class Result;
}
class PgProcContainer: public PgContainer<PgProc> {
public:
using PgContainer<PgProc>::PgContainer;
virtual std::string getLoadQuery() const override;
//std::vector<PgProc> getTriggersForRelation(Oid cls) const;
protected:
PgProc loadElem(const Pgsql::Row &row) override;
private:
};
#endif // PGPROCCONTAINER_H

View file

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

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

@ -0,0 +1,4 @@
#include "PgTablespace.h"
PgTablespace::PgTablespace()
{}

View file

@ -0,0 +1,24 @@
#ifndef PGTABLESPACE_H
#define PGTABLESPACE_H
#include <QString>
#include <libpq-fe.h>
#include <vector>
class PgTablespace {
public:
Oid oid = InvalidOid;
QString name;
Oid owner = InvalidOid;
std::vector<QString> acl;
std::vector<QString> options;
PgTablespace();
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 PgTablespace &rhs) const { return oid < rhs.oid; }
};
#endif // PGTABLESPACE_H

View file

@ -0,0 +1,23 @@
#include "PgTablespaceContainer.h"
#include "Pgsql_Connection.h"
#include "Pgsql_Col.h"
#include "Pgsql_declare.h"
#include "PgDatabaseCatalog.h"
#include <iterator>
std::string PgTablespaceContainer::getLoadQuery() const
{
return
R"__SQL__(
SELECT oid, spcname, spcowner, spcacl, spcoptions
FROM pg_tablespace
)__SQL__";
}
PgTablespace PgTablespaceContainer::loadElem(const Pgsql::Row &row)
{
Pgsql::Col col(row);
PgTablespace v;
col >> v.oid >> v.name >> v.owner >> v.acl >> v.options;
return v;
}

View file

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

View file

@ -0,0 +1,156 @@
#include "PgTrigger.h"
#include "PgClassContainer.h"
#include "PgDatabaseCatalog.h"
#include "PgProcContainer.h"
#include "SqlFormattingUtils.h"
#include <QStringBuilder>
QString PgTrigger::dropSql()
{
if (m_dropSql.isEmpty()) {
auto&& fqtablename = catalog().classes()->getByKey(relid)->fullyQualifiedQuotedObjectName(); // genFQTableName(catalog(), *catalog().classes()->getByKey(relid));
m_dropSql = "DROP TRIGGER " % quotedObjectName()
% " ON " % fqtablename % ";";
}
return m_dropSql;
}
QString PgTrigger::createSql()
{
if (m_createSql.isEmpty()) {
auto&& fqtablename = catalog().classes()->getByKey(relid)->fullyQualifiedQuotedObjectName(); //genFQTableName(catalog(), *catalog().classes()->getByKey(relid));
auto&& triggername = quotedObjectName();
if (constraint != InvalidOid)
m_createSql += "CREATE CONSTRAINT TRIGGER ";
else
m_createSql += "CREATE TRIGGER ";
m_createSql += triggername + "\n "
+ typeFireWhen()
+ " " + event();
m_createSql += "\n ON " + fqtablename;
if (deferrable) {
m_createSql += "\n DEFERRABLE INITIALLY ";
if (initdeferred)
m_createSql += "DEFERRED";
else
m_createSql += "IMMEDIATE";
}
m_createSql += "\n FOR EACH " + forEach();
// requires atleast 8.5 don;t think we have to support older
if (!whenclause.isEmpty())
m_createSql += "\n WHEN (" + whenclause + ")";
m_createSql += QString("\n EXECUTE PROCEDURE %1;\n").arg(procedure());
if (!enabled)
m_createSql += QString("ALTER TABLE %1 DISABLE TRIGGER %2;\n").arg(fqtablename, triggername);
// if (!GetComment().IsEmpty())
// sql += wxT("COMMENT ON TRIGGER ") + GetQuotedIdentifier() + wxT(" ON ") + GetQuotedFullTable()
// + wxT(" IS ") + qtDbString(GetComment()) + wxT(";\n");
}
return m_createSql;
}
QString PgTrigger::typeFireWhen() const
{
QString when;
if (type & TriggerTypeBefore)
when = "BEFORE";
else if (type & TriggerTypeInstead)
when = "INSTEAD OF";
else
when = "AFTER";
return when;
}
QString PgTrigger::eventAbbr() const
{
QString event;
if (type & TriggerTypeInsert)
event += "I";
if (type & TriggerTypeUpdate)
event += "U";
if (type & TriggerTypeDelete)
event += "D";
if (type & TriggerTypeTruncate)
event += "T";
return event;
}
QString PgTrigger::event() const
{
QString event;
if (type & TriggerTypeInsert)
event += "INSERT ";
if (type & TriggerTypeUpdate)
event += "UPDATE ";
if (type & TriggerTypeDelete)
event += "DELETE ";
if (type & TriggerTypeTruncate)
event += "TRUNCATE";
return event.trimmed();
}
QString PgTrigger::forEach() const
{
if (isRow())
return "ROW";
else
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

@ -0,0 +1,71 @@
#ifndef PGTRIGGER_H
#define PGTRIGGER_H
#include "PgNamespaceObject.h"
#include "Pgsql_Value.h"
#include <QString>
#include <libpq-fe.h>
class PgDatabaseCatalog;
class PgTrigger: public PgNamespaceObject {
public:
// Oid oid = InvalidOid;
Oid relid;
// QString name;
Oid foid;
int16_t type;
char enabled;
bool isinternal;
Oid constrrelid;
Oid constrindid;
Oid constraint;
bool deferrable;
bool initdeferred;
int16_t nargs;
QString attr;
QString args;
QString whenclause;
QString oldtable; // >= 10.0
QString newtable; // >= 10.0
using PgNamespaceObject::PgNamespaceObject;
// virtual QString objectName() const override;
// 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 PgTrigger &rhs) const { return oid < rhs.oid; }
static constexpr int TriggerTypeRow = (1 << 0);
static constexpr int TriggerTypeBefore = (1 << 1);
static constexpr int TriggerTypeInsert = (1 << 2);
static constexpr int TriggerTypeDelete = (1 << 3);
static constexpr int TriggerTypeUpdate = (1 << 4);
static constexpr int TriggerTypeTruncate = (1 << 5);
static constexpr int TriggerTypeInstead = (1 << 6);
QString dropSql();
QString createSql();
bool isRow() const { return type & TriggerTypeRow; }
bool isBefore() const { return type & TriggerTypeBefore; }
QString typeFireWhen() const;
QString eventAbbr() const;
QString event() const;
QString forEach() const;
QString procedure() const;
//wxString pgTrigger::GetForEach() const
//{
// return (triggerType & TRIGGER_TYPE_ROW) ? wxT("ROW") : wxT("STATEMENT");
//}
QString arguments() const;
private:
mutable QString m_dropSql; // cache
mutable QString m_createSql; // cache
};
#endif // PGTRIGGER_H

View file

@ -0,0 +1,56 @@
#include "PgTriggerContainer.h"
#include "Pgsql_Connection.h"
#include "Pgsql_Col.h"
#include <algorithm>
#include <iterator>
std::string PgTriggerContainer::getLoadQuery() const
{
std::string q =
"SELECT oid, tgname, tgrelid, tgfoid, tgtype, tgenabled, tgisinternal, tgconstrrelid, \n"
" 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)
{
Pgsql::Col col(row);
Oid oid = col.nextValue();
QString name = col.nextValue();
PgTrigger v(m_catalog, oid, name, InvalidOid);
col >> v.relid >> v.foid >> v.type >> v.enabled >> v.isinternal >> v.constrrelid
>> v.constrindid >> v.constraint >> v.deferrable >> v.initdeferred >> v.nargs >> v.attr;
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;
}
std::vector<PgTrigger> PgTriggerContainer::getTriggersForRelation(Oid cls) const
{
std::vector<PgTrigger> result;
for (auto e : m_container)
if (e.relid == cls)
result.push_back(e);
return result;
}

View file

@ -0,0 +1,28 @@
#ifndef PGTRIGGERCONTAINER_H
#define PGTRIGGERCONTAINER_H
#include "PgContainer.h"
#include "PgTrigger.h"
#include <vector>
namespace Pgsql {
class Result;
}
class PgTriggerContainer: public PgContainer<PgTrigger> {
public:
using PgContainer<PgTrigger>::PgContainer;
virtual std::string getLoadQuery() const override;
std::vector<PgTrigger> getTriggersForRelation(Oid cls) const;
protected:
PgTrigger loadElem(const Pgsql::Row &row) override;
private:
};
#endif // PGTRIGGERCONTAINER_H

View file

@ -0,0 +1,52 @@
#include "PgType.h"
#include "Pgsql_Connection.h"
void operator<<(TypCategory &s, const Pgsql::Value &v)
{
//s = static_cast<T>(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;
}
}

74
pglablib/catalog/PgType.h Normal file
View file

@ -0,0 +1,74 @@
#ifndef PGTYPE_H
#define PGTYPE_H
#include "PgNamespaceObject.h"
#include "PgOwnedObject.h"
#include <QString>
#include <libpq-fe.h>
#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 PgNamespaceObject, public PgOwnedObject {
public:
// Oid oid = InvalidOid;
// QString name; // formatted name as per database, arrays
QString typname; //"name";"NO"
// Oid typnamespace = InvalidOid;//"oid";"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 ispreferred = false;//"boolean";"NO"
bool isdefined = false;//"boolean";"NO"
QString delim;//""char"";"NO"
Oid relid = InvalidOid;//"oid";"NO"
Oid elem = InvalidOid;//"oid";"NO"
Oid array = InvalidOid;//"oid";"NO"
QString input;//regproc";"NO"
QString output;//"regproc";"NO"
QString receive;//"regproc";"NO"
QString send;//"regproc";"NO"
QString modin;//"regproc";"NO"
QString modout;//"regproc";"NO"
QString analyze;//"regproc";"NO"
QString align;//""char"";"NO"
QString storage;//""char"";"NO"
bool notnull = false;//"boolean";"NO"
Oid basetype = InvalidOid;//"oid";"NO"
int typmod = -1;//"integer";"NO"
int ndims = 0;//"integer";"NO"
Oid collation = InvalidOid;//"oid";"NO"
QString defaultbin;//"pg_node_tree";"YES"
QString typdefault;//"text";"YES"
QString acl;//"ARRAY";"YES"
using PgNamespaceObject::PgNamespaceObject;
// 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 PgType &rhs) const { return oid < rhs.oid; }
};
#endif // PGTYPE_H

View file

@ -0,0 +1,31 @@
#include "PgTypeContainer.h"
#include "Pgsql_Connection.h"
#include "Pgsql_Col.h"
#include <algorithm>
std::string PgTypeContainer::getLoadQuery() const
{
return
"SELECT oid, format_type(oid, NULL) AS name, typnamespace, typowner, typname, typlen, typbyval, typtype, typcategory, \n"
" typispreferred, typisdefined, typdelim, typrelid, typelem, typarray, typinput, typoutput, \n"
" typreceive, typsend, typmodin, typmodout, typanalyze, typalign, typstorage, typnotnull, \n"
" typbasetype, typtypmod, typndims, typcollation, typdefaultbin, typdefault, typacl \n"
"FROM pg_type";
}
PgType PgTypeContainer::loadElem(const Pgsql::Row &row)
{
Pgsql::Col col(row);
Oid type_oid = col.nextValue();
QString name = col.nextValue();
Oid ns_oid = col.nextValue();
Oid owner = col.nextValue();
PgType v(m_catalog, type_oid, name, ns_oid);
col >> v.typname >> v.len >> v.byval >> v.type >> v.category
>> v.ispreferred >> v.isdefined >> v.delim >> v.relid >> v.elem >> v.array >> v.input >> v.output
>> v.receive >> v.send >> v.modin >> v.modout >> v.analyze >> v.align >> v.storage >> v.notnull
>> v.basetype >> v.typmod >> v.ndims >> v.collation >> v.defaultbin >> v.typdefault >> v.acl;
v.setOwnerOid(m_catalog, owner);
return v;
}

View file

@ -0,0 +1,33 @@
#ifndef PGTYPECONTAINER_H
#define PGTYPECONTAINER_H
#include "PgType.h"
#include "PgContainer.h"
namespace Pgsql {
class Result;
}
class PgTypeContainer: public PgContainer<PgType> {
public:
using PgContainer<PgType>::PgContainer;
virtual std::string getLoadQuery() const override;
/** Searches for the type matching the specified oid.
*
* \return Returns the matching type or if it is not found a default constructed PgType (oid == InvalidOid).
*/
// const PgType& getTypeByOid(Oid oid) const;
// const PgType& getTypeByName(const QString &name) const;
// const PgType& getTypeByIdx(int idx) const;
protected:
virtual PgType loadElem(const Pgsql::Row &row) override;
private:
// PgType m_invalidType; ///< default constructed object for when a non existent type is being retrieved.
// t_Types m_types; // Keep sorted by Oid
};
#endif // PGTYPECONTAINER_H