Fix an issue where command and statements were parsed incorrectly for Rules. Fixes #5115

Fixed issue where removing command or statements altogether was not generating modified SQL.
This commit is contained in:
Aditya Toshniwal 2020-02-24 12:11:19 +05:30 committed by Akshay Joshi
parent 915b09255c
commit a44f17a2fc
21 changed files with 123 additions and 73 deletions

View File

@ -22,4 +22,5 @@ Housekeeping
Bug fixes
*********
| `Issue #5107 <https://redmine.postgresql.org/issues/5107>`_ - Set proper focus on tab navigation for file manager dialog.
| `Issue #5107 <https://redmine.postgresql.org/issues/5107>`_ - Set proper focus on tab navigation for file manager dialog.
| `Issue #5115 <https://redmine.postgresql.org/issues/5115>`_ - Fix an issue where command and statements were parsed incorrectly for Rules.

View File

@ -169,10 +169,10 @@ define('pgadmin.node.rule', [
allowClear: false,
},
options:[
{label: 'Select', value: 'Select'},
{label: 'Insert', value: 'Insert'},
{label: 'Update', value: 'Update'},
{label: 'Delete', value: 'Delete'},
{label: 'SELECT', value: 'SELECT'},
{label: 'INSERT', value: 'INSERT'},
{label: 'UPDATE', value: 'UPDATE'},
{label: 'DELETE', value: 'DELETE'},
],
},
{

View File

@ -5,7 +5,7 @@
CREATE OR REPLACE RULE "test_delete_rule1_$%{}[]()&*^!@""'`\/#" AS
ON DELETE TO public.test_emp_rule
DO INSTEAD
DELETE FROM test_emp_rule
WHERE test_emp_rule.name = old.name;
(DELETE FROM test_emp_rule
WHERE (test_emp_rule.name = old.name));
COMMENT ON RULE "test_delete_rule1_$%{}[]()&*^!@""'`\/#" ON public.test_emp_rule IS 'This is a delete rule';

View File

@ -3,7 +3,7 @@ ALTER RULE "test_delete_rule_$%{}[]()&*^!@""'`\/#" ON public.test_emp_rule RENAM
CREATE OR REPLACE RULE "test_delete_rule1_$%{}[]()&*^!@""'`\/#" AS
ON DELETE TO public.test_emp_rule
DO INSTEAD
DELETE FROM test_emp_rule
WHERE test_emp_rule.name = old.name;
(DELETE FROM test_emp_rule
WHERE test_emp_rule.name = old.name);
COMMENT ON RULE "test_delete_rule1_$%{}[]()&*^!@""'`\/#" ON public.test_emp_rule IS 'This is a delete rule';

View File

@ -4,9 +4,9 @@
CREATE OR REPLACE RULE "test_insert_rule1_$%{}[]()&*^!@""'`\/#" AS
ON INSERT TO public.test_emp_rule
WHERE new.salary > 8000
WHERE (new.salary > 8000)
DO INSTEAD
UPDATE test_emp_rule SET salary = 8000
WHERE test_emp_rule.emp_id = new.emp_id;
(UPDATE test_emp_rule SET salary = 8000
WHERE (test_emp_rule.emp_id = new.emp_id));
COMMENT ON RULE "test_insert_rule1_$%{}[]()&*^!@""'`\/#" ON public.test_emp_rule IS 'This is a insert rule';

View File

@ -0,0 +1,17 @@
-- Rule: "test_insert_rule1_$%{}[]()&*^!@""'`\/#" ON public.test_emp_rule
-- DROP Rule "test_insert_rule1_$%{}[]()&*^!@""'`\/#" ON public.test_emp_rule;
CREATE OR REPLACE RULE "test_insert_rule1_$%{}[]()&*^!@""'`\/#" AS
ON INSERT TO public.test_emp_rule
WHERE (new.salary > 8000)
DO INSTEAD
( UPDATE test_emp_rule SET salary = 8000
WHERE (test_emp_rule.emp_id = new.emp_id);
INSERT INTO test_emp_rule (emp_id, name, salary) SELECT 1,
'xyz',
2000
WHERE (1 = 2);
);
COMMENT ON RULE "test_insert_rule1_$%{}[]()&*^!@""'`\/#" ON public.test_emp_rule IS 'This is a insert rule';

View File

@ -0,0 +1,8 @@
CREATE OR REPLACE RULE "test_insert_rule1_$%{}[]()&*^!@""'`\/#" AS
ON INSERT TO public.test_emp_rule
WHERE (new.salary > 8000)
DO INSTEAD
(UPDATE test_emp_rule SET salary = 8000
WHERE test_emp_rule.emp_id = new.emp_id;
INSERT INTO test_emp_rule(emp_id, name, salary)
SELECT 1, 'xyz', 2000 WHERE 1=2);

View File

@ -2,9 +2,9 @@ ALTER RULE "test_insert_rule_$%{}[]()&*^!@""'`\/#" ON public.test_emp_rule RENAM
CREATE OR REPLACE RULE "test_insert_rule1_$%{}[]()&*^!@""'`\/#" AS
ON INSERT TO public.test_emp_rule
WHERE new.salary > 8000
WHERE (new.salary > 8000)
DO INSTEAD
UPDATE test_emp_rule SET salary = 8000
WHERE test_emp_rule.emp_id = new.emp_id;
(UPDATE test_emp_rule SET salary = 8000
WHERE test_emp_rule.emp_id = new.emp_id);
COMMENT ON RULE "test_insert_rule1_$%{}[]()&*^!@""'`\/#" ON public.test_emp_rule IS 'This is a insert rule';

View File

@ -4,9 +4,9 @@
CREATE OR REPLACE RULE "test_update_rule1_$%{}[]()&*^!@""'`\/#" AS
ON UPDATE TO public.test_emp_rule
WHERE old.name = 'Sam'::text
WHERE (old.name = 'Sam'::text)
DO INSTEAD
UPDATE test_emp_rule SET salary = new.salary
WHERE test_emp_rule.name = 'Joe'::text;
(UPDATE test_emp_rule SET salary = new.salary
WHERE (test_emp_rule.name = 'Joe'::text));
COMMENT ON RULE "test_update_rule1_$%{}[]()&*^!@""'`\/#" ON public.test_emp_rule IS 'This is a update rule';

View File

@ -2,9 +2,9 @@ ALTER RULE "test_update_rule_$%{}[]()&*^!@""'`\/#" ON public.test_emp_rule RENAM
CREATE OR REPLACE RULE "test_update_rule1_$%{}[]()&*^!@""'`\/#" AS
ON UPDATE TO public.test_emp_rule
WHERE old.name = 'Sam'
WHERE (old.name = 'Sam')
DO INSTEAD
UPDATE test_emp_rule SET salary = new.salary
WHERE test_emp_rule.name = 'Joe';
(UPDATE test_emp_rule SET salary = new.salary
WHERE test_emp_rule.name = 'Joe');
COMMENT ON RULE "test_update_rule1_$%{}[]()&*^!@""'`\/#" ON public.test_emp_rule IS 'This is a update rule';

View File

@ -4,5 +4,4 @@
CREATE OR REPLACE RULE "test_delete_rule_$%{}[]()&*^!@""'`\/#" AS
ON DELETE TO public.test_emp_rule
DO
NOTHING;
DO NOTHING;

View File

@ -1,4 +1,3 @@
CREATE OR REPLACE RULE "test_delete_rule_$%{}[]()&*^!@""'`\/#" AS
ON DELETE TO public.test_emp_rule
DO
NOTHING;
DO NOTHING;

View File

@ -4,7 +4,7 @@
CREATE OR REPLACE RULE "test_insert_rule_$%{}[]()&*^!@""'`\/#" AS
ON INSERT TO public.test_emp_rule
WHERE new.salary > 5000
WHERE (new.salary > 5000)
DO
UPDATE test_emp_rule SET salary = 5000
WHERE test_emp_rule.emp_id = new.emp_id;
(UPDATE test_emp_rule SET salary = 5000
WHERE (test_emp_rule.emp_id = new.emp_id));

View File

@ -1,6 +1,6 @@
CREATE OR REPLACE RULE "test_insert_rule_$%{}[]()&*^!@""'`\/#" AS
ON INSERT TO public.test_emp_rule
WHERE new.salary > 5000
WHERE (new.salary > 5000)
DO
UPDATE test_emp_rule SET salary = 5000
WHERE test_emp_rule.emp_id = new.emp_id;
(UPDATE test_emp_rule SET salary = 5000
WHERE test_emp_rule.emp_id = new.emp_id);

View File

@ -4,7 +4,7 @@
CREATE OR REPLACE RULE "test_update_rule_$%{}[]()&*^!@""'`\/#" AS
ON UPDATE TO public.test_emp_rule
WHERE old.name = 'Joe'::text
WHERE (old.name = 'Joe'::text)
DO
UPDATE test_emp_rule SET salary = new.salary
WHERE test_emp_rule.name = 'Sam'::text;
(UPDATE test_emp_rule SET salary = new.salary
WHERE (test_emp_rule.name = 'Sam'::text));

View File

@ -1,6 +1,6 @@
CREATE OR REPLACE RULE "test_update_rule_$%{}[]()&*^!@""'`\/#" AS
ON UPDATE TO public.test_emp_rule
WHERE old.name = 'Joe'
WHERE (old.name = 'Joe')
DO
UPDATE test_emp_rule SET salary = new.salary
WHERE test_emp_rule.name = 'Sam';
(UPDATE test_emp_rule SET salary = new.salary
WHERE test_emp_rule.name = 'Sam');

View File

@ -57,6 +57,17 @@
},
"expected_sql_file": "alter_insert_event_rule.sql",
"expected_msql_file": "alter_insert_event_rule_msql.sql"
}, {
"type": "alter",
"name": "Alter Rule for insert event with complex commands",
"endpoint": "NODE-rule.obj_id",
"sql_endpoint": "NODE-rule.sql_id",
"msql_endpoint": "NODE-rule.msql_id",
"data": {
"statements": "UPDATE test_emp_rule SET salary = 8000\n\tWHERE test_emp_rule.emp_id = new.emp_id;\nINSERT INTO test_emp_rule(emp_id, name, salary)\nSELECT 1, 'xyz', 2000 WHERE 1=2;"
},
"expected_sql_file": "alter_insert_event_rule_complex.sql",
"expected_msql_file": "alter_insert_event_rule_complex_msql.sql"
}, {
"type": "delete",
"name": "Drop Rule",

View File

@ -9,17 +9,17 @@
CREATE OR REPLACE RULE {{ conn|qtIdent(data.name) }} AS
ON {{ data.event|upper if data.event else 'SELECT' }} TO {{ conn|qtIdent(data.schema, data.view) }}
{% if data.condition %}
WHERE {{ data.condition }}
WHERE ({{ data.condition }})
{% endif %}
DO{% if data.do_instead in ['true', True] %}
{{ ' INSTEAD' }}
{{ ' INSTEAD' }}{% else %}{{ '' }}{% endif %}
{% if data.statements is defined and data.statements.strip() in ['', 'NOTHING'] %}
NOTHING;
{% elif data.statements is defined %}
({{ data.statements.rstrip(';') }});
{% else %}
{{ '' }}
{% endif %}
{% if data.statements %}
{{ data.statements.rstrip(';') }};
{% else %}
NOTHING;
NOTHING;
{% endif %}
{% if data.comment %}

View File

@ -3,6 +3,8 @@
SELECT
rw.oid AS oid,
rw.rulename AS name,
rw.ev_type,
rw.is_instead,
relname AS view,
CASE WHEN relkind = 'r' THEN TRUE ELSE FALSE END AS parentistable,
nspname AS schema,
@ -10,7 +12,7 @@ SELECT
{# ===== Check whether it is system rule or not ===== #}
CASE WHEN rw.rulename = '_RETURN' THEN True ELSE False END AS system_rule,
CASE WHEN rw.ev_enabled != 'D' THEN True ELSE False END AS enabled,
pg_get_ruledef(rw.oid, true) AS definition
pg_get_ruledef(rw.oid) AS definition
FROM
pg_rewrite rw
JOIN pg_class cl ON cl.oid=rw.ev_class

View File

@ -8,22 +8,24 @@
ALTER RULE {{ conn|qtIdent(o_data.name) }} ON {{ conn|qtIdent(o_data.schema, o_data.view) }} RENAME TO {{ conn|qtIdent(data.name) }};
{% endif %}
{% if data.event or data.do_instead is defined or data.condition or data.statements %}
{% if data.event or data.do_instead is defined or data.condition is defined or data.statements is defined %}
CREATE OR REPLACE RULE {{ conn|qtIdent(rule_name) }} AS
ON {% if data.event and data.event != o_data.event %}{{ data.event|upper }}{% else %}{{ o_data.event|upper }}{% endif %}
TO {{ conn|qtIdent(o_data.schema, o_data.view) }}
{% if data.condition and o_data.condition != data.condition %}
WHERE {{ data.condition }}
WHERE ({{ data.condition }})
{% elif data.condition is not defined and o_data.condition %}
WHERE {{ o_data.condition }}
WHERE ({{ o_data.condition }})
{% endif %}
DO {% if (('do_instead' not in data and o_data.do_instead in ['true', True]) or (data.do_instead in ['true', True])) %}{{ 'INSTEAD' }}{% endif %}
{% if data.statements and data.statements != o_data.statements %}
DO{% if (('do_instead' not in data and o_data.do_instead in ['true', True]) or (data.do_instead in ['true', True])) %}{{ ' INSTEAD' }}{% endif %}
{% if data.statements and data.statements != o_data.statements and data.statements.strip() in ['', 'NOTHING']%}
NOTHING;
{% elif data.statements and data.statements != o_data.statements %}
{{ data.statements.rstrip(';') }};
({{ data.statements.rstrip(';') }});
{% elif data.statements is not defined and o_data.statements %}
{{ o_data.statements.rstrip(';') }};
({{ o_data.statements.rstrip(';') }});
{% else %}
NOTHING;
{% endif %}

View File

@ -428,32 +428,43 @@ def parse_rule_definition(res):
res_data = res['rows'][0]
data_def = res_data['definition']
import re
# Parse data for event
e_match = re.search(r"ON\s+(.*)\s+TO", data_def)
event_data = e_match.group(1) if e_match is not None else None
event = event_data if event_data is not None else ''
# Parse data for do instead
inst_match = re.search(r"\s+(INSTEAD)\s+", data_def)
instead_data = inst_match.group(1) if inst_match is not None else None
instead = True if instead_data is not None else False
# Parse data for condition
condition_match = re.search(r"(?:WHERE)\s+([\s\S]*)\s+(?:DO)",
data_def)
condition_data = condition_match.group(1) \
if condition_match is not None else None
condition = condition_data if condition_data is not None else ''
condition = ''
condition_part_match = re.search(
r"((?:ON)\s+(?:[\s\S]+?)"
r"(?:TO)\s+(?:[\s\S]+?)(?:DO))", data_def)
if condition_part_match is not None:
condition_part = condition_part_match.group(1)
# Parse data for statements
condition_match = re.search(
r"(?:WHERE)\s+(\([\s\S]*\))\s+(?:DO)", condition_part)
if condition_match is not None:
condition = condition_match.group(1)
# also remove enclosing brackets
if condition.startswith('(') and condition.endswith(')'):
condition = condition[1:-1]
# Parse data for statements
statement_match = re.search(
r"(?:DO\s+)(?:INSTEAD\s+)?((.|\n)*)", data_def)
statement_data = statement_match.group(1) if statement_match else None
statement = statement_data if statement_data is not None else ''
r"(?:DO\s+)(?:INSTEAD\s+)?([\s\S]*)(?:;)", data_def)
statement = ''
if statement_match is not None:
statement = statement_match.group(1)
# also remove enclosing brackets
if statement.startswith('(') and statement.endswith(')'):
statement = statement[1:-1]
# set columns parse data
res_data['event'] = event.lower().capitalize()
res_data['do_instead'] = instead
res_data['event'] = {
'1': 'SELECT',
'2': 'UPDATE',
'3': 'INSERT',
'4': 'DELETE'
}[res_data['ev_type']]
res_data['do_instead'] = res_data['is_instead']
res_data['statements'] = statement
res_data['condition'] = condition
except Exception as e: