6
0
mirror of https://github.com/FirebirdSQL/firebird-qa.git synced 2025-01-22 13:33:07 +01:00
firebird-qa/tests/bugs/core_6403_test.py

198 lines
7.0 KiB
Python

#coding:utf-8
"""
ID: issue-6641
ISSUE: 6641
TITLE: Some PSQL statements may lead to exceptions report wrong line/column
DESCRIPTION:
We make list of strings, each of them is a multi-line statement starting with 'EXECUTE BLOCK ...'.
Inside every EB one of following loops is checked:
* while (<expr>) do ...
* for select <expr> into ...
-- where <expr> uses several arguments (because of loop) and eventually fails with zero division.
In the BEGIN ... END block we have several lines of code which operates with arguments (see ticket examples).
When <expr> fails then we catch error stack and parse it by searching line '- At block line: N, col: C'.
These values (N and C) are compared later with those which are expected.
In order to proper evaluate expected values of (N,C) we split source expression onto separate lines and numerate them.
Also, within each line we search special marker: "/*!*/" (without qoutes).
If line contains this marker then we find its position and subtract 5 from it (this is length of "while" word).
NB: this requires that between "for" and "/*!*/" we must put two space characters (see below).
Finally, we compare values in two dictionaries:
source_expr_positions_map - K = sequential number of exprerssion in the "main list"; V = line/col of token that causes problem;
actual_stack_positions_map - K = the same as in source_expr_positions_map; V = line/col values that were parsed from error stack.
All pairs for apropriate expressions must be equal.
If some difference will be found then we show this error stack, expected line/col coordinates and actual ones, as in this example:
==================
3 UNEXPECTED MISMATCH between expected and actual values of line/col:
* expected (position of problematic token): 6, 5
* actual (result of error message parsing): 10, 9
* source message of error:
Error while executing SQL statement:
- SQLCODE: -802
- arithmetic exception, numeric overflow, or string truncation
- Integer divide by zero. The code attempted to divide an integer value by an integer divisor of zero.
- At block line: 10, col: 9
Expression:
....:....1....:....2....:....3....:....4....:....5....:....6....:....7....:....8
: 12345678901234567890123456789012345678901234567890123456789012345678901234567890
1 : execute block
2 : as
3 : declare n int = 5;
4 : declare c int;
5 : begin
6 : for /*!*/ select 100/:n from rdb$types into c
7 : -- ^^ NB: two spaces here.
8 : do begin
9 : insert into test(x) values(sqrt(:n));
10 : n = n - 1;
11 : end
12 : end
==================
JIRA: CORE-6403
FBTEST: bugs.core_6403
"""
import pytest
import re
from firebird.qa import *
db = db_factory(init='recreate table test (x int) ;')
act = python_act('db')
expected_stdout = """
Test 0 PASSED
Test 1 PASSED
Test 2 PASSED
Test 3 PASSED
Test 4 PASSED
"""
sttm_list = [
"""execute block
as
declare n integer = 0;
begin
while/*!*/ (1 / n > 0)
do
begin
n = n - 1;
end
end
""",
"""execute
block as declare n integer = 1; begin
while/*!*/
(
1 / n > 0
)
do
begin
n
= n - 1;
end
end
""",
"""execute block
as
begin
while/*!*/ ( (select coalesce(t.x,0) from rdb$database r left join test t on 1=1) <= 1 )
do
begin
insert into test(x) values(1);
insert into test(x) values(2);
insert into test(x) values(3);
end
end
""",
"""execute block
as
declare n int = 5;
declare c int;
begin
for /*!*/ select 100/:n from rdb$types into c
-- ^^ NB: two spaces here.
do begin
insert into test(x) values(sqrt(:n));
n = n - 1;
end
end
""",
"""execute block
as
declare n int = 5;
declare c int;
begin
delete from test;
while ( n >= 0 ) do
begin
for /*!*/ execute statement (
'select 100 / cast(? as int)
from rdb$database
'
) ( n ) into c
-- ^^ NB: two spaces here.
do begin
-- insert into test(x) values(sqrt(:n));
insert into test(x) values( :n );
end
n = n - 1;
end
end
"""
]
@pytest.mark.version('>=4.0')
def test_1(act: Action, capsys):
sttm_map = {}
source_expr_positions_map = {}
for i, stm in enumerate(sttm_list):
sttm_lines = stm.splitlines()
for j, line in enumerate(sttm_lines):
problematic_expr_position = line.find('/*!*/') - 5
sttm_map[i, j+1] = (line, 1 + problematic_expr_position)
if problematic_expr_position >= 0:
source_expr_positions_map[i] = (j+1, 1 + problematic_expr_position)
# - At block line: 3, col: 1
pattern = re.compile('(-)?At\\s+block\\s+line(:)?\\s+\\d+(,)?\\s+col(:)?\\s+\\d+', re.IGNORECASE)
actual_stack_positions_map = {}
with act.db.connect() as con:
for i, cmd in enumerate(sttm_list):
try:
con.execute_immediate(cmd)
except Exception as e:
for msg in e.args[0].splitlines():
if pattern.match(msg):
# '- At block line: 3, col: 1' ==> ' 3 1'
line_col_pair = ''.join([s if s.isdigit() else ' ' for s in msg]).split()
actual_stack_positions_map[i] = (int(line_col_pair[0]), int(line_col_pair[1]), e.args[0])
#
for k, v in sorted(source_expr_positions_map.items()):
w = actual_stack_positions_map.get(k)
if w and v[:2] == w[:2]:
print(f'Test {k} PASSED') # expected line/col in source code matches error stack pair.
else:
print(f'{k} UNEXPECTED MISMATCH between expected and actual values of line/col:')
print(f' * expected (position of problematic token): {v[0]}, {v[1]}')
if w:
print(f' * actual (result of error message parsing): {w[0]}, {w[1]}')
print(' * source message of error:')
for i in w[2].splitlines():
print(' ' * 8, i)
else:
print(' * actual: NONE')
print('Expression:')
for i, s in enumerate(sttm_list[k].splitlines()):
if i == 0:
print(f'| {"".join(["....:...." + str(j) for j in range(1, 9)])}')
print(f'| : {"1234567890" * 8}')
print(f'|{"{:4g}".format(i+1)}: {s}')
# Check
act.expected_stdout = expected_stdout
act.stdout = capsys.readouterr().out
assert act.clean_stdout == act.clean_expected_stdout