From 54d4dfface1170a8e05c77f58dc24ebd338074e5 Mon Sep 17 00:00:00 2001 From: eelke Date: Sun, 23 Dec 2018 19:45:04 +0100 Subject: [PATCH] Improved code generation for functions and procedures. alter statements for configuration settings alter statement for owner --- pglablib/catalog/PgObject.cpp | 5 +- pglablib/catalog/PgOwnedObject.cpp | 7 ++ pglablib/catalog/PgOwnedObject.h | 2 + pglablib/catalog/PgProc.cpp | 178 +++++++++++++++++++---------- pglablib/catalog/PgProc.h | 22 +++- 5 files changed, 145 insertions(+), 69 deletions(-) diff --git a/pglablib/catalog/PgObject.cpp b/pglablib/catalog/PgObject.cpp index 951ef7c..4c6dab4 100644 --- a/pglablib/catalog/PgObject.cpp +++ b/pglablib/catalog/PgObject.cpp @@ -30,7 +30,4 @@ const PgDatabaseCatalog& PgObject::catalog() const return *m_catalog; } -void test(PgObject a, PgObject b) -{ - a = b; -} + diff --git a/pglablib/catalog/PgOwnedObject.cpp b/pglablib/catalog/PgOwnedObject.cpp index 5fabf66..7a28980 100644 --- a/pglablib/catalog/PgOwnedObject.cpp +++ b/pglablib/catalog/PgOwnedObject.cpp @@ -2,6 +2,7 @@ #include "PgAuthId.h" #include "PgAuthIdContainer.h" #include "PgDatabaseCatalog.h" +#include "SqlFormattingUtils.h" void PgOwnedObject::setOwnerOid(PgDatabaseCatalog& cat, Oid oid) { @@ -23,3 +24,9 @@ const PgAuthId* PgOwnedObject::owner() const { return m_owner; } + + +QString PgOwnedObject::alterOwnerSql(const QString& ident) const +{ + return QString("\nALTER %1 OWNER TO %2;").arg(ident, quoteIdent(ownerName())); +} diff --git a/pglablib/catalog/PgOwnedObject.h b/pglablib/catalog/PgOwnedObject.h index 8cc54fe..62761dc 100644 --- a/pglablib/catalog/PgOwnedObject.h +++ b/pglablib/catalog/PgOwnedObject.h @@ -14,6 +14,8 @@ public: Oid ownerOid() const; QString ownerName() const; const PgAuthId* owner() const; + + QString alterOwnerSql(const QString& ident) const; private: Oid m_ownerOid = InvalidOid; const PgAuthId * m_owner; diff --git a/pglablib/catalog/PgProc.cpp b/pglablib/catalog/PgProc.cpp index 0dc0517..ee635bf 100644 --- a/pglablib/catalog/PgProc.cpp +++ b/pglablib/catalog/PgProc.cpp @@ -91,9 +91,44 @@ void operator<<(ProcKind &s, const Pgsql::Value &v) case 'w': s = ProcKind::Window; break; + default: + throw std::runtime_error("Unexpected value for ProcKind"); } } +void operator<<(ParallelMode &s, const Pgsql::Value &v) +{ + const char *c = v.c_str(); + switch (*c) { + case 'u': + s = ParallelMode::Unsafe; + break; + case 'r': + s = ParallelMode::Restricted; + break; + case 's': + s = ParallelMode::Safe; + break; + default: + throw std::runtime_error("Unexpected value for ParallelMode"); + } +} + +QString parallelModeToSql(ParallelMode pm, bool explicit_unsafe) +{ + switch (pm) { + case ParallelMode::Unsafe: + return explicit_unsafe ? "PARALLEL UNSAFE" : ""; + case ParallelMode::Restricted: + return "PARALLEL RESTRICTED"; + case ParallelMode::Safe: + return "PARALLEL SAFE"; + default: + throw std::runtime_error("Unexpected value for pm in parallelModeToSql"); + } +} + + void PgProc::setArgs( std::vector argtypes, std::vector allargtypes, @@ -210,89 +245,106 @@ QString PgProc::argSigList(const bool forScript) const } -QString PgProc::createSql() const +const 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()); -// else -// sql += qtDbString(GetSource()); -// } - QString language; - { - auto l = catalog().languages()->getByKey(lang); - if (l) - language = l->objectName(); + if (isAggregate()) { + /// \todo Support for aggregates + createSqlCache = "-- aggregates not supported yet\n"; + return createSqlCache; } - if (language == "c") { + QString quoted_name = QString("%1(%2)").arg(fullyQualifiedQuotedObjectName(), argListWithNames(true)); + QString quoted_sig = QString("%1(%2)").arg(fullyQualifiedQuotedObjectName(), argSigList()); + if (isProcedure()) { + // P CREATE [ OR REPLACE ] PROCEDURE + // name ( [ [ argmode ] [ argname ] argtype [ { DEFAULT | = } default_expr ] [, ...] ] ) + sql = QString("-- Procedure: %1\n\n" + "-- DROP PROCEDURE %1;\n\n" + "CREATE OR REPLACE PROCEDURE %2\n" + ).arg(quoted_sig, quoted_name); + } + else { + // all other kinds are functions + // F CREATE [ OR REPLACE ] FUNCTION + // name ( [ [ argmode ] [ argname ] argtype [ { DEFAULT | = } default_expr ] [, ...] ] ) + // F [ RETURNS rettype + /// \todo F | RETURNS TABLE ( column_name column_type [, ...] ) ] + 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" + ).arg(quoted_sig, quoted_name, return_type); + } + + sql += " AS\n"; + auto language = catalog().languages()->getByKey(lang); + BOOST_ASSERT(language != nullptr); + + if (language->isC()) { + // | AS 'obj_file', 'link_symbol' sql += escapeLiteral(bin) % ", " % escapeLiteral(src); } else { + // | AS 'definition' sql += dollarQuoteString(src); } -// sql += wxT("\n LANGUAGE ") + GetLanguage() + wxT(" "); - sql += "\n LANGUAGE " % language % " "; -// if (GetConnection()->BackendMinimumVersion(8, 4) && GetIsWindow()) -// sql += wxT("WINDOW "); + + // { LANGUAGE lang_name + sql += "\n LANGUAGE " % language->quotedObjectName(); + + /// \todo | TRANSFORM { FOR TYPE type_name } [, ... ] + + // | WINDOW if (isWindow()) - sql += "WINDOW "; -// sql += GetVolatility(); - sql += volatility(); + sql += " WINDOW"; + + if (!isProcedure()) { + // F | IMMUTABLE | STABLE | VOLATILE | [ NOT ] LEAKPROOF + sql += " " % volatility(); + if (leakproof) + sql += " LEAKPROOF"; + // F | CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT + // CALLED ON NULL INPUT is the default so we never specify this + // RETURNS NULL ON NULL INPUT is an alias for STRICT so we use STRICT + if (isstrict) + sql += " STRICT" + ; + /// \todo F | [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER + // if (GetSecureDefiner()) + // sql += wxT(" SECURITY DEFINER"); + // F | PARALLEL { UNSAFE | RESTRICTED | SAFE } + sql += " " % parallelModeToSql(parallel); + // F | COST execution_cost + sql += QString("\n COST %1").arg(static_cast(cost)); + // F | ROWS result_rows + if (retset) + sql += QString("n ROWS %1").arg(static_cast(rows)); + + } - if (leakproof) - sql += " LEAKPROOF"; - if (isstrict) - sql += " STRICT"; -// if (GetSecureDefiner()) -// sql += wxT(" SECURITY DEFINER"); - sql += QString("\n COST %1").arg(cost); - if (retset) - sql += QString("n ROWS %1").arg(rows); sql += ";"; -// 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"); -// } + // | SET configuration_parameter { TO value | = value | FROM CURRENT } + for (auto&& cfg : config) { + auto before = cfg.section('=', 0, 0); // before first = + auto after = cfg.section('=', 1, -1); // everything after first = + if (before != "search_path" && before != "temp_tablespaces") + sql += QString("\nALTER FUNCTION %1 SET %2 TO '%3'").arg(quoted_sig, before, after); + else + sql += QString("\nALTER FUNCTION %1 SET %2 TO %3").arg(quoted_sig, before, after); + } // sql += wxT("\n") -// + GetOwnerSql(8, 0, wxT("FUNCTION ") + qtSig) // + GetGrant(wxT("X"), wxT("FUNCTION ") + qtSig); + sql += alterOwnerSql("FUNCTION " + quoted_sig); + // if (!GetComment().IsNull()) // { // sql += wxT("COMMENT ON FUNCTION ") + qtSig diff --git a/pglablib/catalog/PgProc.h b/pglablib/catalog/PgProc.h index 49d5083..88c24d5 100644 --- a/pglablib/catalog/PgProc.h +++ b/pglablib/catalog/PgProc.h @@ -34,6 +34,24 @@ enum class ProcKind { void operator<<(ProcKind &s, const Pgsql::Value &v); +enum class ParallelMode { + Unsafe, // u, default + Restricted, // r, + Safe // s +}; + +void operator<<(ParallelMode &s, const Pgsql::Value &v); + +/** + * @brief parallelModeToSql + * @param pm The parallel mode + * @param explicit_unsafe When false an empty string is return for unsage when true + * a "PARALLEL UNSAFE" is returned. + * @return + */ +QString parallelModeToSql(ParallelMode pm, bool explicit_unsafe = false); + + class PgProc: public PgNamespaceObject, public PgOwnedObject { public: using PgNamespaceObject::PgNamespaceObject; @@ -55,7 +73,7 @@ public: bool isstrict = false; // bool bool retset = false; // bool char provolatile = '\0'; // char - char parallel = '\0'; // char, version >= 9.6 + ParallelMode parallel = ParallelMode::Unsafe; // char, version >= 9.6 int16_t nargs = 0; // int2 int16_t nargdefaults; // = 0; // int2 Oid rettype = InvalidOid; // oid @@ -81,7 +99,7 @@ public: // bool operator<(Oid _oid) const { return oid < _oid; } // bool operator<(const PgProc &rhs) const { return oid < rhs.oid; } - QString createSql() const; + const QString& createSql() const; QString argListWithNames(bool multiline = false) const; QString argSigList(const bool forScript = false) const;