Hi,

Please ignore my previous patch, find the attached updated one.

Thanks,
Khushboo

On Wed, Jun 6, 2018 at 12:12 PM, Khushboo Vashi <
khushboo.va...@enterprisedb.com> wrote:

> Hi,
>
> Please find the attached patch to fix #3362 - Fix the functions for PG
> v11, and add support procedure for PG v11.
>
> Thanks,
> Khushboo
>
>
diff --git a/docs/en_US/procedure_dialog.rst b/docs/en_US/procedure_dialog.rst
index da5f34f..82c2227 100644
--- a/docs/en_US/procedure_dialog.rst
+++ b/docs/en_US/procedure_dialog.rst
@@ -4,7 +4,7 @@
 The Procedure Dialog
 ********************
 
-Use the *Procedure* dialog to create a procedure; procedures are supported by EDB Postgres Advanced Server. The *Procedure* dialog allows you to implement options of the CREATE PROCEDURE command; for more information about the CREATE PROCEDURE SQL command, please see the Database Compatibility for Oracle Developer's, available at:
+Use the *Procedure* dialog to create a procedure; procedures are supported by PostgreSQL v11 and EDB Postgres Advanced Server. The *Procedure* dialog allows you to implement options of the CREATE PROCEDURE command; for more information about the CREATE PROCEDURE SQL command, please see the Database Compatibility for Oracle Developer's, available at:
 
      http://www.enterprisedb.com
 
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/__init__.py
index d8e7a68..7fecade 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/__init__.py
@@ -1592,10 +1592,11 @@ class ProcedureModule(SchemaChildModule):
         """
         super(ProcedureModule, self).__init__(*args, **kwargs)
 
-        self.min_ver = 90100
+        self.min_ver = 110000
         self.max_ver = None
+        self.min_ppasver = 90100
         self.min_gpdbver = 1000000000
-        self.server_type = ['ppas']
+        self.server_type = ['pg', 'ppas']
 
     def get_nodes(self, gid, sid, did, scid):
         """
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/procedure.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/procedure.js
index 041b509..1e02c99 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/procedure.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/procedure.js
@@ -71,11 +71,7 @@ define('pgadmin.node.procedure', [
         if ('catalog' in node_hierarchy)
           return false;
 
-        // Procedures supported only in PPAS
-        return (
-          'server' in node_hierarchy &&
-            node_hierarchy['server'].server_type == 'ppas'
-        );
+        return true;
       },
       model: Function.model.extend({
         defaults: _.extend({},
@@ -103,20 +99,24 @@ define('pgadmin.node.procedure', [
             return true;
           }
           switch(this.name){
-          case 'provolatility':
+          case 'provolatile':
           case 'proisstrict':
-          case 'prosecdef':
           case 'procost':
           case 'proleakproof':
           case 'variables':
-            return this.node_info.server.version < 90500;
+            return (this.node_info.server.version < 90500 || this.node_info.server.type != 'ppas');
+          case 'prosecdef':
+            return (this.node_info.server.version < 90500);
           case 'prorows':
             var server = this.node_info.server;
             return !(server.version >= 90500 && m.get('proretset') == true);
           case 'funcowner':
-          case 'lanname':
           case 'proargs':
             return true;
+          case 'proparallel':
+            return (this.node_info.server.version < 90600 || this.node_info.server.type != 'ppas');
+          case 'lanname':
+            return this.node_info.server.version < 110000;
           default:
             return false;
           }
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/coll_stats.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/coll_stats.sql
new file mode 100644
index 0000000..cc4f734
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/coll_stats.sql
@@ -0,0 +1,20 @@
+SELECT
+    funcname AS {{ conn|qtIdent(_('Name')) }},
+    calls AS {{ conn|qtIdent(_('Number of calls')) }},
+    total_time AS {{ conn|qtIdent(_('Total time')) }},
+    self_time AS {{ conn|qtIdent(_('Self time')) }}
+FROM
+    pg_stat_user_functions
+WHERE
+    schemaname = {{schema_name|qtLiteral}}
+    AND funcid IN (
+        SELECT p.oid
+        FROM
+            pg_proc p
+        JOIN
+            pg_type typ ON typ.oid=p.prorettype
+        WHERE
+            p.prokind IN ('f', 'w')
+            AND typname NOT IN ('trigger', 'event_trigger')
+    )
+ORDER BY funcname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/create.sql
new file mode 100644
index 0000000..01cdf51
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/create.sql
@@ -0,0 +1,70 @@
+{% import 'macros/functions/security.macros' as SECLABEL %}
+{% import 'macros/functions/privilege.macros' as PRIVILEGE %}
+{% import 'macros/functions/variable.macros' as VARIABLE %}
+{% set is_columns = [] %}
+{% if data %}
+{% if query_for == 'sql_panel' and func_def is defined %}
+CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{func_def}}
+{% else %}
+CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({% if data.arguments %}
+{% for p in data.arguments %}{% if p.argmode %}{{p.argmode}} {% endif %}{% if p.argname %}{{ conn|qtIdent(p.argname) }} {% endif %}{% if p.argtype %}{{ conn|qtTypeIdent(p.argtype) }}{% endif %}{% if p.argdefval %} DEFAULT {{p.argdefval}}{% endif %}
+{% if not loop.last %}, {% endif %}
+{% endfor %}
+{% endif -%}
+)
+{% endif %}
+    RETURNS{% if data.proretset and (data.prorettypename.startswith('SETOF ') or data.prorettypename.startswith('TABLE')) %} {{ data.prorettypename }} {% elif data.proretset %} SETOF {{ conn|qtTypeIdent(data.prorettypename) }}{% else %} {{ conn|qtTypeIdent(data.prorettypename) }}{% endif %}
+
+    LANGUAGE {{ data.lanname|qtLiteral }}
+{% if data.procost %}
+
+    COST {{data.procost}}
+{% endif %}
+    {% if data.provolatile %}{% if data.provolatile == 'i' %}IMMUTABLE{% elif data.provolatile == 's' %}STABLE{% else %}VOLATILE{% endif %} {% endif %}{% if data.proleakproof %}LEAKPROOF {% endif %}
+{% if data.proisstrict %}STRICT {% endif %}
+{% if data.prosecdef %}SECURITY DEFINER {% endif %}
+{% if data.proiswindow %}WINDOW {% endif %}
+{% if data.proparallel and (data.proparallel == 'r' or data.proparallel == 's') %}
+{% if data.proparallel == 'r' %}PARALLEL RESTRICTED{% elif data.proparallel == 's' %}PARALLEL SAFE{% endif %}{% endif -%}
+{% if data.prorows and (data.prorows | int) > 0 %}
+
+    ROWS {{data.prorows}}{% endif -%}{% if data.variables %}{% for v in data.variables %}
+
+    SET {{ conn|qtIdent(v.name) }}={{ v.value|qtLiteral }}{% endfor %}
+{% endif %}
+
+AS {% if data.lanname == 'c' %}
+{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
+{% else %}
+$BODY$
+{{ data.prosrc }}
+$BODY${% endif -%};
+{% if data.funcowner %}
+
+ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
+    OWNER TO {{ conn|qtIdent(data.funcowner) }};
+{% endif -%}
+{% if data.acl %}
+{% for p in data.acl %}
+
+{{ PRIVILEGE.SET(conn, "FUNCTION", p.grantee, data.name, p.without_grant, p.with_grant, data.pronamespace, data.func_args_without)}}
+{% endfor %}{% endif %}
+{% if data.revoke_all %}
+
+{{ PRIVILEGE.UNSETALL(conn, "FUNCTION", "PUBLIC", data.name, data.pronamespace, data.func_args_without)}}
+{% endif %}
+{% if data.description %}
+
+COMMENT ON FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
+    IS {{ data.description|qtLiteral  }};
+{% endif -%}
+{% if data.seclabels %}
+{% for r in data.seclabels %}
+{% if r.label and r.provider %}
+
+{{ SECLABEL.SET(conn, 'FUNCTION', data.name, r.provider, r.label, data.pronamespace, data.func_args_without) }}
+{% endif %}
+{% endfor %}
+{% endif -%}
+
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/delete.sql
new file mode 100644
index 0000000..540044c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/delete.sql
@@ -0,0 +1,21 @@
+{% if scid and fnid %}
+SELECT
+    pr.proname as name, '(' || COALESCE(pg_catalog
+    .pg_get_function_identity_arguments(pr.oid), '') || ')' as func_args,
+    nspname
+FROM
+    pg_proc pr
+JOIN
+    pg_type typ ON typ.oid=prorettype
+JOIN
+    pg_namespace nsp ON nsp.oid=pr.pronamespace
+WHERE
+    pr.prokind IN ('f', 'w')
+    AND pronamespace = {{scid}}::oid
+    AND typname NOT IN ('trigger', 'event_trigger')
+    AND pr.oid = {{fnid}};
+{% endif %}
+
+{% if name %}
+DROP FUNCTION {{ conn|qtIdent(nspname, name) }}{{func_args}}{% if cascade %} CASCADE{% endif %};
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/get_definition.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/get_definition.sql
new file mode 100644
index 0000000..ab25dbf
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/get_definition.sql
@@ -0,0 +1,15 @@
+SELECT
+    pg_get_functiondef({{fnid}}::oid) AS func_def,
+    COALESCE(pg_catalog.pg_get_function_identity_arguments(pr.oid), '') as
+    func_with_identity_arguments,
+    nspname,
+    pr.proname as proname,
+    COALESCE(pg_catalog.pg_get_function_arguments(pr.oid), '') as func_args
+FROM
+    pg_proc pr
+JOIN
+    pg_namespace nsp ON nsp.oid=pr.pronamespace
+WHERE
+    pr.prokind IN ('f', 'w')
+    AND pronamespace = {{scid}}::oid
+    AND pr.oid = {{fnid}}::oid;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/get_oid.sql
new file mode 100644
index 0000000..93a2107
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/get_oid.sql
@@ -0,0 +1,17 @@
+SELECT
+    pr.oid, pr.proname || '(' || COALESCE(pg_catalog
+    .pg_get_function_identity_arguments(pr.oid), '') || ')' as name,
+    lanname, pg_get_userbyid(proowner) as funcowner, pr.pronamespace as nsp
+FROM
+    pg_proc pr
+JOIN
+    pg_type typ ON typ.oid=prorettype
+JOIN
+    pg_language lng ON lng.oid=prolang
+JOIN
+    pg_namespace nsp ON nsp.oid=pr.pronamespace
+    AND nsp.nspname={{ nspname|qtLiteral }}
+WHERE
+    pr.prokind IN ('f', 'w')
+    AND typname NOT IN ('trigger', 'event_trigger')
+    AND pr.proname = {{ name|qtLiteral }};
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/node.sql
new file mode 100644
index 0000000..5dae8d1
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/node.sql
@@ -0,0 +1,22 @@
+SELECT
+    pr.oid, pr.proname || '(' || COALESCE(pg_catalog.pg_get_function_identity_arguments(pr.oid), '') || ')' as name,
+    lanname, pg_get_userbyid(proowner) as funcowner, description
+FROM
+    pg_proc pr
+JOIN
+    pg_type typ ON typ.oid=prorettype
+JOIN
+    pg_language lng ON lng.oid=prolang
+LEFT OUTER JOIN
+    pg_description des ON (des.objoid=pr.oid AND des.classoid='pg_proc'::regclass)
+WHERE
+   pr.prokind IN ('f', 'w')
+{% if fnid %}
+    AND pr.oid = {{ fnid|qtLiteral }}
+{% endif %}
+{% if scid %}
+    AND pronamespace = {{scid}}::oid
+{% endif %}
+    AND typname NOT IN ('trigger', 'event_trigger')
+ORDER BY
+    proname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/properties.sql
new file mode 100644
index 0000000..50c2ca0
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/properties.sql
@@ -0,0 +1,33 @@
+SELECT
+    pr.oid, pr.xmin, pr.*, pr.prosrc AS prosrc_c,
+    pr.proname AS name, pg_get_function_result(pr.oid) AS prorettypename,
+    typns.nspname AS typnsp, lanname, proargnames, oidvectortypes(proargtypes) AS proargtypenames,
+    pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) AS proargdefaultvals,
+    pronargdefaults, proconfig, pg_get_userbyid(proowner) AS funcowner, description,
+    CASE WHEN pr.prokind = 'w' THEN TRUE ELSE FALSE END AS proiswindow,
+    (SELECT
+        array_agg(provider || '=' || label)
+    FROM
+        pg_seclabel sl1
+    WHERE
+        sl1.objoid=pr.oid) AS seclabels
+FROM
+    pg_proc pr
+JOIN
+    pg_type typ ON typ.oid=prorettype
+JOIN
+    pg_namespace typns ON typns.oid=typ.typnamespace
+JOIN
+    pg_language lng ON lng.oid=prolang
+LEFT OUTER JOIN
+    pg_description des ON (des.objoid=pr.oid AND des.classoid='pg_proc'::regclass)
+WHERE
+    pr.prokind IN ('f', 'w')
+{% if fnid %}
+    AND pr.oid = {{fnid}}::oid
+{% else %}
+    AND pronamespace = {{scid}}::oid
+{% endif %}
+    AND typname NOT IN ('trigger', 'event_trigger')
+ORDER BY
+    proname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/update.sql
new file mode 100644
index 0000000..92bd24b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/function/pg/sql/11_plus/update.sql
@@ -0,0 +1,116 @@
+{% import 'macros/functions/security.macros' as SECLABEL %}
+{% import 'macros/functions/privilege.macros' as PRIVILEGE %}
+{% import 'macros/functions/variable.macros' as VARIABLE %}{% if data %}
+{% set name = o_data.name %}
+{% if data.name %}
+{% if data.name != o_data.name %}
+ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, o_data.name) }}({{
+o_data.proargtypenames }})
+    RENAME TO {{ conn|qtIdent(data.name) }};
+{% set name = data.name %}
+{% endif %}
+{% endif -%}
+{% if data.change_func  %}
+
+CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if data.arguments %}
+{% for p in data.arguments %}{% if p.argmode %}{{p.argmode}} {% endif %}{% if p.argname %}{{ conn|qtIdent(p.argname) }} {% endif %}{% if p.argtype %}{{ conn|qtTypeIdent(p.argtype) }}{% endif %}{% if p.argdefval %} DEFAULT {{p.argdefval}}{% endif %}
+{% if not loop.last %},{% endif %}
+{% endfor %}
+{% endif -%}
+)
+    RETURNS {{ o_data.prorettypename }}
+{% if 'lanname' in data %}
+    LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
+    LANGUAGE {{ o_data.lanname|qtLiteral }}
+    {% endif %}{% if 'provolatile' in data and data.provolatile %}{{ data.provolatile }} {% elif 'provolatile' not in data and o_data.provolatile %}{{ o_data.provolatile }}{% endif %}
+{% if ('proleakproof' in data and data.proleakproof) or ('proleakproof' not in data and o_data.proleakproof) %} LEAKPROOF{% elif 'proleakproof' in data and not data.proleakproof %} NOT LEAKPROOF{% endif %}
+{% if ('proisstrict' in data and data.proisstrict) or ('proisstrict' not in data and o_data.proisstrict) %} STRICT{% endif %}
+{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %}
+{% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %}
+
+    {% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %}
+
+    {% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %}
+
+    ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %}    ROWS {{o_data.prorows}} {%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}
+
+    SET {{ conn|qtIdent(v.name) }}={{ v.value|qtLiteral }}{% endfor -%}
+    {% endif %}
+
+AS {% if 'probin' in data or 'prosrc_c' in data %}
+{% if 'probin' in data %}{{ data.probin|qtLiteral }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %}
+$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %}
+{{ o_data.probin|qtLiteral }}, {{ o_data.prosrc_c|qtLiteral }}{% else %}
+$BODY${{ o_data.prosrc }}$BODY${% endif -%};
+{% endif -%}
+{% if data.funcowner %}
+
+ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }})
+    OWNER TO {{ conn|qtIdent(data.funcowner) }};
+{% endif -%}
+{# The SQL generated below will change priviledges #}
+{% if data.acl %}
+{% if 'deleted' in data.acl %}
+{% for priv in data.acl.deleted %}
+
+{{ PRIVILEGE.UNSETALL(conn, 'FUNCTION', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }}
+{% endfor %}
+{% endif -%}
+{% if 'changed' in data.datacl %}
+{% for priv in data.acl.changed %}
+
+{{ PRIVILEGE.UNSETALL(conn, 'FUNCTION', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }}
+
+{{ PRIVILEGE.SET(conn, 'FUNCTION', priv.grantee, name, priv.without_grant, priv.with_grant, o_data.pronamespace, o_data.proargtypenames) }}
+{% endfor %}
+{% endif -%}
+{% if 'added' in data.acl %}
+{% for priv in data.acl.added %}
+
+{{ PRIVILEGE.SET(conn, 'FUNCTION', priv.grantee, name, priv.without_grant, priv.with_grant, o_data.pronamespace, o_data.proargtypenames) }}
+{% endfor %}{% endif -%}
+{% endif -%}
+{% if data.change_func == False %}
+{% if data.variables %}
+{% if 'deleted' in data.variables and data.variables.deleted|length > 0 %}
+
+{{ VARIABLE.UNSET(conn, 'FUNCTION', name, data.variables.deleted, o_data.pronamespace, o_data.proargtypenames) }}
+{% endif -%}
+{% if 'merged_variables' in data and data.merged_variables|length > 0 %}
+
+{{ VARIABLE.SET(conn, 'FUNCTION', name, data.merged_variables, o_data.pronamespace, o_data.proargtypenames) }}
+{% endif -%}
+{% endif -%}
+{% endif -%}
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+
+{{ SECLABEL.UNSET(conn, 'FUNCTION', name, r.provider, o_data.pronamespace, o_data.proargtypenames) }}
+{% endfor %}
+{% endif -%}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+
+{{ SECLABEL.SET(conn, 'FUNCTION', name, r.provider, r.label, o_data.pronamespace, o_data.proargtypenames) }}
+{% endfor %}
+{% endif -%}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+
+{{ SECLABEL.SET(conn, 'FUNCTION', name, r.provider, r.label, o_data.pronamespace, o_data.proargtypenames) }}
+{% endfor %}
+{% endif -%}
+{% if data.description is defined and data.description != o_data.description%}
+
+COMMENT ON FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }})
+    IS {{ data.description|qtLiteral }};
+{% endif -%}
+
+{% if data.pronamespace %}
+
+ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }})
+    SET SCHEMA {{ conn|qtIdent(data.pronamespace) }};
+{% endif -%}
+
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/acl.sql
new file mode 100644
index 0000000..2e7aae5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/acl.sql
@@ -0,0 +1,35 @@
+SELECT
+    COALESCE(gt.rolname, 'PUBLIC') AS grantee,
+    g.rolname AS grantor, array_agg(privilege_type) AS privileges,
+    array_agg(is_grantable) AS grantable
+FROM
+    (SELECT
+        d.grantee, d.grantor, d.is_grantable,
+        CASE d.privilege_type
+        WHEN 'CONNECT' THEN 'c'
+        WHEN 'CREATE' THEN 'C'
+        WHEN 'DELETE' THEN 'd'
+        WHEN 'EXECUTE' THEN 'X'
+        WHEN 'INSERT' THEN 'a'
+        WHEN 'REFERENCES' THEN 'x'
+        WHEN 'SELECT' THEN 'r'
+        WHEN 'TEMPORARY' THEN 'T'
+        WHEN 'TRIGGER' THEN 't'
+        WHEN 'TRUNCATE' THEN 'D'
+        WHEN 'UPDATE' THEN 'w'
+        WHEN 'USAGE' THEN 'U'
+        ELSE 'UNKNOWN'
+        END AS privilege_type
+    FROM
+        (SELECT
+            (d).grantee AS grantee, (d).grantor AS grantor,
+            (d).is_grantable AS is_grantable,
+            (d).privilege_type AS privilege_type
+        FROM
+            (SELECT aclexplode(db.proacl) AS d FROM pg_proc db
+            WHERE db.oid = {{fnid}}::OID) a
+        ) d
+    ) d
+    LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+    LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY g.rolname, gt.rolname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/coll_stats.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/coll_stats.sql
new file mode 100644
index 0000000..5924679
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/coll_stats.sql
@@ -0,0 +1,20 @@
+SELECT
+    funcname AS {{ conn|qtIdent(_('Name')) }},
+    calls AS {{ conn|qtIdent(_('Number of calls')) }},
+    total_time AS {{ conn|qtIdent(_('Total time')) }},
+    self_time AS {{ conn|qtIdent(_('Self time')) }}
+FROM
+    pg_stat_user_functions
+WHERE
+    schemaname = {{schema_name|qtLiteral}}
+    AND funcid IN (
+        SELECT p.oid
+        FROM
+            pg_proc p
+        JOIN
+            pg_type typ ON typ.oid=p.prorettype
+        WHERE
+            p.prokind = 'p'::char
+            AND typname NOT IN ('trigger', 'event_trigger')
+    )
+ORDER BY funcname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/create.sql
new file mode 100644
index 0000000..ffff17d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/create.sql
@@ -0,0 +1,46 @@
+{% import 'macros/functions/security.macros' as SECLABEL %}
+{% import 'macros/functions/privilege.macros' as PRIVILEGE %}
+{% import 'macros/functions/variable.macros' as VARIABLE %}
+{% set is_columns = [] %}
+{% if data %}
+{% if query_for == 'sql_panel' and func_def is defined %}
+CREATE OR REPLACE  PROCEDURE {{func_def}}
+{% else %}
+CREATE OR REPLACE PROCEDURE {{ conn|qtIdent(data.pronamespace, data.name) }}{% if data.arguments is defined %}
+({% for p in data.arguments %}{% if p.argmode %}{{p.argmode}} {% endif %}{% if p.argname %}{{ conn|qtIdent(p.argname)}} {% endif %}{% if p.argtype %}{{ conn|qtTypeIdent(p.argtype) }}{% endif %}{% if p.argdefval %} DEFAULT {{p.argdefval}}{% endif %}
+{% if not loop.last %}, {% endif %}
+{% endfor -%}
+{% endif %}
+)
+{% endif %}
+LANGUAGE {{ data.lanname|qtLiteral }}
+{% if data.prosecdef %}SECURITY DEFINER {% endif %}
+
+AS {% if data.lanname == 'c' %}
+{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
+{% else %}
+$BODY${{ data.prosrc }}$BODY${% endif -%};
+{% if data.acl and not is_sql %}
+{% for p in data.acl %}
+
+{{ PRIVILEGE.SET(conn, "PROCEDURE", p.grantee, data.name, p.without_grant, p.with_grant, data.pronamespace, data.func_args_without)}}
+{% endfor %}{% endif %}
+{% if data.revoke_all %}
+
+{{ PRIVILEGE.UNSETALL(conn, "PROCEDURE", "PUBLIC", data.name, data.pronamespace, data.func_args_without)}}
+{% endif %}
+{% if data.description %}
+
+COMMENT ON PROCEDURE {{ conn|qtIdent(data.pronamespace, data.name) }}
+    IS {{ data.description|qtLiteral  }};
+{% endif -%}
+{% if data.seclabels %}
+{% for r in data.seclabels %}
+{% if r.label and r.provider %}
+
+{{ SECLABEL.SET(conn, 'PROCEDURE', data.name, r.provider, r.label, data.pronamespace, data.func_args_without) }}
+{% endif %}
+{% endfor %}
+{% endif -%}
+
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/delete.sql
new file mode 100644
index 0000000..b4fcdc2
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/delete.sql
@@ -0,0 +1,21 @@
+{% if scid and fnid %}
+SELECT
+    pr.proname as name, '(' || COALESCE(pg_catalog
+    .pg_get_function_identity_arguments(pr.oid), '') || ')' as func_args,
+    nspname
+FROM
+    pg_proc pr
+JOIN
+    pg_type typ ON typ.oid=prorettype
+JOIN
+    pg_namespace nsp ON nsp.oid=pr.pronamespace
+WHERE
+    pr.prokind = 'p'::char
+    AND pronamespace = {{scid}}::oid
+    AND typname NOT IN ('trigger', 'event_trigger')
+    AND pr.oid = {{fnid}};
+{% endif %}
+
+{% if name %}
+DROP PROCEDURE {{ conn|qtIdent(nspname, name) }};
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/get_definition.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/get_definition.sql
new file mode 100644
index 0000000..4c2e9d9
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/get_definition.sql
@@ -0,0 +1,15 @@
+SELECT
+    pg_get_functiondef({{fnid}}::oid) AS func_def,
+    COALESCE(pg_catalog.pg_get_function_identity_arguments(pr.oid), '') as
+    func_with_identity_arguments,
+    nspname,
+    pr.proname as proname,
+    COALESCE(pg_catalog.pg_get_function_arguments(pr.oid), '') as func_args
+FROM
+    pg_proc pr
+JOIN
+    pg_namespace nsp ON nsp.oid=pr.pronamespace
+WHERE
+    pr.prokind = 'p'::char
+    AND pronamespace = {{scid}}::oid
+    AND pr.oid = {{fnid}}::oid;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/get_languages.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/get_languages.sql
new file mode 100644
index 0000000..f81ddfb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/get_languages.sql
@@ -0,0 +1,4 @@
+SELECT
+    lanname as label, lanname as value
+FROM
+    pg_language;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/get_oid.sql
new file mode 100644
index 0000000..be98b53
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/get_oid.sql
@@ -0,0 +1,17 @@
+SELECT
+    pr.oid, pr.proname || '(' || COALESCE(pg_catalog
+    .pg_get_function_identity_arguments(pr.oid), '') || ')' as name,
+    lanname, pg_get_userbyid(proowner) as funcowner, pr.pronamespace AS nsp
+FROM
+    pg_proc pr
+JOIN
+    pg_type typ ON typ.oid=prorettype
+JOIN
+    pg_language lng ON lng.oid=prolang
+JOIN
+    pg_namespace nsp ON nsp.oid=pr.pronamespace
+    AND nsp.nspname={{ nspname|qtLiteral }}
+WHERE
+    pr.prokind = 'p'::char
+    AND typname NOT IN ('trigger', 'event_trigger')
+    AND pr.proname = {{ name|qtLiteral }};
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/get_out_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/get_out_types.sql
new file mode 100644
index 0000000..64a1187
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/get_out_types.sql
@@ -0,0 +1,6 @@
+SELECT
+    format_type(oid, NULL) AS out_arg_type
+FROM
+    pg_type
+WHERE
+    oid = {{ out_arg_oid }}::oid;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/get_schema.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/get_schema.sql
new file mode 100644
index 0000000..127d4b9
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/get_schema.sql
@@ -0,0 +1,6 @@
+SELECT
+    nspname
+FROM
+    pg_namespace
+WHERE
+    oid = {{ scid }}::oid;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/get_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/get_types.sql
new file mode 100644
index 0000000..2a5582e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/get_types.sql
@@ -0,0 +1,20 @@
+SELECT
+    *
+FROM
+    (SELECT
+        format_type(t.oid,NULL) AS typname,
+        CASE WHEN typelem > 0 THEN typelem ELSE t.oid END as elemoid, typlen, typtype, t.oid, nspname,
+        (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup
+    FROM
+        pg_type t
+    JOIN
+        pg_namespace nsp ON typnamespace=nsp.oid
+    WHERE
+        (NOT (typname = 'unknown' AND nspname = 'pg_catalog'))
+    AND
+        (
+            typtype IN ('b', 'c', 'd', 'e', 'p', 'r')
+            AND typname NOT IN ('any', 'trigger', 'language_handler', 'event_trigger')
+        )
+    ) AS dummy
+ORDER BY nspname <> 'pg_catalog', nspname <> 'public', nspname, 1;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/node.sql
new file mode 100644
index 0000000..89dc768
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/node.sql
@@ -0,0 +1,29 @@
+SELECT
+    pr.oid,
+    CASE WHEN
+        pg_catalog.pg_get_function_identity_arguments(pr.oid) <> ''
+    THEN
+        pr.proname || '(' || pg_catalog.pg_get_function_identity_arguments(pr.oid) || ')'
+    ELSE
+        pr.proname
+    END AS name,
+    lanname, pg_get_userbyid(proowner) AS funcowner, description
+FROM
+    pg_proc pr
+JOIN
+    pg_type typ ON typ.oid=prorettype
+JOIN
+    pg_language lng ON lng.oid=prolang
+LEFT OUTER JOIN
+    pg_description des ON (des.objoid=pr.oid AND des.classoid='pg_proc'::regclass)
+WHERE
+    pr.prokind = 'p'::char
+{% if fnid %}
+    AND pr.oid = {{ fnid|qtLiteral }}
+{% endif %}
+{% if scid %}
+    AND pronamespace = {{scid}}::oid
+{% endif %}
+    AND typname NOT IN ('trigger', 'event_trigger')
+ORDER BY
+    proname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/properties.sql
new file mode 100644
index 0000000..79bea86
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/properties.sql
@@ -0,0 +1,39 @@
+SELECT
+    pr.oid, pr.xmin, pr.*, pr.prosrc AS prosrc_c,
+    pr.proname AS name, pg_get_function_result(pr.oid) AS prorettypename,
+    typns.nspname AS typnsp, lanname, proargnames, oidvectortypes(proargtypes) AS proargtypenames,
+    pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) AS proargdefaultvals,
+    pronargdefaults, proconfig, pg_get_userbyid(proowner) AS funcowner, description,
+    (CASE WHEN
+        pg_catalog.pg_get_function_identity_arguments(pr.oid) <> ''
+    THEN
+        pr.proname || '(' || pg_catalog.pg_get_function_identity_arguments(pr.oid) || ')'
+    ELSE
+        pr.proname
+    END) AS name_with_args,
+    (SELECT
+        array_agg(provider || '=' || label)
+    FROM
+        pg_seclabel sl1
+    WHERE
+        sl1.objoid=pr.oid) AS seclabels
+FROM
+    pg_proc pr
+JOIN
+    pg_type typ ON typ.oid=prorettype
+JOIN
+    pg_namespace typns ON typns.oid=typ.typnamespace
+JOIN
+    pg_language lng ON lng.oid=prolang
+LEFT OUTER JOIN
+    pg_description des ON (des.objoid=pr.oid AND des.classoid='pg_proc'::regclass)
+WHERE
+    pr.prokind = 'p'
+    AND typname NOT IN ('trigger', 'event_trigger')
+{% if fnid %}
+    AND pr.oid = {{fnid}}::oid
+{% else %}
+    AND pronamespace = {{scid}}::oid
+{% endif %}
+ORDER BY
+    proname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/stats.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/stats.sql
new file mode 100644
index 0000000..2e276b6
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/stats.sql
@@ -0,0 +1,8 @@
+SELECT
+    calls AS {{ conn|qtIdent(_('Number of calls')) }},
+    total_time AS {{ conn|qtIdent(_('Total time')) }},
+    self_time AS {{ conn|qtIdent(_('Self time')) }}
+FROM
+    pg_stat_user_functions
+WHERE
+    funcid = {{fnid}}::OID
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/update.sql
new file mode 100644
index 0000000..38cd413
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/update.sql
@@ -0,0 +1,90 @@
+{% import 'macros/functions/security.macros' as SECLABEL %}
+{% import 'macros/functions/privilege.macros' as PRIVILEGE %}{% if data %}
+{% set name = o_data.name %}
+{% if data.name %}
+{% if data.name != o_data.name %}
+ALTER PROCEDURE {{ conn|qtIdent(o_data.pronamespace, o_data.name) }}{% if o_data.proargtypenames %}({{ o_data.proargtypenames }}){% endif %}
+
+    RENAME TO {{ conn|qtIdent(data.name) }};
+{% set name = data.name %}
+{% endif %}
+
+{% endif -%}
+{% if data.change_func  %}
+CREATE OR REPLACE PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }}({% if data.arguments %}{% for p in data.arguments %}{% if p.argmode %}{{p.argmode}} {% endif %}{% if p.argname %}{{ conn|qtIdent(p.argname) }} {% endif %}{% if p.argtype %}{{ conn|qtTypeIdent(p.argtype) }}{% endif %}{% if p.argdefval %} DEFAULT {{p.argdefval}}{% endif %}
+{% if not loop.last %}, {% endif %}
+{% endfor %}
+{% endif %}
+)
+{% if 'lanname' in data %}
+    LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
+    LANGUAGE {{ o_data.lanname|qtLiteral }}
+    {% endif %}
+{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %}SECURITY DEFINER{% endif %}
+
+AS {% if 'probin' in data or 'prosrc_c' in data %}
+{% if 'probin' in data %}{{ data.probin|qtLiteral }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %}
+$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %}
+{{ o_data.probin|qtLiteral }}, {{ o_data.prosrc_c|qtLiteral }}{% else %}
+$BODY${{ o_data.prosrc }}$BODY${% endif -%};
+{% endif -%}
+{% if data.funcowner %}
+
+ALTER PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }}{% if o_data.proargtypenames %}({{ o_data.proargtypenames }}){% endif %}
+    OWNER TO {{ data.funcowner }};
+{% endif -%}
+{# The SQL generated below will change priviledges #}
+{% if data.acl %}
+{% if 'deleted' in data.acl %}
+{% for priv in data.acl.deleted %}
+
+{{ PRIVILEGE.UNSETALL(conn, 'PROCEDURE', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }}
+{% endfor %}
+{% endif -%}
+{% if 'changed' in data.datacl %}
+{% for priv in data.acl.changed %}
+
+{{ PRIVILEGE.UNSETALL(conn, 'PROCEDURE', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }}
+
+{{ PRIVILEGE.SET(conn, 'PROCEDURE', priv.grantee, name, priv.without_grant,
+ priv.with_grant, o_data.pronamespace, o_data.proargtypenames) }}
+{% endfor %}
+{% endif -%}
+{% if 'added' in data.acl %}
+{% for priv in data.acl.added %}
+
+{{ PRIVILEGE.SET(conn, 'PROCEDURE', priv.grantee, name, priv.without_grant, priv.with_grant, o_data.pronamespace, o_data.proargtypenames) }}
+{% endfor %}
+{% endif %}
+{% endif -%}
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+
+{{ SECLABEL.UNSET(conn, 'PROCEDURE', name, r.provider, o_data.pronamespace, o_data.proargtypenames) }}
+{% endfor %}
+{% endif -%}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+
+{{ SECLABEL.SET(conn, 'PROCEDURE', name, r.provider, r.label, o_data.pronamespace, o_data.proargtypenames) }}
+{% endfor %}
+{% endif -%}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+
+{{ SECLABEL.SET(conn, 'PROCEDURE', name, r.provider, r.label, o_data.pronamespace, o_data.proargtypenames) }}
+{% endfor %}
+{% endif -%}
+{% if data.description is defined and data.description != o_data.description%}
+
+COMMENT ON PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }}
+    IS {{ data.description|qtLiteral }};
+{% endif -%}
+{% if data.pronamespace %}
+
+ALTER PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }}
+    SET SCHEMA {{ conn|qtIdent(data.pronamespace) }};
+{% endif -%}
+
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/variables.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/variables.sql
new file mode 100644
index 0000000..5233c71
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedure/pg/sql/default/variables.sql
@@ -0,0 +1,6 @@
+SELECT
+    name, vartype, min_val, max_val, enumvals
+FROM
+    pg_settings
+WHERE
+    context in ('user', 'superuser');
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/coll_stats.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/coll_stats.sql
new file mode 100644
index 0000000..cd8cecd
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/coll_stats.sql
@@ -0,0 +1,20 @@
+SELECT
+    funcname AS {{ conn|qtIdent(_('Name')) }},
+    calls AS {{ conn|qtIdent(_('Number of calls')) }},
+    total_time AS {{ conn|qtIdent(_('Total time')) }},
+    self_time AS {{ conn|qtIdent(_('Self time')) }}
+FROM
+    pg_stat_user_functions
+WHERE
+    schemaname = {{schema_name|qtLiteral}}
+    AND funcid IN (
+        SELECT p.oid
+        FROM
+            pg_proc p
+        JOIN
+            pg_type typ ON typ.oid=p.prorettype
+        WHERE
+            p.prokind IN ('f', 'w')
+            AND typname = 'trigger'
+    )
+ORDER BY funcname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/create.sql
new file mode 100644
index 0000000..20499a9
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/create.sql
@@ -0,0 +1,57 @@
+{% import 'macros/functions/security.macros' as SECLABEL %}
+{% import 'macros/functions/privilege.macros' as PRIVILEGE %}
+{% import 'macros/functions/variable.macros' as VARIABLE %}
+{% set is_columns = [] %}
+{% if data %}
+CREATE FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({% if data.proargnames %}{{data.proargnames}}{% endif %})
+    RETURNS{% if data.proretset %} SETOF{% endif %} {{ conn|qtTypeIdent(data.prorettypename) }}
+    LANGUAGE {{ data.lanname|qtLiteral }}
+{% if data.procost %}
+    COST {{data.procost}}
+{% endif %}
+    {% if data.provolatile %}{% if data.provolatile == 'i' %}IMMUTABLE{% elif data.provolatile == 's' %}STABLE{% else %}VOLATILE{% endif %} {% endif %}{% if data.proleakproof %}LEAKPROOF {% else %}NOT LEAKPROOF {% endif %}
+{% if data.proisstrict %}STRICT {% endif %}
+{% if data.prosecdef %}SECURITY DEFINER {% endif %}
+{% if data.proiswindow %}WINDOW{% endif %}
+{% if data.prorows and (data.prorows | int) > 0 %}
+
+    ROWS {{data.prorows}}{% endif -%}{% if data.variables %}{% for v in data.variables %}
+
+    SET {{ conn|qtIdent(v.name) }}={{ v.value|qtLiteral }}{% endfor %}
+{% endif %}
+
+AS {% if data.lanname == 'c' %}
+{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
+{% else %}
+$BODY$
+{{ data.prosrc }}
+$BODY${% endif -%};
+{% if data.funcowner %}
+
+ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args}})
+    OWNER TO {{ conn|qtIdent(data.funcowner) }};
+{% endif -%}
+{% if data.acl %}
+{% for p in data.acl %}
+
+{{ PRIVILEGE.SET(conn, "FUNCTION", p.grantee, data.name, p.without_grant, p.with_grant, data.pronamespace, data.func_args)}}
+{% endfor %}{% endif %}
+{% if data.revoke_all %}
+
+{{ PRIVILEGE.UNSETALL(conn, "FUNCTION", "PUBLIC", data.name, data.pronamespace, data.func_args_without)}}
+{% endif %}
+{% if data.description %}
+
+COMMENT ON FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args}})
+    IS {{ data.description|qtLiteral  }};
+{% endif -%}
+{% if data.seclabels %}
+{% for r in data.seclabels %}
+{% if r.label and r.provider %}
+
+{{ SECLABEL.SET(conn, 'FUNCTION', data.name, r.provider, r.label, data.pronamespace, data.func_args) }}
+{% endif %}
+{% endfor %}
+{% endif -%}
+
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/delete.sql
new file mode 100644
index 0000000..f7f906b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/delete.sql
@@ -0,0 +1,21 @@
+{% if scid and fnid %}
+SELECT
+    pr.proname as name, '(' || COALESCE(pg_catalog
+    .pg_get_function_identity_arguments(pr.oid), '') || ')' as func_args,
+    nspname
+FROM
+    pg_proc pr
+JOIN
+    pg_type typ ON typ.oid=prorettype
+JOIN
+    pg_namespace nsp ON nsp.oid=pr.pronamespace
+WHERE
+    pr.prokind IN ('f', 'w')
+    AND pronamespace = {{scid}}::oid
+    AND typname IN ('trigger', 'event_trigger')
+    AND pr.oid = {{fnid}};
+{% endif %}
+
+{% if name %}
+DROP FUNCTION {{ conn|qtIdent(nspname, name) }}{{func_args}}{% if cascade %} CASCADE{% endif %};
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/get_definition.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/get_definition.sql
new file mode 100644
index 0000000..ab25dbf
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/get_definition.sql
@@ -0,0 +1,15 @@
+SELECT
+    pg_get_functiondef({{fnid}}::oid) AS func_def,
+    COALESCE(pg_catalog.pg_get_function_identity_arguments(pr.oid), '') as
+    func_with_identity_arguments,
+    nspname,
+    pr.proname as proname,
+    COALESCE(pg_catalog.pg_get_function_arguments(pr.oid), '') as func_args
+FROM
+    pg_proc pr
+JOIN
+    pg_namespace nsp ON nsp.oid=pr.pronamespace
+WHERE
+    pr.prokind IN ('f', 'w')
+    AND pronamespace = {{scid}}::oid
+    AND pr.oid = {{fnid}}::oid;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/get_oid.sql
new file mode 100644
index 0000000..413df91
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/get_oid.sql
@@ -0,0 +1,18 @@
+SELECT
+    pr.oid, pr.proname || '(' || COALESCE(pg_catalog
+    .pg_get_function_identity_arguments(pr.oid), '') || ')' as name,
+    lanname, pg_get_userbyid(proowner) as funcowner, pr.pronamespace AS nsp
+FROM
+    pg_proc pr
+JOIN
+    pg_type typ ON typ.oid=prorettype
+JOIN
+    pg_language lng ON lng.oid=prolang
+JOIN
+    pg_namespace nsp ON nsp.oid=pr.pronamespace
+    AND nsp.nspname={{ nspname|qtLiteral }}
+WHERE
+    pr.prokind IN ('f', 'w')
+    AND typname IN ('trigger', 'event_trigger')
+    AND lanname NOT IN ('edbspl', 'sql', 'internal')
+    AND pr.proname = {{ name|qtLiteral }};
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/node.sql
new file mode 100644
index 0000000..55d7c54
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/node.sql
@@ -0,0 +1,23 @@
+SELECT
+    pr.oid, pr.proname || '()' as name,
+    lanname, pg_get_userbyid(proowner) as funcowner, description
+FROM
+    pg_proc pr
+JOIN
+    pg_type typ ON typ.oid=prorettype
+JOIN
+    pg_language lng ON lng.oid=prolang
+LEFT OUTER JOIN
+    pg_description des ON (des.objoid=pr.oid AND des.classoid='pg_proc'::regclass)
+WHERE
+    pr.prokind IN ('f', 'w')
+{% if fnid %}
+    AND pr.oid = {{ fnid|qtLiteral }}
+{% endif %}
+{% if scid %}
+    AND pronamespace = {{scid}}::oid
+{% endif %}
+    AND typname IN ('trigger', 'event_trigger')
+    AND lanname NOT IN ('edbspl', 'sql', 'internal')
+ORDER BY
+    proname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/properties.sql
new file mode 100644
index 0000000..09d7866
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/properties.sql
@@ -0,0 +1,33 @@
+SELECT
+    pr.oid, pr.xmin, pr.*, pr.prosrc AS prosrc_c,
+    pr.proname AS name, pg_get_function_result(pr.oid) AS prorettypename,
+    typns.nspname AS typnsp, lanname, proargnames, oidvectortypes(proargtypes) AS proargtypenames,
+    pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) AS proargdefaultvals,
+    pronargdefaults, proconfig, pg_get_userbyid(proowner) AS funcowner, description,
+    (SELECT
+        array_agg(provider || '=' || label)
+    FROM
+        pg_seclabel sl1
+    WHERE
+        sl1.objoid=pr.oid) AS seclabels
+FROM
+    pg_proc pr
+JOIN
+    pg_type typ ON typ.oid=prorettype
+JOIN
+    pg_namespace typns ON typns.oid=typ.typnamespace
+JOIN
+    pg_language lng ON lng.oid=prolang
+LEFT OUTER JOIN
+    pg_description des ON (des.objoid=pr.oid AND des.classoid='pg_proc'::regclass)
+WHERE
+    pr.prokind IN ('f', 'w')
+    AND typname IN ('trigger', 'event_trigger')
+    AND lanname NOT IN ('edbspl', 'sql', 'internal')
+{% if fnid %}
+    AND pr.oid = {{fnid}}::oid
+{% else %}
+    AND pronamespace = {{scid}}::oid
+{% endif %}
+ORDER BY
+    proname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/update.sql
new file mode 100644
index 0000000..413be62
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/trigger_function/pg/sql/11_plus/update.sql
@@ -0,0 +1,109 @@
+{% import 'macros/functions/security.macros' as SECLABEL %}
+{% import 'macros/functions/privilege.macros' as PRIVILEGE %}
+{% import 'macros/functions/variable.macros' as VARIABLE %}{% if data %}
+{% set name = o_data.name %}
+{% if data.name %}
+{% if data.name != o_data.name %}
+ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, o_data.name) }}({{
+o_data.proargtypenames }})
+    RENAME TO {{ conn|qtIdent(data.name) }};
+{% set name = data.name %}
+{% endif %}
+{% endif -%}
+{% if data.change_func  %}
+
+CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}()
+    RETURNS {{ o_data.prorettypename }}
+{% if 'lanname' in data %}
+    LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
+    LANGUAGE {{ o_data.lanname|qtLiteral }}
+    {% endif %}{% if 'provolatile' in data and data.provolatile %}{{ data.provolatile }} {% elif 'provolatile' not in data and o_data.provolatile %}{{ o_data.provolatile }}{% endif %}
+{% if ('proleakproof' in data and data.proleakproof) or ('proleakproof' not in data and o_data.proleakproof) %} LEAKPROOF{% elif 'proleakproof' in data and not data.proleakproof %} NOT LEAKPROOF{% endif %}
+{% if ('proisstrict' in data and data.proisstrict) or ('proisstrict' not in data and o_data.proisstrict) %} STRICT{% endif %}
+{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %}
+{% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %}
+
+    {% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %}
+
+    ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %}    ROWS {{o_data.prorows}} {%endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}
+
+    SET {{ conn|qtIdent(v.name) }}={{ v.value|qtLiteral }}{% endfor -%}
+    {% endif %}
+
+AS {% if 'probin' in data or 'prosrc_c' in data %}
+{% if 'probin' in data %}{{ data.probin|qtLiteral }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %}
+$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %}
+{{ o_data.probin|qtLiteral }}, {{ o_data.prosrc_c|qtLiteral }}{% else %}
+$BODY${{ o_data.prosrc }}$BODY${% endif -%};
+{% endif -%}
+{% if data.funcowner %}
+
+ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }})
+    OWNER TO {{ conn|qtIdent(data.funcowner) }};
+{% endif -%}
+{# The SQL generated below will change priviledges #}
+{% if data.acl %}
+{% if 'deleted' in data.acl %}
+{% for priv in data.acl.deleted %}
+
+{{ PRIVILEGE.UNSETALL(conn, 'FUNCTION', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }}
+{% endfor %}
+{% endif -%}
+{% if 'changed' in data.datacl %}
+{% for priv in data.acl.changed %}
+
+{{ PRIVILEGE.UNSETALL(conn, 'FUNCTION', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }}
+
+{{ PRIVILEGE.SET(conn, 'FUNCTION', priv.grantee, name, priv.without_grant, priv.with_grant, o_data.pronamespace, o_data.proargtypenames) }}
+{% endfor %}
+{% endif -%}
+{% if 'added' in data.acl %}
+{% for priv in data.acl.added %}
+
+{{ PRIVILEGE.SET(conn, 'FUNCTION', priv.grantee, name, priv.without_grant, priv.with_grant, o_data.pronamespace, o_data.proargtypenames) }}
+{% endfor %}{% endif -%}
+{% endif -%}
+{% if data.change_func == False %}
+{% if data.variables %}
+{% if 'deleted' in data.variables and data.variables.deleted|length > 0 %}
+
+{{ VARIABLE.UNSET(conn, 'FUNCTION', name, data.variables.deleted, o_data.pronamespace, o_data.proargtypenames) }}
+{% endif -%}
+{% if 'merged_variables' in data and data.merged_variables|length > 0 %}
+
+{{ VARIABLE.SET(conn, 'FUNCTION', name, data.merged_variables, o_data.pronamespace, o_data.proargtypenames) }}
+{% endif -%}
+{% endif -%}
+{% endif -%}
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+
+{{ SECLABEL.UNSET(conn, 'FUNCTION', name, r.provider, o_data.pronamespace, o_data.proargtypenames) }}
+{% endfor %}
+{% endif -%}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+
+{{ SECLABEL.SET(conn, 'FUNCTION', name, r.provider, r.label, o_data.pronamespace, o_data.proargtypenames) }}
+{% endfor %}
+{% endif -%}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+
+{{ SECLABEL.SET(conn, 'FUNCTION', name, r.provider, r.label, o_data.pronamespace, o_data.proargtypenames) }}
+{% endfor %}
+{% endif -%}
+{% if data.description is defined and data.description != o_data.description%}
+
+COMMENT ON FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }})
+    IS {{ data.description|qtLiteral }};
+{% endif -%}
+
+{% if data.pronamespace %}
+
+ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }})
+    SET SCHEMA {{ conn|qtIdent(data.pronamespace) }};
+{% endif -%}
+
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_function_add.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_function_add.py
new file mode 100644
index 0000000..71ee285
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_function_add.py
@@ -0,0 +1,80 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+import uuid
+
+
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+    database_utils
+from pgadmin.utils.route import BaseTestGenerator
+from regression.python_test_utils import test_utils as utils
+from . import utils as funcs_utils
+
+
+class FunctionAddTestCase(BaseTestGenerator):
+    """ This class will add new function under schema node. """
+    scenarios = [
+        # Fetching default URL for function node.
+        ('Fetch Function Node URL', dict(
+            url='/browser/function/obj/'))
+    ]
+
+    def runTest(self):
+        """ This function will add function under schema node. """
+        super(FunctionAddTestCase, self).runTest()
+        self = funcs_utils.set_up(self)
+        db_user = self.server["username"]
+        data = {
+            "acl": [
+                {
+                    "grantee": db_user,
+                    "grantor": db_user,
+                    "privileges":
+                        [
+                            {
+                                "privilege_type": "X",
+                                "privilege": True,
+                                "with_grant": True
+                            }
+                        ]
+                }
+            ],
+            "arguments": [],
+            "funcowner": db_user,
+            "lanname": "sql",
+            "name": "test_function",
+            "options": [],
+            "proleakproof": True,
+            "pronamespace": 2200,
+            "prorettypename": "integer",
+            "prosecdef": True,
+            "prosrc": "SELECT 1;",
+            "probin": "$libdir/",
+            "provolatile": "s",
+            "seclabels": [],
+            "variables": []
+        }
+
+        data["name"] = "test_function_add_%s" % str(uuid.uuid4())[1:8]
+        if self.schema_id:
+            data['pronamespace'] = self.schema_id
+        else:
+            self.schema_id = data['pronamespace']
+        response = self.tester.post(
+            self.url + str(utils.SERVER_GROUP) + '/' +
+            str(self.server_id) + '/' + str(self.db_id) +
+            '/' + str(self.schema_id) + '/',
+            data=json.dumps(data),
+            content_type='html/json'
+        )
+
+        self.assertEquals(response.status_code, 200)
+        # Disconnect the database
+        database_utils.disconnect_database(self, self.server_id, self.db_id)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_function_delete.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_function_delete.py
new file mode 100644
index 0000000..79a89fa
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_function_delete.py
@@ -0,0 +1,49 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import uuid
+
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+    database_utils
+from pgadmin.utils.route import BaseTestGenerator
+from regression.python_test_utils import test_utils as utils
+from . import utils as funcs_utils
+
+
+class FunctionDeleteTestCase(BaseTestGenerator):
+    """ This class will delete the function under schema node. """
+    scenarios = [
+        # Fetching default URL for function node.
+        ('Fetch Function Node URL',
+         dict(url='/browser/function/obj/'))
+    ]
+
+    def runTest(self):
+        """ This function will delete function under database node. """
+        super(FunctionDeleteTestCase, self).setUp()
+        self = funcs_utils.set_up(self)
+
+        func_name = "test_function_delete_%s" % str(uuid.uuid4())[1:8]
+        function_info = funcs_utils.create_function(
+            self.server, self.db_name, self.schema_name, func_name)
+
+        func_id = function_info[0]
+        response = self.tester.delete(
+            self.url + str(utils.SERVER_GROUP) + '/' +
+            str(self.server_id) + '/' +
+            str(self.db_id) + '/' +
+            str(self.schema_id) + '/' + str(func_id),
+            content_type='html/json'
+        )
+        self.assertEquals(response.status_code, 200)
+        # Disconnect the database
+        database_utils.disconnect_database(self, self.server_id, self.db_id)
+
+    def tearDown(self):
+        pass
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_function_get.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_function_get.py
new file mode 100644
index 0000000..32a5585
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_function_get.py
@@ -0,0 +1,49 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import uuid
+
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+    database_utils
+from pgadmin.utils.route import BaseTestGenerator
+from regression.python_test_utils import test_utils as utils
+from . import utils as funcs_utils
+
+
+class FunctionGetTestCase(BaseTestGenerator):
+    """This class will fetch added function under schema node."""
+    skip_on_database = ['gpdb']
+    scenarios = [
+        # Fetching default URL for function node.
+        ('Fetch Function Node URL',
+         dict(url='/browser/function/obj/'))
+    ]
+
+    def runTest(self):
+        """ This function will delete function under database node. """
+        super(FunctionGetTestCase, self).setUp()
+        self = funcs_utils.set_up(self)
+
+        func_name = "test_function_get_%s" % str(uuid.uuid4())[1:8]
+        function_info = funcs_utils.create_function(
+            self.server, self.db_name, self.schema_name, func_name)
+
+        trigger_func_id = function_info[0]
+        response = self.tester.get(
+            self.url + str(utils.SERVER_GROUP) + '/' +
+            str(self.server_id) + '/' +
+            str(self.db_id) + '/' +
+            str(self.schema_id) + '/' + str(trigger_func_id),
+            content_type='html/json')
+        self.assertEquals(response.status_code, 200)
+        # Disconnect the database
+        database_utils.disconnect_database(self, self.server_id, self.db_id)
+
+    def tearDown(self):
+        pass
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_function_put.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_function_put.py
new file mode 100644
index 0000000..8565687
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_function_put.py
@@ -0,0 +1,57 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+import uuid
+
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+    database_utils
+from pgadmin.utils.route import BaseTestGenerator
+from regression.python_test_utils import test_utils as utils
+from . import utils as funcs_utils
+
+
+class FunctionPutTestCase(BaseTestGenerator):
+    """ This class will update new function under schema node. """
+    skip_on_database = ['gpdb']
+    scenarios = [
+        # Fetching default URL for function node.
+        ('Fetch Function Node URL',
+         dict(url='/browser/function/obj/'))
+    ]
+
+    def runTest(self):
+        """ This function will update function under database node. """
+        super(FunctionPutTestCase, self).setUp()
+        self = funcs_utils.set_up(self)
+
+        func_name = "test_event_delete_%s" % str(uuid.uuid4())[1:8]
+        function_info = funcs_utils.create_function(
+            self.server, self.db_name, self.schema_name, func_name)
+
+        func_id = function_info[0]
+
+        data = {
+            "description": "This is a procedure update comment",
+            "id": func_id
+        }
+
+        put_response = self.tester.put(
+            self.url + str(utils.SERVER_GROUP) +
+            '/' + str(self.server_id) + '/' + str(self.db_id) + '/' +
+            str(self.schema_id) + '/' +
+            str(func_id),
+            data=json.dumps(data),
+            follow_redirects=True)
+        self.assertEquals(put_response.status_code, 200)
+        # Disconnect the database
+        database_utils.disconnect_database(self, self.server_id, self.db_id)
+
+    def tearDown(self):
+        pass
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_procedure_add.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_procedure_add.py
new file mode 100644
index 0000000..b5cdf17
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_procedure_add.py
@@ -0,0 +1,90 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+import uuid
+
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+    database_utils
+from pgadmin.utils.route import BaseTestGenerator
+from regression.python_test_utils import test_utils as utils
+from . import utils as funcs_utils
+
+
+class ProcedureAddTestCase(BaseTestGenerator):
+    """ This class will add new procedure under schema node. """
+    skip_on_database = ['gpdb']
+    scenarios = [
+        # Fetching default URL for procedure node.
+        ('Fetch Procedure Node URL', dict(
+            url='/browser/procedure/obj/'))
+    ]
+
+    def runTest(self):
+        """ This function will add procedure under schema node. """
+        super(ProcedureAddTestCase, self).setUp()
+        self = funcs_utils.set_up(self)
+
+        if self.server_type == "pg" and\
+                self.server_version < 110000:
+            message = "Procedures are not supported by PG < 110000."
+            self.skipTest(message)
+
+        db_user = self.server["username"]
+        data = {
+            "acl": [
+                {
+                    "grantee": db_user,
+                    "grantor": db_user,
+                    "privileges":
+                        [
+                            {
+                                "privilege_type": "X",
+                                "privilege": True,
+                                "with_grant": True
+                            }
+                        ]
+                }
+            ],
+            "arguments": [],
+            "funcowner": db_user,
+            "lanname": "sql",
+            "name": "test_pg_11_proc",
+            "options": [],
+            "proleakproof": True,
+            "pronamespace": 2200,
+            "prosecdef": True,
+            "prosrc": "BEGIN RAISE EXCEPTION 'command % is disabled',"
+                      " tg_tag; END;",
+            "seclabels": [],
+            "variables": [
+                {
+                    "name": "enable_sort",
+                    "value": True
+                }
+            ]
+        }
+
+        data["name"] = "test_proc_add_%s" % str(uuid.uuid4())[1:8]
+        if self.server_type == 'pg':
+            data['prosrc'] = 'SELECT 1;'
+        if self.schema_id:
+            data['pronamespace'] = self.schema_id
+        else:
+            self.schema_id = data['pronamespace']
+        response = self.tester.post(
+            self.url + str(utils.SERVER_GROUP) + '/' +
+            str(self.server_id) + '/' + str(self.db_id) + '/' +
+            str(self.schema_id) + '/', data=json.dumps(data),
+            content_type='html/json'
+        )
+
+        self.assertEquals(response.status_code, 200)
+        # Disconnect the database
+        database_utils.disconnect_database(self, self.server_id, self.db_id)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_procedure_delete.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_procedure_delete.py
new file mode 100644
index 0000000..4556b9f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_procedure_delete.py
@@ -0,0 +1,56 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import uuid
+
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+    database_utils
+from pgadmin.utils.route import BaseTestGenerator
+from regression.python_test_utils import test_utils as utils
+from . import utils as funcs_utils
+
+
+class procedureDeleteTestCase(BaseTestGenerator):
+    """ This class will delete the procedure under schema node. """
+    skip_on_database = ['gpdb']
+    scenarios = [
+        # Fetching default URL for procedure node.
+        ('Fetch Procedure Node URL',
+         dict(url='/browser/procedure/obj/'))
+    ]
+
+    def runTest(self):
+        """ This function will delete procedure under database node. """
+        super(procedureDeleteTestCase, self).setUp()
+        self = funcs_utils.set_up(self)
+
+        if self.server_type == "pg" and\
+                self.server_version < 110000:
+            message = "Procedures are not supported by PG < 110000."
+            self.skipTest(message)
+
+        func_name = "test_procedure_delete_%s" % str(uuid.uuid4())[1:8]
+        proc_info = funcs_utils.create_procedure(
+            self.server, self.db_name, self.schema_name, func_name,
+            self.server_type)
+
+        proc_id = proc_info[0]
+        response = self.tester.delete(
+            self.url + str(utils.SERVER_GROUP) + '/' +
+            str(self.server_id) + '/' +
+            str(self.db_id) + '/' +
+            str(self.schema_id) + '/' + str(proc_id),
+            content_type='html/json'
+        )
+        self.assertEquals(response.status_code, 200)
+        # Disconnect the database
+        database_utils.disconnect_database(self, self.server_id, self.db_id)
+
+    def tearDown(self):
+        pass
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_procedure_get.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_procedure_get.py
new file mode 100644
index 0000000..2b15878
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_procedure_get.py
@@ -0,0 +1,55 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import uuid
+
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+    database_utils
+from pgadmin.utils.route import BaseTestGenerator
+from regression.python_test_utils import test_utils as utils
+from . import utils as funcs_utils
+
+
+class ProcedureGetTestCase(BaseTestGenerator):
+    """This class will fetch added procedure under schema node."""
+    skip_on_database = ['gpdb']
+    scenarios = [
+        # Fetching default URL for procedure node.
+        ('Fetch Procedure Node URL',
+         dict(url='/browser/procedure/obj/'))
+    ]
+
+    def runTest(self):
+        """ This function will get procedure under database node. """
+        super(ProcedureGetTestCase, self).setUp()
+        self = funcs_utils.set_up(self)
+
+        if self.server_type == "pg" and\
+                self.server_version < 110000:
+            message = "Procedures are not supported by PG < 110000."
+            self.skipTest(message)
+
+        func_name = "test_procedure_get_%s" % str(uuid.uuid4())[1:8]
+        proc_info = funcs_utils.create_procedure(
+            self.server, self.db_name, self.schema_name, func_name,
+            self.server_type)
+
+        proc_id = proc_info[0]
+        response = self.tester.get(
+            self.url + str(utils.SERVER_GROUP) + '/' +
+            str(self.server_id) + '/' +
+            str(self.db_id) + '/' +
+            str(self.schema_id) + '/' + str(proc_id),
+            content_type='html/json')
+        self.assertEquals(response.status_code, 200)
+        # Disconnect the database
+        database_utils.disconnect_database(self, self.server_id, self.db_id)
+
+    def tearDown(self):
+        pass
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_procedure_put.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_procedure_put.py
new file mode 100644
index 0000000..73ba1ae
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_procedure_put.py
@@ -0,0 +1,62 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+import uuid
+
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+    database_utils
+from pgadmin.utils.route import BaseTestGenerator
+from regression.python_test_utils import test_utils as utils
+from . import utils as funcs_utils
+
+
+class ProcedurePutTestCase(BaseTestGenerator):
+    """ This class will update new procedure under schema node. """
+    skip_on_database = ['gpdb']
+    scenarios = [
+        # Fetching default URL for procedure node.
+        ('Fetch Procedure Node URL',
+         dict(url='/browser/procedure/obj/'))
+    ]
+
+    def runTest(self):
+        """ This function will update procedure under database node. """
+        super(ProcedurePutTestCase, self).setUp()
+        self = funcs_utils.set_up(self)
+
+        if self.server_type == "pg" and\
+                self.server_version < 110000:
+            message = "Procedures are not supported by PG < 110000."
+            self.skipTest(message)
+
+        func_name = "test_procedure_put_%s" % str(uuid.uuid4())[1:8]
+        proc_info = funcs_utils.create_procedure(
+            self.server, self.db_name, self.schema_name, func_name,
+            self.server_type)
+
+        proc_id = proc_info[0]
+        data = {
+            "description": "This is procedure update comment",
+            "id": proc_id
+        }
+
+        put_response = self.tester.put(
+            self.url + str(utils.SERVER_GROUP) +
+            '/' + str(self.server_id) + '/' + str(self.db_id) + '/' +
+            str(self.schema_id) + '/' +
+            str(proc_id),
+            data=json.dumps(data),
+            follow_redirects=True)
+        self.assertEquals(put_response.status_code, 200)
+        # Disconnect the database
+        database_utils.disconnect_database(self, self.server_id, self.db_id)
+
+    def tearDown(self):
+        pass
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_trigger_func_add.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_trigger_func_add.py
index d238bf0..e99f7c3 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_trigger_func_add.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_trigger_func_add.py
@@ -10,14 +10,11 @@
 import json
 import uuid
 
