WIP: Added page showing list of functions.

Only list is shown, still working on details.
This commit is contained in:
eelke 2018-11-25 09:06:01 +01:00
parent 7db859737a
commit 840af1e0a9
19 changed files with 635 additions and 92 deletions

View file

@ -51,7 +51,7 @@ public:
bool ispopulated;
int frozenxid;
int minmxid;
std::vector<QString> acl;
QString acl;
std::vector<QString> options;
bool operator==(Oid _oid) const { return oid == _oid; }

View file

@ -1,6 +1,283 @@
#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;
}
}
QString PgProc::objectName() const
{
return name;
}
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)->name;
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)->name;
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)->name;
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;
}

View file

@ -5,6 +5,24 @@
#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 PgSchemaObject {
public:
@ -28,18 +46,24 @@ public:
char provolatile = '\0'; // char
char parallel = '\0'; // char, version >= 9.6
int16_t nargs = 0; // int2
int16_t nargdefaults = 0; // int2
int16_t nargdefaults; // = 0; // int2
Oid rettype = InvalidOid; // oid
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
std::vector<Oid> trftypes; // oid[], version >= 9.5
QString src; // text
QString bin; // text
std::vector<QString> config; // text[]
std::vector<QString> acl; // aclitem[]
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; }
@ -47,6 +71,19 @@ public:
bool operator<(const PgProc &rhs) const { return oid < rhs.oid; }
virtual QString objectName() const override;
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

@ -28,17 +28,22 @@ PgProc PgProcContainer::loadElem(const Pgsql::Row &row)
Pgsql::Col col(row);
PgProc v(m_catalog);
Oid namespace_oid;
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 >> v.oid >> v.name >> namespace_oid >> 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;
col.getAsVector<Oid>(std::back_inserter(v.argtypes));
col >> v.allargtypes >> v.argmodes >> v.argnames
>> v.argdefaults;
col >> v.src;
col >> v.bin >> v.config >> v.acl;
col.getAsVector<Oid>(std::back_inserter(argtypes));
col >> allargtypes >> argmodes >> argnames >> argdefaults
>> v.src >> v.bin >> v.config >> v.acl;
v.setSchemaOid(namespace_oid);
v.setArgs(argtypes, allargtypes, argmodes, argnames, argdefaults);
if (minimumVersion(90500)) {
col >> v.trftypes;

View file

@ -31,7 +31,8 @@ public:
PgType();
Oid oid = InvalidOid;
QString name;//"name";"NO"
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"

View file

@ -1,5 +1,6 @@
#include "PgTypeContainer.h"
#include "Pgsql_Connection.h"
#include "Pgsql_Col.h"
#include <algorithm>
//const PgType& PgTypeContainer::getTypeByOid(Oid oid) const
@ -29,7 +30,7 @@
std::string PgTypeContainer::getLoadQuery() const
{
return
"SELECT oid, typname, typnamespace, typowner, typlen, typbyval, typtype, typcategory, \n"
"SELECT oid, format_type(oid, NULL) AS name, typname, typnamespace, typowner, 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"
@ -38,37 +39,11 @@ std::string PgTypeContainer::getLoadQuery() const
PgType PgTypeContainer::loadElem(const Pgsql::Row &row)
{
Pgsql::Col col(row);
PgType v;
v.oid << row.get(0); // InvalidOid;
v.name << row.get(1); //. operator QString(); // "name";"NO"
v.typnamespace << row.get(2); // InvalidOid;//"oid";"NO"
v.owner << row.get(3); // InvalidOid;//"oid";"NO"
v.len << row.get(4); // -1;//"smallint";"NO"
v.byval << row.get(5); // false;//"boolean";"NO"
v.type << row.get(6);//""char"";"NO"
v.category << row.get(7);//""char"";"NO"
v.ispreferred << row.get(8); //false;//"boolean";"NO"
v.isdefined << row.get(9); //false;//"boolean";"NO"
v.delim << row.get(10); //""char"";"NO"
v.relid << row.get(11); // InvalidOid;//"oid";"NO"
v.elem << row.get(12); // InvalidOid;//"oid";"NO"
v.array << row.get(13); // InvalidOid;//"oid";"NO"
v.input << row.get(14);//regproc";"NO"
v.output << row.get(15);//"regproc";"NO"
v.receive << row.get(16);//"regproc";"NO"
v.send << row.get(17);//"regproc";"NO"
v.modin << row.get(18);//"regproc";"NO"
v.modout << row.get(19);//"regproc";"NO"
v.analyze << row.get(20);//"regproc";"NO"
v.align << row.get(21); // //""char"";"NO"
v.storage << row.get(22); //""char"";"NO"
v.notnull << row.get(23); //"boolean";"NO"
v.basetype << row.get(24); //"oid";"NO"
v.typmod << row.get(25); //-1;//"integer";"NO"
v.ndims << row.get(26); //"integer";"NO"
v.collation << row.get(27); //InvalidOid;//"oid";"NO"
v.defaultbin << row.get(28);//"pg_node_tree";"YES"
v.typdefault << row.get(29);//"text";"YES"
v.acl << row.get(30);//"ARRAY";"YES"
col >> v.oid >> v.name >> v.typname >> v.typnamespace >> v.owner >> 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;
return v;
}

View file

@ -227,6 +227,23 @@ QString quoteIdent(QString ident)
}
QString dollarQuoteString(const QString &value)
{
QString def_tag = "BODY";
QString tag = QString("$%1$").arg(def_tag);
int counter = 1;
while (value.indexOf(tag) >= 0)
tag = QString("$%1%2$").arg(def_tag, counter++);
return tag
+ value
+ tag;
}
QString genSchemaPrefix(const PgNamespace &ns)
{
QString str;

View file

@ -13,6 +13,10 @@ QString escapeLiteral(const QString &input);
bool identNeedsQuotes(QString ident);
QString quoteIdent(QString ident);
/// Puts a string in dollar quote for passing in a query
/// the standard quotes are $BODY$ if the tag already exists in the string it will start numbering
QString dollarQuoteString(const QString &value);
QString genFQTableName(const PgDatabaseCatalog &catalog, const PgClass &cls);
QString getDropConstraintDefinition(const PgDatabaseCatalog &catalog, const PgConstraint &constraint);

View file

@ -155,3 +155,16 @@ else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../pgsq
else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../pgsql/release/pgsql.lib
else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../pgsql/debug/pgsql.lib
else:unix:!macx: PRE_TARGETDEPS += $$OUT_PWD/../pgsql/libpgsql.a
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../core/release/ -lcore
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../core/debug/ -lcore
else:unix: LIBS += -L$$OUT_PWD/../core/ -lcore
INCLUDEPATH += $$PWD/../core
DEPENDPATH += $$PWD/../core
win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/release/libcore.a
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/debug/libcore.a
else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/release/core.lib
else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/debug/core.lib
else:unix: PRE_TARGETDEPS += $$OUT_PWD/../core/libcore.a