Hi,
Please ignore my previous patch, find the attached updated one.
Thanks,
Khushboo
On Wed, Jun 6, 2018 at 12:12 PM, Khushboo Vashi <
[email protected]> 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},