-from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
-    utils as schema_utils
 from pgadmin.browser.server_groups.servers.databases.tests import utils as \
     database_utils
-from pgadmin.utils import server_utils as server_utils
 from pgadmin.utils.route import BaseTestGenerator
-from regression import parent_node_dict
 from regression.python_test_utils import test_utils as utils
+from . import utils as funcs_utils
 
 
 class TriggerFuncAddTestCase(BaseTestGenerator):
@@ -32,30 +29,7 @@ class TriggerFuncAddTestCase(BaseTestGenerator):
     def runTest(self):
         """ This function will add trigger function under schema node. """
         super(TriggerFuncAddTestCase, self).runTest()
-        db_name = parent_node_dict["database"][-1]["db_name"]
-        schema_info = parent_node_dict["schema"][-1]
-        server_id = schema_info["server_id"]
-        db_id = schema_info["db_id"]
-        prorettypename = "event_trigger/trigger"
-        server_con = server_utils.connect_server(self, server_id)
-        if not server_con["info"] == "Server connected.":
-            raise Exception("Could not connect to server to add resource "
-                            "groups.")
-        if "type" in server_con["data"]:
-            if server_con["data"]["version"] < 90300:
-                prorettypename = "trigger"
-
-        db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
-                                                 server_id, db_id)
-        if not db_con['data']["connected"]:
-            raise Exception("Could not connect to database to add a function.")
-        schema_id = schema_info["schema_id"]
-        schema_name = schema_info["schema_name"]
-        schema_response = schema_utils.verify_schemas(self.server,
-                                                      db_name,
-                                                      schema_name)
-        if not schema_response:
-            raise Exception("Could not find the schema to add a function.")
+        self = funcs_utils.set_up(self)
         db_user = self.server["username"]
         data = {
             "acl": [
@@ -79,7 +53,7 @@ class TriggerFuncAddTestCase(BaseTestGenerator):
             "options": [],
             "proleakproof": True,
             "pronamespace": 2200,
-            "prorettypename": prorettypename,
+            "prorettypename": self.prorettypename,
             "prosecdef": True,
             "prosrc": "BEGIN RAISE EXCEPTION 'command % is disabled',"
                       " tg_tag; END;",
@@ -98,16 +72,17 @@ class TriggerFuncAddTestCase(BaseTestGenerator):
         for func_type in trigger_func_types:
             data['prorettypename'] = func_type
             data["name"] = "test_event_add_%s" % str(uuid.uuid4())[1:8]
-            if schema_id:
-                data['pronamespace'] = schema_id
+            if self.schema_id:
+                data['pronamespace'] = self.schema_id
             else:
-                schema_id = data['pronamespace']
+                self.schema_id = data['pronamespace']
             response = self.tester.post(
                 self.url + str(utils.SERVER_GROUP) + '/' +
-                str(server_id) + '/' + str(db_id) + '/' + str(schema_id) +
+                str(self.server_id) + '/' + str(self.db_id) +
+                '/' + str(self.schema_id) +
                 '/', data=json.dumps(data), content_type='html/json'
             )
 
             self.assertEquals(response.status_code, 200)
         # Disconnect the database
-        database_utils.disconnect_database(self, server_id, db_id)
+        database_utils.disconnect_database(self, self.server_id, self.db_id)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_trigger_func_delete.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_trigger_func_delete.py
index 3d1fe9f..f722470 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_trigger_func_delete.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_trigger_func_delete.py
@@ -9,13 +9,9 @@
 
 import uuid
 
-from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
-    utils as schema_utils
 from pgadmin.browser.server_groups.servers.databases.tests import utils as \
     database_utils
-from pgadmin.utils import server_utils as server_utils
 from pgadmin.utils.route import BaseTestGenerator
-from regression import parent_node_dict
 from regression.python_test_utils import test_utils as utils
 from . import utils as trigger_funcs_utils
 
@@ -29,49 +25,27 @@ class TriggerFuncDeleteTestCase(BaseTestGenerator):
          dict(url='/browser/trigger_function/obj/'))
     ]
 
-    def setUp(self):
-        super(TriggerFuncDeleteTestCase, self).setUp()
-        self.db_name = parent_node_dict["database"][-1]["db_name"]
-        self.schema_name = parent_node_dict["schema"][-1]["schema_name"]
-        self.schema_id = parent_node_dict["schema"][-1]["schema_id"]
-
     def runTest(self):
         """ This function will delete trigger function under database node. """
-        schema_info = parent_node_dict["schema"][-1]
-        server_id = schema_info["server_id"]
-        db_id = schema_info["db_id"]
+        super(TriggerFuncDeleteTestCase, self).setUp()
+        self = trigger_funcs_utils.set_up(self)
+
         func_name = "test_event_delete_%s" % str(uuid.uuid4())[1:8]
-        server_con = server_utils.connect_server(self, server_id)
-        if not server_con["info"] == "Server connected.":
-            raise Exception("Could not connect to server to add resource "
-                            "groups.")
-        server_version = 0
-        if "type" in server_con["data"]:
-            if server_con["data"]["version"] < 90300:
-                server_version = server_con["data"]["version"]
-        self.function_info = trigger_funcs_utils.create_trigger_function(
+        function_info = trigger_funcs_utils.create_trigger_function(
             self.server, self.db_name, self.schema_name, func_name,
-            server_version)
+            self.server_version)
 
