Support Enable Always and Enable Replica on triggers. Fixes #4006

This commit is contained in:
Aditya Toshniwal authored and Dave Page committed 2019-11-01 11:49:17 +00:00
1 parent ab5d954337
commit 131a944e24
39 files changed
+131 -86

No files matched your search

+7
View File
@@ -24,6 +24,13 @@ Use the fields in the *General* tab to identify the compound trigger:
The name will be displayed in the *pgAdmin* tree control. The name will be displayed in the *pgAdmin* tree control.
* Store notes about the compound trigger in the *Comment* field. * Store notes about the compound trigger in the *Comment* field.
.. image:: images/compound_trigger_general_enabled.png
:alt: Trigger enabled in dialog tab
:align: center
* *Trigger enabled* field is available in compound trigger dialog once the trigger is created.
You can select one of the four options available.
Click the *Events* tab to continue. Click the *Events* tab to continue.
.. image:: images/compound_trigger_events.png .. image:: images/compound_trigger_events.png
Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

+1
View File
@@ -11,6 +11,7 @@ New features
| `Issue #1974 <https://redmine.postgresql.org/issues/1974>`_ - Added encrypted password in reverse engineered SQL for roles. | `Issue #1974 <https://redmine.postgresql.org/issues/1974>`_ - Added encrypted password in reverse engineered SQL for roles.
| `Issue #4351 <https://redmine.postgresql.org/issues/4351>`_ - Add an option to request confirmation before cancelling changes on a Properties dialog. | `Issue #4351 <https://redmine.postgresql.org/issues/4351>`_ - Add an option to request confirmation before cancelling changes on a Properties dialog.
| `Issue #4006 <https://redmine.postgresql.org/issues/4006>`_ - Support Enable Always and Enable Replica on triggers.
Housekeeping Housekeeping
************ ************
+8 -1
View File
@@ -48,6 +48,13 @@ Use the fields in the *Definition* tab to define the trigger:
arguments to the function when the trigger is executed. The arguments are arguments to the function when the trigger is executed. The arguments are
literal string constants. literal string constants.
.. image:: images/trigger_definition_enabled.png
:alt: Trigger enabled in dialog tab
:align: center
* *Trigger enabled* field is available in trigger dialog once the trigger is created.
You can select one of the four options available.
Click the *Events* tab to continue. Click the *Events* tab to continue.
.. image:: images/trigger_events.png .. image:: images/trigger_events.png
@@ -96,4 +103,4 @@ The example demonstrates creating a trigger named *log_update*.
* Click the *Info* button (i) to access online help. * Click the *Info* button (i) to access online help.
* Click the *Save* button to save work. * Click the *Save* button to save work.
* Click the *Cancel* button to exit without saving work. * Click the *Cancel* button to exit without saving work.
* Click the *Reset* button to restore configuration parameters. * Click the *Reset* button to restore configuration parameters.
@@ -1228,7 +1228,7 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings):
request.data, encoding='utf-8' request.data, encoding='utf-8'
) )
# Convert str 'true' to boolean type # Convert str 'true' to boolean type
is_enable = json.loads(data['enable']) is_enable_trigger = data['is_enable_trigger']
try: try:
SQL = render_template( SQL = render_template(
@@ -1245,7 +1245,7 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings):
"/".join([ "/".join([
self.table_template_path, 'enable_disable_trigger.sql' self.table_template_path, 'enable_disable_trigger.sql'
]), ]),
data=data, is_enable_trigger=is_enable data=data, is_enable_trigger=is_enable_trigger
) )
status, res = self.conn.execute_scalar(SQL) status, res = self.conn.execute_scalar(SQL)
if not status: if not status:
@@ -1253,8 +1253,9 @@ class TableView(BaseTableView, DataTypeReader, VacuumSettings):
return make_json_response( return make_json_response(
success=1, success=1,
info=gettext("Trigger(s) have been enabled") if is_enable info=gettext("Trigger(s) have been disabled")
else gettext("Trigger(s) have been disabled"), if is_enable_trigger == 'D'
else gettext("Trigger(s) have been enabled"),
data={ data={
'id': tid, 'id': tid,
'scid': scid 'scid': scid
@@ -372,9 +372,9 @@ class CompoundTriggerView(PGChildNodeView):
rset['rows'][0]['oid'], rset['rows'][0]['oid'],
tid, tid,
rset['rows'][0]['name'], rset['rows'][0]['name'],
icon="icon-compound_trigger" if icon="icon-compound_trigger-bad" if
rset['rows'][0]['is_enable_trigger'] else rset['rows'][0]['is_enable_trigger'] == 'D' else
"icon-compound_trigger-bad" "icon-compound_trigger"
) )
return make_json_response( return make_json_response(
@@ -412,8 +412,9 @@ class CompoundTriggerView(PGChildNodeView):
row['oid'], row['oid'],
tid, tid,
row['name'], row['name'],
icon="icon-compound_trigger" if row['is_enable_trigger'] icon="icon-compound_trigger-bad"
else "icon-compound_trigger-bad" if row['is_enable_trigger'] == 'D'
else "icon-compound_trigger"
)) ))
return make_json_response( return make_json_response(
@@ -736,9 +737,9 @@ class CompoundTriggerView(PGChildNodeView):
new_trid, new_trid,
tid, tid,
name, name,
icon="icon-%s" % self.node_type if icon="icon-%s-bad" % self.node_type if
data['is_enable_trigger'] else data['is_enable_trigger'] == 'D' else
"icon-%s-bad" % self.node_type "icon-%s" % self.node_type
) )
) )
except Exception as e: except Exception as e:
@@ -889,7 +890,7 @@ class CompoundTriggerView(PGChildNodeView):
SQL = sql_header + '\n\n' + SQL.strip('\n') SQL = sql_header + '\n\n' + SQL.strip('\n')
# If compound trigger is disbaled then add sql code for the same # If compound trigger is disbaled then add sql code for the same
if not data['is_enable_trigger']: if data['is_enable_trigger'] != 'O':
SQL += '\n\n' SQL += '\n\n'
SQL += render_template("/".join([self.template_path, SQL += render_template("/".join([self.template_path,
'enable_disable_trigger.sql']), 'enable_disable_trigger.sql']),
@@ -917,7 +918,7 @@ class CompoundTriggerView(PGChildNodeView):
) )
# Convert str 'true' to boolean type # Convert str 'true' to boolean type
is_enable_flag = json.loads(data['enable']) is_enable_trigger = data['is_enable_trigger']
try: try:
@@ -940,7 +941,7 @@ class CompoundTriggerView(PGChildNodeView):
# current compound trigger which is disabled already so we need to # current compound trigger which is disabled already so we need to
# alter the 'is_enable_trigger' flag so that we can render # alter the 'is_enable_trigger' flag so that we can render
# correct SQL for operation # correct SQL for operation
o_data['is_enable_trigger'] = is_enable_flag o_data['is_enable_trigger'] = is_enable_trigger
# Adding parent into data dict, will be using it while creating sql # Adding parent into data dict, will be using it while creating sql
o_data['schema'] = self.schema o_data['schema'] = self.schema
@@ -108,7 +108,7 @@ define('pgadmin.node.compound_trigger', [
$.ajax({ $.ajax({
url: obj.generate_url(i, 'enable' , d, true), url: obj.generate_url(i, 'enable' , d, true),
type:'PUT', type:'PUT',
data: {'enable' : true}, data: {'is_enable_trigger' : 'O'},
dataType: 'json', dataType: 'json',
}) })
.done(function(res) { .done(function(res) {
@@ -146,7 +146,7 @@ define('pgadmin.node.compound_trigger', [
$.ajax({ $.ajax({
url: obj.generate_url(i, 'enable' , d, true), url: obj.generate_url(i, 'enable' , d, true),
type:'PUT', type:'PUT',
data: {'enable' : false}, data: {'is_enable_trigger' : 'D'},
dataType: 'json', dataType: 'json',
}) })
.done(function(res) { .done(function(res) {
@@ -185,13 +185,20 @@ define('pgadmin.node.compound_trigger', [
type: 'int', disabled: true, mode: ['properties'], type: 'int', disabled: true, mode: ['properties'],
},{ },{
id: 'is_enable_trigger', label: gettext('Trigger enabled?'), id: 'is_enable_trigger', label: gettext('Trigger enabled?'),
type: 'switch', mode: ['edit', 'properties'], mode: ['edit', 'properties'],
disabled: function() { disabled: function() {
if(this.node_info && ('catalog' in this.node_info || 'view' in this.node_info)) { if(this.node_info && ('catalog' in this.node_info || 'view' in this.node_info)) {
return true; return true;
} }
return false; return false;
}, },
options: [
{label: 'Enable', value: 'O'},
{label: 'Enable Replica', value: 'R'},
{label: 'Enable Always', value: 'A'},
{label: 'Disable', value: 'D'},
],
control: 'select2', select2: { allowClear: false, width: '100%' },
},{ },{
type: 'nested', control: 'fieldset', mode: ['create','edit', 'properties'], type: 'nested', control: 'fieldset', mode: ['create','edit', 'properties'],
label: gettext('FOR Events'), group: gettext('Events'), contentClass: 'row', label: gettext('FOR Events'), group: gettext('Events'), contentClass: 'row',
@@ -45,12 +45,20 @@ class CompoundTriggersUpdateTestCase(BaseTestGenerator):
})), })),
('Enable compound trigger', ('Enable compound trigger',
dict(url='/browser/compound_trigger/obj/', dict(url='/browser/compound_trigger/obj/',
data={"is_enable_trigger": True}, data={"is_enable_trigger": 'O'},
disable_trigger=True disable_trigger=True
)), )),
('Enable always compound trigger',
dict(url='/browser/compound_trigger/obj/',
data={"is_enable_trigger": 'A'}
)),
('Enable replica compound trigger',
dict(url='/browser/compound_trigger/obj/',
data={"is_enable_trigger": 'R'}
)),
('Disable compound trigger', ('Disable compound trigger',
dict(url='/browser/compound_trigger/obj/', dict(url='/browser/compound_trigger/obj/',
data={"is_enable_trigger": False} data={"is_enable_trigger": 'D'}
)), )),
] ]
@@ -10,10 +10,10 @@
import axios from 'axios'; import axios from 'axios';
export function disableTriggers(tree, alertify, generateUrl, args) { export function disableTriggers(tree, alertify, generateUrl, args) {
return setTriggers(tree, alertify, generateUrl, args, {enable: 'false' }); return setTriggers(tree, alertify, generateUrl, args, {is_enable_trigger: 'D' });
} }
export function enableTriggers(tree, alertify, generateUrl, args) { export function enableTriggers(tree, alertify, generateUrl, args) {
return setTriggers(tree, alertify, generateUrl, args, {enable: 'true' }); return setTriggers(tree, alertify, generateUrl, args, {is_enable_trigger: 'O' });
} }
function setTriggers(tree, alertify, generateUrl, args, params) { function setTriggers(tree, alertify, generateUrl, args, params) {
@@ -1,2 +1,3 @@
{% set enable_map = {'R':'ENABLE REPLICA', 'A':'ENABLE ALWAYS', 'O':'ENABLE', 'D':'DISABLE'} %}
ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }} ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
{% if data.is_enable_trigger == True %}ENABLE{% else %}DISABLE{% endif %} TRIGGER {{ conn|qtIdent(data.name) }}; {{ enable_map[data.is_enable_trigger] }} TRIGGER {{ conn|qtIdent(data.name) }};
@@ -1,4 +1,4 @@
SELECT t.oid, t.tgname as name, (CASE WHEN tgenabled = 'O' THEN true ElSE false END) AS is_enable_trigger SELECT t.oid, t.tgname as name, t.tgenabled AS is_enable_trigger
FROM pg_trigger t FROM pg_trigger t
WHERE NOT tgisinternal WHERE NOT tgisinternal
@@ -1,4 +1,4 @@
SELECT t.oid,t.tgname AS name, t.xmin, t.tgtype, t.tgattr, relname, SELECT t.oid,t.tgname AS name, t.xmin, t.tgenabled AS is_enable_trigger, t.tgtype, t.tgattr, relname,
CASE WHEN relkind = 'r' THEN TRUE ELSE FALSE END AS parentistable, CASE WHEN relkind = 'r' THEN TRUE ELSE FALSE END AS parentistable,
nspname, des.description, nspname, des.description,
regexp_replace(regexp_replace(pg_get_triggerdef(t.oid), regexp_replace(regexp_replace(pg_get_triggerdef(t.oid),
@@ -6,9 +6,8 @@ SELECT t.oid,t.tgname AS name, t.xmin, t.tgtype, t.tgattr, relname,
) AS prosrc, ) AS prosrc,
COALESCE(substring(pg_get_triggerdef(t.oid), 'WHEN (.*) \nCOMPOUND'), NULL) AS whenclause, COALESCE(substring(pg_get_triggerdef(t.oid), 'WHEN (.*) \nCOMPOUND'), NULL) AS whenclause,
{% if datlastsysoid %} {% if datlastsysoid %}
(CASE WHEN t.oid <= {{ datlastsysoid}}::oid THEN true ElSE false END) AS is_sys_trigger, (CASE WHEN t.oid <= {{ datlastsysoid}}::oid THEN true ElSE false END) AS is_sys_trigger
{% endif %} {% endif %}
(CASE WHEN tgenabled = 'O' THEN true ElSE false END) AS is_enable_trigger
FROM pg_trigger t FROM pg_trigger t
JOIN pg_class cl ON cl.oid=tgrelid JOIN pg_class cl ON cl.oid=tgrelid
JOIN pg_namespace na ON na.oid=relnamespace JOIN pg_namespace na ON na.oid=relnamespace
@@ -38,6 +38,7 @@ COMMENT ON TRIGGER {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(o_data.nspna
IS {{data.description|qtLiteral}}; IS {{data.description|qtLiteral}};
{% endif %} {% endif %}
{% if data.is_enable_trigger is defined and o_data.is_enable_trigger != data.is_enable_trigger %} {% if data.is_enable_trigger is defined and o_data.is_enable_trigger != data.is_enable_trigger %}
{% set enable_map = {'R':'ENABLE REPLICA', 'A':'ENABLE ALWAYS', 'O':'ENABLE', 'D':'DISABLE'} %}
ALTER TABLE {{ conn|qtIdent(o_data.nspname, o_data.relname) }} ALTER TABLE {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
{% if data.is_enable_trigger == True %}ENABLE{% else %}DISABLE{% endif %} TRIGGER {{ conn|qtIdent(data.name) }}; {{ enable_map[data.is_enable_trigger] }} TRIGGER {{ conn|qtIdent(data.name) }};
{% endif %} {% endif %}
@@ -1,2 +1,3 @@
{% set enable_map = {'O':'ENABLE', 'D':'DISABLE'} %}
ALTER TABLE {{ conn|qtIdent(data.schema, data.name) }} ALTER TABLE {{ conn|qtIdent(data.schema, data.name) }}
{% if is_enable_trigger == True %}ENABLE{% else %}DISABLE{% endif %} TRIGGER ALL; {{ enable_map[is_enable_trigger] }} TRIGGER ALL;
@@ -1,6 +1,5 @@
SELECT t.oid, t.tgname as name, (CASE WHEN tgenabled = 'O' THEN true ElSE false END) AS is_enable_trigger SELECT t.oid, t.tgname as name, t.tgenabled AS is_enable_trigger
FROM pg_trigger t FROM pg_trigger t
WHERE NOT tgisinternal WHERE NOT tgisinternal
AND tgrelid = {{tid}}::OID AND tgrelid = {{tid}}::OID
{% if trid %} {% if trid %}
@@ -1,4 +1,4 @@
SELECT t.oid,t.tgname AS name, t.xmin, t.*, relname, CASE WHEN relkind = 'r' THEN TRUE ELSE FALSE END AS parentistable, SELECT t.oid,t.tgname AS name, t.xmin, t.tgenabled AS is_enable_trigger, t.*, relname, CASE WHEN relkind = 'r' THEN TRUE ELSE FALSE END AS parentistable,
nspname, des.description, l.lanname, p.prosrc, p.proname AS tfunction, nspname, des.description, l.lanname, p.prosrc, p.proname AS tfunction,
COALESCE(substring(pg_get_triggerdef(t.oid), 'WHEN (.*) EXECUTE PROCEDURE'), COALESCE(substring(pg_get_triggerdef(t.oid), 'WHEN (.*) EXECUTE PROCEDURE'),
substring(pg_get_triggerdef(t.oid), 'WHEN (.*) \$trigger')) AS whenclause, substring(pg_get_triggerdef(t.oid), 'WHEN (.*) \$trigger')) AS whenclause,
@@ -7,8 +7,7 @@ SELECT t.oid,t.tgname AS name, t.xmin, t.*, relname, CASE WHEN relkind = 'r' THE
{% if datlastsysoid %} {% if datlastsysoid %}
(CASE WHEN t.oid <= {{ datlastsysoid}}::oid THEN true ElSE false END) AS is_sys_trigger, (CASE WHEN t.oid <= {{ datlastsysoid}}::oid THEN true ElSE false END) AS is_sys_trigger,
{% endif %} {% endif %}
(CASE WHEN tgconstraint != 0::OID THEN true ElSE false END) AS is_constraint_trigger, (CASE WHEN tgconstraint != 0::OID THEN true ElSE false END) AS is_constraint_trigger
(CASE WHEN tgenabled = 'O' THEN true ElSE false END) AS is_enable_trigger
FROM pg_trigger t FROM pg_trigger t
JOIN pg_class cl ON cl.oid=tgrelid JOIN pg_class cl ON cl.oid=tgrelid
JOIN pg_namespace na ON na.oid=relnamespace JOIN pg_namespace na ON na.oid=relnamespace
@@ -1,2 +1,3 @@
{% set enable_map = {'R':'ENABLE REPLICA', 'A':'ENABLE ALWAYS', 'O':'ENABLE', 'D':'DISABLE'} %}
ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }} ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
{% if data.is_enable_trigger == True %}ENABLE{% else %}DISABLE{% endif %} TRIGGER {{ conn|qtIdent(data.name) }}; {{ enable_map[data.is_enable_trigger] }} TRIGGER {{ conn|qtIdent(data.name) }};
@@ -1,4 +1,4 @@
SELECT t.oid, t.tgname as name, (CASE WHEN tgenabled = 'O' THEN true ElSE false END) AS is_enable_trigger SELECT t.oid, t.tgname as name, t.tgenabled AS is_enable_trigger
FROM pg_trigger t FROM pg_trigger t
WHERE tgrelid = {{tid}}::OID WHERE tgrelid = {{tid}}::OID
{% if trid %} {% if trid %}
@@ -45,6 +45,7 @@ COMMENT ON TRIGGER {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(o_data.nspna
IS {{data.description|qtLiteral}}; IS {{data.description|qtLiteral}};
{% endif %} {% endif %}
{% if data.is_enable_trigger is defined and o_data.is_enable_trigger != data.is_enable_trigger %} {% if data.is_enable_trigger is defined and o_data.is_enable_trigger != data.is_enable_trigger %}
{% set enable_map = {'R':'ENABLE REPLICA', 'A':'ENABLE ALWAYS', 'O':'ENABLE', 'D':'DISABLE'} %}
ALTER TABLE {{ conn|qtIdent(o_data.nspname, o_data.relname) }} ALTER TABLE {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
{% if data.is_enable_trigger == True %}ENABLE{% else %}DISABLE{% endif %} TRIGGER {{ conn|qtIdent(data.name) }}; {{ enable_map[data.is_enable_trigger] }} TRIGGER {{ conn|qtIdent(data.name) }};
{% endif %} {% endif %}
@@ -1,4 +1,4 @@
SELECT t.oid,t.tgname AS name, t.xmin, t.*, relname, CASE WHEN relkind = 'r' THEN TRUE ELSE FALSE END AS parentistable, SELECT t.oid,t.tgname AS name, t.xmin, t.tgenabled AS is_enable_trigger, t.*, relname, CASE WHEN relkind = 'r' THEN TRUE ELSE FALSE END AS parentistable,
nspname, des.description, l.lanname, p.prosrc, p.proname AS tfunction, nspname, des.description, l.lanname, p.prosrc, p.proname AS tfunction,
COALESCE(substring(pg_get_triggerdef(t.oid), 'WHEN (.*) EXECUTE PROCEDURE'), COALESCE(substring(pg_get_triggerdef(t.oid), 'WHEN (.*) EXECUTE PROCEDURE'),
substring(pg_get_triggerdef(t.oid), 'WHEN (.*) \$trigger')) AS whenclause, substring(pg_get_triggerdef(t.oid), 'WHEN (.*) \$trigger')) AS whenclause,
@@ -8,7 +8,6 @@ SELECT t.oid,t.tgname AS name, t.xmin, t.*, relname, CASE WHEN relkind = 'r' THE
(CASE WHEN t.oid <= {{ datlastsysoid}}::oid THEN true ElSE false END) AS is_sys_trigger, (CASE WHEN t.oid <= {{ datlastsysoid}}::oid THEN true ElSE false END) AS is_sys_trigger,
{% endif %} {% endif %}
(CASE WHEN tgconstraint != 0::OID THEN true ElSE false END) AS is_constraint_trigger, (CASE WHEN tgconstraint != 0::OID THEN true ElSE false END) AS is_constraint_trigger,
(CASE WHEN tgenabled = 'O' THEN true ElSE false END) AS is_enable_trigger,
tgoldtable, tgoldtable,
tgnewtable tgnewtable
FROM pg_trigger t FROM pg_trigger t
@@ -1,6 +1,5 @@
SELECT t.oid, t.tgname as name, (CASE WHEN tgenabled = 'O' THEN true ElSE false END) AS is_enable_trigger SELECT t.oid, t.tgname as name, t.tgenabled AS is_enable_trigger
FROM pg_trigger t FROM pg_trigger t
WHERE NOT tgisinternal WHERE NOT tgisinternal
AND tgrelid = {{tid}}::OID AND tgrelid = {{tid}}::OID
{% if trid %} {% if trid %}
@@ -1,4 +1,4 @@
SELECT t.oid,t.tgname AS name, t.xmin, t.*, relname, CASE WHEN relkind = 'r' THEN TRUE ELSE FALSE END AS parentistable, SELECT t.oid,t.tgname AS name, t.xmin, t.tgenabled AS is_enable_trigger, t.*, relname, CASE WHEN relkind = 'r' THEN TRUE ELSE FALSE END AS parentistable,
nspname, des.description, l.lanname, p.prosrc, p.proname AS tfunction, nspname, des.description, l.lanname, p.prosrc, p.proname AS tfunction,
COALESCE(substring(pg_get_triggerdef(t.oid), 'WHEN (.*) EXECUTE PROCEDURE'), COALESCE(substring(pg_get_triggerdef(t.oid), 'WHEN (.*) EXECUTE PROCEDURE'),
substring(pg_get_triggerdef(t.oid), 'WHEN (.*) \$trigger')) AS whenclause, substring(pg_get_triggerdef(t.oid), 'WHEN (.*) \$trigger')) AS whenclause,
@@ -7,8 +7,7 @@ SELECT t.oid,t.tgname AS name, t.xmin, t.*, relname, CASE WHEN relkind = 'r' THE
{% if datlastsysoid %} {% if datlastsysoid %}
(CASE WHEN t.oid <= {{ datlastsysoid}}::oid THEN true ElSE false END) AS is_sys_trigger, (CASE WHEN t.oid <= {{ datlastsysoid}}::oid THEN true ElSE false END) AS is_sys_trigger,
{% endif %} {% endif %}
(CASE WHEN tgconstraint != 0::OID THEN true ElSE false END) AS is_constraint_trigger, (CASE WHEN tgconstraint != 0::OID THEN true ElSE false END) AS is_constraint_trigger
(CASE WHEN tgenabled = 'O' THEN true ElSE false END) AS is_enable_trigger
FROM pg_trigger t FROM pg_trigger t
JOIN pg_class cl ON cl.oid=tgrelid JOIN pg_class cl ON cl.oid=tgrelid
JOIN pg_namespace na ON na.oid=relnamespace JOIN pg_namespace na ON na.oid=relnamespace
@@ -1,2 +1,3 @@
{% set enable_map = {'R':'ENABLE REPLICA', 'A':'ENABLE ALWAYS', 'O':'ENABLE', 'D':'DISABLE'} %}
ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }} ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
{% if data.is_enable_trigger == True %}ENABLE{% else %}DISABLE{% endif %} TRIGGER {{ conn|qtIdent(data.name) }}; {{ enable_map[data.is_enable_trigger] }} TRIGGER {{ conn|qtIdent(data.name) }};
@@ -1,4 +1,4 @@
SELECT t.oid, t.tgname as name, (CASE WHEN tgenabled = 'O' THEN true ElSE false END) AS is_enable_trigger SELECT t.oid, t.tgname as name, t.tgenabled AS is_enable_trigger
FROM pg_trigger t FROM pg_trigger t
WHERE tgrelid = {{tid}}::OID WHERE tgrelid = {{tid}}::OID
{% if trid %} {% if trid %}
@@ -45,6 +45,7 @@ COMMENT ON TRIGGER {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(o_data.nspna
IS {{data.description|qtLiteral}}; IS {{data.description|qtLiteral}};
{% endif %} {% endif %}
{% if data.is_enable_trigger is defined and o_data.is_enable_trigger != data.is_enable_trigger %} {% if data.is_enable_trigger is defined and o_data.is_enable_trigger != data.is_enable_trigger %}
{% set enable_map = {'R':'ENABLE REPLICA', 'A':'ENABLE ALWAYS', 'O':'ENABLE', 'D':'DISABLE'} %}
ALTER TABLE {{ conn|qtIdent(o_data.nspname, o_data.relname) }} ALTER TABLE {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
{% if data.is_enable_trigger == True %}ENABLE{% else %}DISABLE{% endif %} TRIGGER {{ conn|qtIdent(data.name) }}; {{ enable_map[data.is_enable_trigger] }} TRIGGER {{ conn|qtIdent(data.name) }};
{% endif %} {% endif %}
@@ -1,4 +1,4 @@
SELECT t.oid,t.tgname AS name, t.xmin, t.*, relname, CASE WHEN relkind = 'r' THEN TRUE ELSE FALSE END AS parentistable, SELECT t.oid,t.tgname AS name, t.xmin, t.tgenabled AS is_enable_trigger, t.*, relname, CASE WHEN relkind = 'r' THEN TRUE ELSE FALSE END AS parentistable,
nspname, des.description, l.lanname, p.prosrc, p.proname AS tfunction, nspname, des.description, l.lanname, p.prosrc, p.proname AS tfunction,
COALESCE(substring(pg_get_triggerdef(t.oid), 'WHEN (.*) EXECUTE PROCEDURE'), COALESCE(substring(pg_get_triggerdef(t.oid), 'WHEN (.*) EXECUTE PROCEDURE'),
substring(pg_get_triggerdef(t.oid), 'WHEN (.*) \$trigger')) AS whenclause, substring(pg_get_triggerdef(t.oid), 'WHEN (.*) \$trigger')) AS whenclause,
@@ -8,7 +8,6 @@ SELECT t.oid,t.tgname AS name, t.xmin, t.*, relname, CASE WHEN relkind = 'r' THE
(CASE WHEN t.oid <= {{ datlastsysoid}}::oid THEN true ElSE false END) AS is_sys_trigger, (CASE WHEN t.oid <= {{ datlastsysoid}}::oid THEN true ElSE false END) AS is_sys_trigger,
{% endif %} {% endif %}
(CASE WHEN tgconstraint != 0::OID THEN true ElSE false END) AS is_constraint_trigger, (CASE WHEN tgconstraint != 0::OID THEN true ElSE false END) AS is_constraint_trigger,
(CASE WHEN tgenabled = 'O' THEN true ElSE false END) AS is_enable_trigger,
tgoldtable, tgoldtable,
tgnewtable tgnewtable
FROM pg_trigger t FROM pg_trigger t
@@ -1,6 +1,5 @@
SELECT t.oid, t.tgname as name, (CASE WHEN tgenabled = 'O' THEN true ElSE false END) AS is_enable_trigger SELECT t.oid, t.tgname as name, t.tgenabled AS is_enable_trigger
FROM pg_trigger t FROM pg_trigger t
WHERE NOT tgisinternal WHERE NOT tgisinternal
AND tgrelid = {{tid}}::OID AND tgrelid = {{tid}}::OID
AND tgpackageoid = 0 AND tgpackageoid = 0
@@ -1,4 +1,4 @@
SELECT t.oid,t.tgname AS name, t.xmin, t.*, relname, CASE WHEN relkind = 'r' THEN TRUE ELSE FALSE END AS parentistable, SELECT t.oid,t.tgname AS name, t.xmin, t.tgenabled AS is_enable_trigger, t.*, relname, CASE WHEN relkind = 'r' THEN TRUE ELSE FALSE END AS parentistable,
nspname, des.description, l.lanname, p.prosrc, p.proname AS tfunction, nspname, des.description, l.lanname, p.prosrc, p.proname AS tfunction,
COALESCE(substring(pg_get_triggerdef(t.oid), 'WHEN (.*) EXECUTE PROCEDURE'), COALESCE(substring(pg_get_triggerdef(t.oid), 'WHEN (.*) EXECUTE PROCEDURE'),
substring(pg_get_triggerdef(t.oid), 'WHEN (.*) \$trigger')) AS whenclause, substring(pg_get_triggerdef(t.oid), 'WHEN (.*) \$trigger')) AS whenclause,
@@ -8,7 +8,6 @@ SELECT t.oid,t.tgname AS name, t.xmin, t.*, relname, CASE WHEN relkind = 'r' THE
(CASE WHEN t.oid <= {{ datlastsysoid}}::oid THEN true ElSE false END) AS is_sys_trigger, (CASE WHEN t.oid <= {{ datlastsysoid}}::oid THEN true ElSE false END) AS is_sys_trigger,
{% endif %} {% endif %}
(CASE WHEN tgconstraint != 0::OID THEN true ElSE false END) AS is_constraint_trigger, (CASE WHEN tgconstraint != 0::OID THEN true ElSE false END) AS is_constraint_trigger,
(CASE WHEN tgenabled = 'O' THEN true ElSE false END) AS is_enable_trigger,
tgoldtable, tgoldtable,
tgnewtable tgnewtable
FROM pg_trigger t FROM pg_trigger t
@@ -1,6 +1,5 @@
SELECT t.oid, t.tgname as name, (CASE WHEN tgenabled = 'O' THEN true ElSE false END) AS is_enable_trigger SELECT t.oid, t.tgname as name, t.tgenabled AS is_enable_trigger
FROM pg_trigger t FROM pg_trigger t
WHERE NOT tgisinternal WHERE NOT tgisinternal
AND tgrelid = {{tid}}::OID AND tgrelid = {{tid}}::OID
{% if trid %} {% if trid %}
@@ -1,4 +1,4 @@
SELECT t.oid,t.tgname AS name, t.xmin, t.*, relname, CASE WHEN relkind = 'r' THEN TRUE ELSE FALSE END AS parentistable, SELECT t.oid,t.tgname AS name, t.xmin, t.tgenabled AS is_enable_trigger, t.*, relname, CASE WHEN relkind = 'r' THEN TRUE ELSE FALSE END AS parentistable,
nspname, des.description, l.lanname, p.prosrc, p.proname AS tfunction, nspname, des.description, l.lanname, p.prosrc, p.proname AS tfunction,
COALESCE(substring(pg_get_triggerdef(t.oid), 'WHEN (.*) EXECUTE PROCEDURE'), COALESCE(substring(pg_get_triggerdef(t.oid), 'WHEN (.*) EXECUTE PROCEDURE'),
substring(pg_get_triggerdef(t.oid), 'WHEN (.*) \$trigger')) AS whenclause, substring(pg_get_triggerdef(t.oid), 'WHEN (.*) \$trigger')) AS whenclause,
@@ -7,8 +7,7 @@ SELECT t.oid,t.tgname AS name, t.xmin, t.*, relname, CASE WHEN relkind = 'r' THE
{% if datlastsysoid %} {% if datlastsysoid %}
(CASE WHEN t.oid <= {{ datlastsysoid}}::oid THEN true ElSE false END) AS is_sys_trigger, (CASE WHEN t.oid <= {{ datlastsysoid}}::oid THEN true ElSE false END) AS is_sys_trigger,
{% endif %} {% endif %}
(CASE WHEN tgconstraint != 0::OID THEN true ElSE false END) AS is_constraint_trigger, (CASE WHEN tgconstraint != 0::OID THEN true ElSE false END) AS is_constraint_trigger
(CASE WHEN tgenabled = 'O' THEN true ElSE false END) AS is_enable_trigger
FROM pg_trigger t FROM pg_trigger t
JOIN pg_class cl ON cl.oid=tgrelid JOIN pg_class cl ON cl.oid=tgrelid
JOIN pg_namespace na ON na.oid=relnamespace JOIN pg_namespace na ON na.oid=relnamespace
@@ -1,2 +1,3 @@
{% set enable_map = {'R':'ENABLE REPLICA', 'A':'ENABLE ALWAYS', 'O':'ENABLE', 'D':'DISABLE'} %}
ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }} ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
{% if data.is_enable_trigger == True %}ENABLE{% else %}DISABLE{% endif %} TRIGGER {{ conn|qtIdent(data.name) }}; {{ enable_map[data.is_enable_trigger] }} TRIGGER {{ conn|qtIdent(data.name) }};
@@ -1,4 +1,4 @@
SELECT t.oid, t.tgname as name, (CASE WHEN tgenabled = 'O' THEN true ElSE false END) AS is_enable_trigger SELECT t.oid, t.tgname as name, t.tgenabled AS is_enable_trigger
FROM pg_trigger t FROM pg_trigger t
WHERE tgrelid = {{tid}}::OID WHERE tgrelid = {{tid}}::OID
{% if trid %} {% if trid %}
@@ -45,6 +45,7 @@ COMMENT ON TRIGGER {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(o_data.nspna
IS {{data.description|qtLiteral}}; IS {{data.description|qtLiteral}};
{% endif %} {% endif %}
{% if data.is_enable_trigger is defined and o_data.is_enable_trigger != data.is_enable_trigger %} {% if data.is_enable_trigger is defined and o_data.is_enable_trigger != data.is_enable_trigger %}
{% set enable_map = {'R':'ENABLE REPLICA', 'A':'ENABLE ALWAYS', 'O':'ENABLE', 'D':'DISABLE'} %}
ALTER TABLE {{ conn|qtIdent(o_data.nspname, o_data.relname) }} ALTER TABLE {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
{% if data.is_enable_trigger == True %}ENABLE{% else %}DISABLE{% endif %} TRIGGER {{ conn|qtIdent(data.name) }}; {{ enable_map[data.is_enable_trigger] }} TRIGGER {{ conn|qtIdent(data.name) }};
{% endif %} {% endif %}
@@ -410,8 +410,8 @@ class TriggerView(PGChildNodeView):
rset['rows'][0]['oid'], rset['rows'][0]['oid'],
tid, tid,
rset['rows'][0]['name'], rset['rows'][0]['name'],
icon="icon-trigger" if icon="icon-trigger-bad" if
rset['rows'][0]['is_enable_trigger'] else "icon-trigger-bad" rset['rows'][0]['is_enable_trigger'] == 'D' else "icon-trigger"
) )
return make_json_response( return make_json_response(
@@ -449,8 +449,8 @@ class TriggerView(PGChildNodeView):
row['oid'], row['oid'],
tid, tid,
row['name'], row['name'],
icon="icon-trigger" if row['is_enable_trigger'] icon="icon-trigger-bad" if row['is_enable_trigger'] == 'D'
else "icon-trigger-bad" else "icon-trigger"
)) ))
return make_json_response( return make_json_response(
@@ -804,9 +804,9 @@ class TriggerView(PGChildNodeView):
new_trid, new_trid,
tid, tid,
name, name,
icon="icon-%s" % self.node_type if icon="icon-%s-bad" % self.node_type if
data['is_enable_trigger'] else data['is_enable_trigger'] == 'D' else
"icon-%s-bad" % self.node_type "icon-%s" % self.node_type
) )
) )
except Exception as e: except Exception as e:
@@ -999,7 +999,7 @@ class TriggerView(PGChildNodeView):
SQL = sql_header + '\n\n' + SQL.strip('\n') SQL = sql_header + '\n\n' + SQL.strip('\n')
# If trigger is disbaled then add sql code for the same # If trigger is disbaled then add sql code for the same
if not data['is_enable_trigger']: if data['is_enable_trigger'] != 'O':
SQL += '\n\n' SQL += '\n\n'
SQL += render_template("/".join([self.template_path, SQL += render_template("/".join([self.template_path,
'enable_disable_trigger.sql']), 'enable_disable_trigger.sql']),
@@ -1025,8 +1025,7 @@ class TriggerView(PGChildNodeView):
request.data, encoding='utf-8' request.data, encoding='utf-8'
) )
# Convert str 'true' to boolean type is_enable_trigger = data['is_enable_trigger']
is_enable_flag = json.loads(data['enable'])
try: try:
@@ -1049,7 +1048,7 @@ class TriggerView(PGChildNodeView):
# current trigger which is disabled already so we need to # current trigger which is disabled already so we need to
# alter the 'is_enable_trigger' flag so that we can render # alter the 'is_enable_trigger' flag so that we can render
# correct SQL for operation # correct SQL for operation
o_data['is_enable_trigger'] = is_enable_flag o_data['is_enable_trigger'] = is_enable_trigger
# Adding parent into data dict, will be using it while creating sql # Adding parent into data dict, will be using it while creating sql
o_data['schema'] = self.schema o_data['schema'] = self.schema
@@ -108,7 +108,7 @@ define('pgadmin.node.trigger', [
$.ajax({ $.ajax({
url: obj.generate_url(i, 'enable' , d, true), url: obj.generate_url(i, 'enable' , d, true),
type:'PUT', type:'PUT',
data: {'enable' : true}, data: {'is_enable_trigger' : 'O'},
dataType: 'json', dataType: 'json',
}) })
.done(function(res) { .done(function(res) {
@@ -146,7 +146,7 @@ define('pgadmin.node.trigger', [
$.ajax({ $.ajax({
url: obj.generate_url(i, 'enable' , d, true), url: obj.generate_url(i, 'enable' , d, true),
type:'PUT', type:'PUT',
data: {'enable' : false}, data: {'is_enable_trigger' : 'D'},
dataType: 'json', dataType: 'json',
}) })
.done(function(res) { .done(function(res) {
@@ -187,13 +187,20 @@ define('pgadmin.node.trigger', [
type: 'int', disabled: true, mode: ['properties'], type: 'int', disabled: true, mode: ['properties'],
},{ },{
id: 'is_enable_trigger', label: gettext('Trigger enabled?'), id: 'is_enable_trigger', label: gettext('Trigger enabled?'),
type: 'switch', mode: ['edit', 'properties'], group: gettext('Definition'), mode: ['edit', 'properties'], group: gettext('Definition'),
disabled: function() { disabled: function() {
if(this.node_info && ('catalog' in this.node_info || 'view' in this.node_info)) { if(this.node_info && ('catalog' in this.node_info || 'view' in this.node_info)) {
return true; return true;
} }
return false; return false;
}, },
options: [
{label: 'Enable', value: 'O'},
{label: 'Enable Replica', value: 'R'},
{label: 'Enable Always', value: 'A'},
{label: 'Disable', value: 'D'},
],
control: 'select2', select2: { allowClear: false, width: '100%' },
},{ },{
id: 'is_row_trigger', label: gettext('Row trigger?'), id: 'is_row_trigger', label: gettext('Row trigger?'),
type: 'switch', group: gettext('Definition'), type: 'switch', group: gettext('Definition'),
@@ -352,7 +359,6 @@ define('pgadmin.node.trigger', [
return view_options; return view_options;
} }
}, },
// If create mode then by default open composite type
control: 'select2', select2: { allowClear: false, width: '100%' }, control: 'select2', select2: { allowClear: false, width: '100%' },
disabled: function(m) { disabled: function(m) {
// If contraint trigger is set to True then only enable it // If contraint trigger is set to True then only enable it
@@ -28,7 +28,18 @@ class TriggersUpdateTestCase(BaseTestGenerator):
"""This class will update trigger under table node.""" """This class will update trigger under table node."""
skip_on_database = ['gpdb'] skip_on_database = ['gpdb']
scenarios = [ scenarios = [
('Put trigger Node URL', dict(url='/browser/trigger/obj/')) ('Put trigger Node URL', dict(
url='/browser/trigger/obj/',
data={"description": "This is test comment."})),
('Enable Always compound trigger',
dict(url='/browser/trigger/obj/',
data={"is_enable_trigger": 'A'})),
('Enable Replica compound trigger',
dict(url='/browser/trigger/obj/',
data={"is_enable_trigger": 'R'})),
('Disable compound trigger',
dict(url='/browser/trigger/obj/',
data={"is_enable_trigger": 'D'})),
] ]
def setUp(self): def setUp(self):
@@ -72,15 +83,14 @@ class TriggersUpdateTestCase(BaseTestGenerator):
self.trigger_name) self.trigger_name)
if not trigger_response: if not trigger_response:
raise Exception("Could not find the trigger to update.") raise Exception("Could not find the trigger to update.")
data = {"id": self.trigger_id,
"description": "This is test comment." self.data.update({"id": self.trigger_id})
}
response = self.tester.put( response = self.tester.put(
"{0}{1}/{2}/{3}/{4}/{5}/{6}".format(self.url, utils.SERVER_GROUP, "{0}{1}/{2}/{3}/{4}/{5}/{6}".format(self.url, utils.SERVER_GROUP,
self.server_id, self.db_id, self.server_id, self.db_id,
self.schema_id, self.table_id, self.schema_id, self.table_id,
self.trigger_id), self.trigger_id),
data=json.dumps(data), data=json.dumps(self.data),
follow_redirects=True follow_redirects=True
) )
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
@@ -1075,7 +1075,7 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
trigger_sql = sql_header + '\n\n' + trigger_sql.strip('\n') trigger_sql = sql_header + '\n\n' + trigger_sql.strip('\n')
# If trigger is disabled then add sql code for the same # If trigger is disabled then add sql code for the same
if not data['is_enable_trigger']: if data['is_enable_trigger'] != 'O':
trigger_sql += '\n\n' trigger_sql += '\n\n'
trigger_sql += render_template("/".join([ trigger_sql += render_template("/".join([
self.trigger_template_path, self.trigger_template_path,
@@ -1155,7 +1155,7 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
sql_header + '\n\n' + compound_trigger_sql.strip('\n') sql_header + '\n\n' + compound_trigger_sql.strip('\n')
# If trigger is disabled then add sql code for the same # If trigger is disabled then add sql code for the same
if not data['is_enable_trigger']: if data['is_enable_trigger'] != 'O':
compound_trigger_sql += '\n\n' compound_trigger_sql += '\n\n'
compound_trigger_sql += render_template("/".join( compound_trigger_sql += render_template("/".join(
[self.compound_trigger_template_path, [self.compound_trigger_template_path,
@@ -106,7 +106,7 @@ describe('#enableTriggers', () => {
it('call backend with the correct parameters', (done) => { it('call backend with the correct parameters', (done) => {
enableTriggers(tree, alertify, generateUrlSpy, {item: [{id: 'table1'}]}); enableTriggers(tree, alertify, generateUrlSpy, {item: [{id: 'table1'}]});
setTimeout(() => { setTimeout(() => {
expect(networkMockCalledWith.data).toEqual(JSON.stringify({enable: 'true'})); expect(networkMockCalledWith.data).toEqual(JSON.stringify({is_enable_trigger: 'O'}));
done(); done();
}, 0); }, 0);
}); });
@@ -234,7 +234,7 @@ describe('#disableTriggers', () => {
it('call backend with the correct parameters', (done) => { it('call backend with the correct parameters', (done) => {
disableTriggers(tree, alertify, generateUrlSpy, {item: [{id: 'table1'}]}); disableTriggers(tree, alertify, generateUrlSpy, {item: [{id: 'table1'}]});
setTimeout(() => { setTimeout(() => {
expect(networkMockCalledWith.data).toEqual(JSON.stringify({enable: 'false'})); expect(networkMockCalledWith.data).toEqual(JSON.stringify({is_enable_trigger: 'D'}));
done(); done();
}, 0); }, 0);
}); });