-        db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
-                                                 server_id, db_id)
-        if not db_con['data']["connected"]:
-            raise Exception("Could not connect to database to add collation.")
-        schema_response = schema_utils.verify_schemas(self.server,
-                                                      self.db_name,
-                                                      self.schema_name)
-        if not schema_response:
-            raise Exception("Could not find the schema to add the collation.")
-        trigger_func_id = self.function_info[0]
+        trigger_func_id = function_info[0]
         response = self.tester.delete(
-            self.url + str(utils.SERVER_GROUP) + '/' + str(server_id) + '/' +
-            str(db_id) + '/' +
+            self.url + str(utils.SERVER_GROUP) + '/' +
+            str(self.server_id) + '/' +
+            str(self.db_id) + '/' +
             str(self.schema_id) + '/' + str(trigger_func_id),
             content_type='html/json'
         )
         self.assertEquals(response.status_code, 200)
         # Disconnect the database
-        database_utils.disconnect_database(self, server_id, db_id)
+        database_utils.disconnect_database(self, self.server_id, self.db_id)
 
     def tearDown(self):
         pass
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_trigger_func_get.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_trigger_func_get.py
index 7eb0d2a..15ae1ba 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_trigger_func_get.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_trigger_func_get.py
@@ -9,13 +9,9 @@
 
 import uuid
 
-from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
-    utils as schema_utils
 from pgadmin.browser.server_groups.servers.databases.tests import utils as \
     database_utils
-from pgadmin.utils import server_utils as server_utils
 from pgadmin.utils.route import BaseTestGenerator
-from regression import parent_node_dict
 from regression.python_test_utils import test_utils as utils
 from . import utils as trigger_funcs_utils
 
@@ -29,49 +25,26 @@ class TriggerFuncGetTestCase(BaseTestGenerator):
          dict(url='/browser/trigger_function/obj/'))
     ]
 
-    def setUp(self):
-        super(TriggerFuncGetTestCase, self).setUp()
-        self.db_name = parent_node_dict["database"][-1]["db_name"]
-        self.schema_name = parent_node_dict["schema"][-1]["schema_name"]
-        self.schema_id = parent_node_dict["schema"][-1]["schema_id"]
-
     def runTest(self):
         """ This function will delete trigger function under database node. """
-        schema_info = parent_node_dict["schema"][-1]
-        server_id = schema_info["server_id"]
-        db_id = schema_info["db_id"]
-        func_name = "test_event_get_%s" % str(uuid.uuid4())[1:8]
-        db_user = self.server["username"]
-        server_con = server_utils.connect_server(self, server_id)
-        if not server_con["info"] == "Server connected.":
-            raise Exception("Could not connect to server to add resource "
-                            "groups.")
-        server_version = 0
-        if "type" in server_con["data"]:
-            if server_con["data"]["version"] < 90300:
-                server_version = server_con["data"]["version"]
-        self.function_info = trigger_funcs_utils.create_trigger_function(
+        super(TriggerFuncGetTestCase, self).setUp()
+        self = trigger_funcs_utils.set_up(self)
+
+        func_name = "test_event_delete_%s" % str(uuid.uuid4())[1:8]
+        function_info = trigger_funcs_utils.create_trigger_function(
             self.server, self.db_name, self.schema_name, func_name,
-            server_version)
+            self.server_version)
 
-        db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
-                                                 server_id, db_id)
-        if not db_con['data']["connected"]:
-            raise Exception("Could not connect to database to add collation.")
-        schema_response = schema_utils.verify_schemas(self.server,
-                                                      self.db_name,
-                                                      self.schema_name)
-        if not schema_response:
-            raise Exception("Could not find the schema to add the collation.")
-        trigger_func_id = self.function_info[0]
+        trigger_func_id = function_info[0]
         response = self.tester.get(
-            self.url + str(utils.SERVER_GROUP) + '/' + str(server_id) + '/' +
-            str(db_id) + '/' +
+            self.url + str(utils.SERVER_GROUP) + '/' +
+            str(self.server_id) + '/' +
+            str(self.db_id) + '/' +
             str(self.schema_id) + '/' + str(trigger_func_id),
             content_type='html/json')
         self.assertEquals(response.status_code, 200)
         # Disconnect the database
-        database_utils.disconnect_database(self, server_id, db_id)
+        database_utils.disconnect_database(self, self.server_id, self.db_id)
 
     def tearDown(self):
         pass
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_trigger_func_put.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_trigger_func_put.py
index 63d4c08..78ddc8f 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_trigger_func_put.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_trigger_func_put.py
@@ -10,13 +10,9 @@
 import json
 import uuid
 
-from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
-    utils as schema_utils
 from pgadmin.browser.server_groups.servers.databases.tests import utils as \
     database_utils
-from pgadmin.utils import server_utils as server_utils
 from pgadmin.utils.route import BaseTestGenerator
-from regression import parent_node_dict
 from regression.python_test_utils import test_utils as utils
 from . import utils as trigger_funcs_utils
 
@@ -30,63 +26,33 @@ class TriggerFuncPutTestCase(BaseTestGenerator):
          dict(url='/browser/trigger_function/obj/'))
     ]
 
-    def setUp(self):
-        super(TriggerFuncPutTestCase, self).setUp()
-        self.db_name = parent_node_dict["database"][-1]["db_name"]
-        self.schema_name = parent_node_dict["schema"][-1]["schema_name"]
-        self.schema_id = parent_node_dict["schema"][-1]["schema_id"]
-
     def runTest(self):
         """ This function will update trigger function under database node. """
-        schema_info = parent_node_dict["schema"][-1]
-        server_id = schema_info["server_id"]
-        db_id = schema_info["db_id"]
-        func_name = "test_event_put_%s" % str(uuid.uuid4())[1:8]
-        server_con = server_utils.connect_server(self, server_id)
-        if not server_con["info"] == "Server connected.":
-            raise Exception("Could not connect to server to add resource "
-                            "groups.")
-        server_version = 0
-        if "type" in server_con["data"]:
-            if server_con["data"]["version"] < 90300:
-                server_version = server_con["data"]["version"]
-        self.function_info = trigger_funcs_utils.create_trigger_function(
+        super(TriggerFuncPutTestCase, self).setUp()
+        self = trigger_funcs_utils.set_up(self)
+
+        func_name = "test_event_delete_%s" % str(uuid.uuid4())[1:8]
+        function_info = trigger_funcs_utils.create_trigger_function(
             self.server, self.db_name, self.schema_name, func_name,
-            server_version)
-        db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
-                                                 server_id, db_id)
-        if not db_con['data']["connected"]:
-            raise Exception("Could not connect to database to add collation.")
-        schema_response = schema_utils.verify_schemas(self.server,
-                                                      self.db_name,
-                                                      self.schema_name)
-        if not schema_response:
-            raise Exception("Could not find the schema to add the collation.")
-        func_name = self.function_info[1]
-        func_response = trigger_funcs_utils.verify_trigger_function(
-            self.server,
-            self.db_name,
-            func_name)
-        if not func_response:
-            raise Exception("Could not find the trigger function to update"
-                            " it's details.")
+            self.server_version)
+
+        trigger_func_id = function_info[0]
 
-        trigger_func_id = self.function_info[0]
         data = {
-            "description": "This is trigger function update comment",
+            "description": "This is a trigger function update comment",
             "id": trigger_func_id
         }
 
         put_response = self.tester.put(
             self.url + str(utils.SERVER_GROUP) +
-            '/' + str(server_id) + '/' + str(db_id) + '/' +
+            '/' + str(self.server_id) + '/' + str(self.db_id) + '/' +
             str(self.schema_id) + '/' +
             str(trigger_func_id),
             data=json.dumps(data),
             follow_redirects=True)
         self.assertEquals(put_response.status_code, 200)
         # Disconnect the database
-        database_utils.disconnect_database(self, server_id, db_id)
+        database_utils.disconnect_database(self, self.server_id, self.db_id)
 
     def tearDown(self):
         pass
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/utils.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/utils.py
index 4facf60..3606b45 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/utils.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/utils.py
@@ -13,6 +13,11 @@ import sys
 import traceback
 
 from regression.python_test_utils import test_utils as utils
+from pgadmin.utils import server_utils as server_utils
+from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
+    utils as schema_utils
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+    database_utils
 
 
 def create_trigger_function(server, db_name, schema_name, func_name,
@@ -92,3 +97,118 @@ def verify_trigger_function(server, db_name, func_name):
     functions = pg_cursor.fetchone()
     connection.close()
     return functions
+
+
+def create_procedure(server, db_name, schema_name, func_name, s_type):
+    """This function add the procedure to schema"""
+    try:
+        connection = utils.get_db_connection(db_name,
+                                             server['username'],
+                                             server['db_password'],
+                                             server['host'],
+                                             server['port'],
+                                             server['sslmode'])
+        pg_cursor = connection.cursor()
+        if s_type == 'pg':
+            query = "CREATE PROCEDURE " + schema_name + "." + func_name + \
+                    "()" \
+                    " LANGUAGE 'sql'" \
+                    " SECURITY DEFINER AS $$" \
+                    " SELECT 1; $$;"
+        else:
+            query = "CREATE PROCEDURE " + schema_name + "." + func_name + \
+                    "()" \
+                    " SECURITY DEFINER AS $BODY$ BEGIN" \
+                    " NULL; END; $BODY$"
+        pg_cursor.execute(query)
+        connection.commit()
+        # Get 'oid' from newly created function
+        pg_cursor.execute("SELECT pro.oid, pro.proname FROM"
+                          " pg_proc pro WHERE pro.proname='%s'" %
+                          func_name)
+        functions = pg_cursor.fetchone()
+        connection.close()
+        return functions
+    except Exception:
+        traceback.print_exc(file=sys.stderr)
+
+
+def create_function(server, db_name, schema_name, func_name):
+    """This function add the procedure to schema"""
+    try:
+        connection = utils.get_db_connection(db_name,
+                                             server['username'],
+                                             server['db_password'],
+                                             server['host'],
+                                             server['port'],
+                                             server['sslmode'])
+        pg_cursor = connection.cursor()
+        query = "CREATE FUNCTION " + schema_name + "." + func_name + \
+                "()" \
+                " RETURNS integer LANGUAGE 'sql' STABLE LEAKPROOF" \
+                " SECURITY DEFINER AS $$" \
+                " SELECT 1; $$;"
+        pg_cursor.execute(query)
+        connection.commit()
+        # Get 'oid' from newly created function
+        pg_cursor.execute("SELECT pro.oid, pro.proname FROM"
+                          " pg_proc pro WHERE pro.proname='%s'" %
+                          func_name)
+        functions = pg_cursor.fetchone()
+        connection.close()
+        return functions
+    except Exception:
+        traceback.print_exc(file=sys.stderr)
+
+
+def verify_procedure(server, db_name, proc_name):
+    """This function verifies the procedure in db"""
+    connection = utils.get_db_connection(db_name,
+                                         server['username'],
+                                         server['db_password'],
+                                         server['host'],
+                                         server['port'],
+                                         server['sslmode'])
+    pg_cursor = connection.cursor()
+    pg_cursor.execute("SELECT pro.oid, pro.proname FROM"
+                      " pg_proc pro WHERE pro.proname='%s'" %
+                      proc_name)
+    procs = pg_cursor.fetchone()
+    connection.close()
+    return procs
+
+
+def set_up(obj):
+    """Common set up function"""
+    from regression import parent_node_dict
+
+    obj.db_name = parent_node_dict["database"][-1]["db_name"]
+    schema_info = parent_node_dict["schema"][-1]
+    obj.server_id = schema_info["server_id"]
+    obj.db_id = schema_info["db_id"]
+    obj.prorettypename = "event_trigger/trigger"
+    server_con = server_utils.connect_server(obj, obj.server_id)
+
+    if not server_con["info"] == "Server connected.":
+        raise Exception("Could not connect to server.")
+    if "version" in server_con["data"]:
+        obj.server_version = server_con["data"]["version"]
+        if server_con["data"]["version"] < 90300:
+            obj.prorettypename = "trigger"
+    if "type" in server_con["data"]:
+        obj.server_type = server_con["data"]["type"]
+
+    db_con = database_utils.connect_database(obj, utils.SERVER_GROUP,
+                                             obj.server_id,
+                                             obj.db_id)
+    if not db_con['data']["connected"]:
+        raise Exception("Could not connect to database.")
+    obj.schema_id = schema_info["schema_id"]
+    obj.schema_name = schema_info["schema_name"]
+    schema_response = schema_utils.verify_schemas(obj.server,
+                                                  obj.db_name,
+                                                  obj.schema_name)
+    if not schema_response:
+        raise Exception("Could not find the schema.")
+
+    return obj
diff --git a/web/pgadmin/browser/utils.py b/web/pgadmin/browser/utils.py
index 00a72dd..7f6f19c 100644
--- a/web/pgadmin/browser/utils.py
+++ b/web/pgadmin/browser/utils.py
@@ -51,7 +51,9 @@ class PGChildModule(object):
 
     def __init__(self, *args, **kwargs):
         self.min_ver = 0
-        self.max_ver = 1000000000
+        self.max_ver = 1100000000
+        self.min_ppasver = 0
+        self.max_ppasver = 1100000000
         self.server_type = None
         self.min_gpdbver = 80323
         self.max_gpdbver = 1000000000
@@ -72,6 +74,9 @@ class PGChildModule(object):
         if self.server_type is None or manager.server_type in self.server_type:
             min_server_version = self.min_ver
             max_server_version = self.max_ver
+            if manager.server_type == 'ppas':
+                min_server_version = self.min_ppasver
+                max_server_version = self.max_ppasver
             if manager.server_type == 'gpdb':
                 min_server_version = self.min_gpdbver
                 max_server_version = self.max_gpdbver
diff --git a/web/pgadmin/utils/versioned_template_loader.py b/web/pgadmin/utils/versioned_template_loader.py
index 3481978..dadaeba 100644
--- a/web/pgadmin/utils/versioned_template_loader.py
+++ b/web/pgadmin/utils/versioned_template_loader.py
@@ -75,7 +75,8 @@ def get_version_mapping(template):
             {'name': "default", 'number': 0}
         )
 
-    return ({'name': "10_plus", 'number': 100000},
+    return ({'name': "11_plus", 'number': 110000},
+            {'name': "10_plus", 'number': 100000},
             {'name': "9.6_plus", 'number': 90600},
             {'name': "9.5_plus", 'number': 90500},
             {'name': "9.4_plus", 'number': 90400},

Reply via email to