mirror of
https://github.com/FirebirdSQL/firebird-qa.git
synced 2025-01-22 21:43:06 +01:00
Added/Updated bugs\gh_6782_test.py. Checked on 3.0.8.33535, 4.0.1.2692, 5.0.0.509.
This commit is contained in:
parent
8760ded984
commit
3f31ec0739
@ -5,49 +5,144 @@ ID: issue-6782
|
|||||||
ISSUE: 6782
|
ISSUE: 6782
|
||||||
TITLE: Getting "records fetched" for functions/procedures in trace
|
TITLE: Getting "records fetched" for functions/procedures in trace
|
||||||
DESCRIPTION:
|
DESCRIPTION:
|
||||||
Confirmed bug on 4.0.0.2436: there was no lines with numner of:
|
Confirmed bug on 4.0.0.2436, there was no:
|
||||||
* number of fetched rows;
|
* lines with number of fetched rows;
|
||||||
* additional info about number of fetches/reads/writes/marks (after elapsed time).
|
* additional info about number of fetches/reads/writes/marks (after elapsed time).
|
||||||
|
|
||||||
In other words, trace log:
|
In other words, trace log:
|
||||||
* was before fix:
|
* was before fix:
|
||||||
Procedure <some_name>
|
Procedure <some_name>
|
||||||
0 ms
|
0 ms
|
||||||
* became after fix:
|
* became after fix:
|
||||||
Procedure <some_name>
|
Procedure <some_name>
|
||||||
5 records fetched <<< --- added
|
5 records fetched <<< --- added
|
||||||
0 ms, 10 fetch(es)
|
0 ms, 10 fetch(es)
|
||||||
^^^^^^^^^^^^ --- added
|
^^^^^^^^^^^^ --- added
|
||||||
|
|
||||||
Test parses trace log and search there lines with names of known procedures/functions and
|
Test parses trace log and search there lines with names of known procedures/functions and
|
||||||
then checks presence of lines with number of fetched records (for selectable procedures) and
|
then checks presence of lines with number of fetched records (for selectable procedures) and
|
||||||
additional statistics ('fetches/reads/writes/marks').
|
additional statistics ('fetches/reads/writes/marks').
|
||||||
|
|
||||||
Checked on:
|
|
||||||
5.0.0.87 SS: 7.231s.
|
|
||||||
5.0.0.85 CS: 6.425s.
|
|
||||||
4.0.1.2520 SS: 6.929s.
|
|
||||||
4.0.1.2519 CS: 9.452s.
|
|
||||||
3.0.8.33476 SS: 12.199s.
|
|
||||||
3.0.8.33476 CS: 14.090s.
|
|
||||||
NOTES:
|
NOTES:
|
||||||
[29.06.2021]
|
[30.06.2022] pzotov
|
||||||
Added delay for 1.1 second after ISQL finished its script and before we ask trace to stop.
|
Checked on 3.0.8.33535, 4.0.1.2692, 5.0.0.509.
|
||||||
This is the only way to make trace log be completed. DO NOT EVER remove this delay because fbsvcmgr
|
|
||||||
makes query to FB services only one time per SECOND.
|
|
||||||
|
|
||||||
See also reply from Vlad related to test for core-6469, privately, letter: 04-mar-2021 13:02.
|
|
||||||
FBTEST: bugs.gh_6782
|
FBTEST: bugs.gh_6782
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import locale
|
||||||
|
import re
|
||||||
import pytest
|
import pytest
|
||||||
from firebird.qa import *
|
from firebird.qa import *
|
||||||
|
|
||||||
db = db_factory()
|
init_script = '''
|
||||||
|
create table test(id int);
|
||||||
|
insert into test(id) select row_number()over() from rdb$types rows 5;
|
||||||
|
commit;
|
||||||
|
|
||||||
|
set term ^;
|
||||||
|
create procedure standalone_selectable_sp returns(id int) as
|
||||||
|
begin
|
||||||
|
for select id from test as cursor c
|
||||||
|
do begin
|
||||||
|
update test set id = -id * (select count(*) from rdb$database)
|
||||||
|
where current of c;
|
||||||
|
suspend;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
^
|
||||||
|
|
||||||
|
create procedure standalone_nonselected_sp as
|
||||||
|
begin
|
||||||
|
for select id from test as cursor c
|
||||||
|
do begin
|
||||||
|
update test set id = -id * (select count(*) from rdb$database)
|
||||||
|
where current of c;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
^
|
||||||
|
|
||||||
|
create function standalone_func returns int as
|
||||||
|
begin
|
||||||
|
update test set id = rand()*10000000;
|
||||||
|
return (select max(id) from test);
|
||||||
|
end
|
||||||
|
^
|
||||||
|
|
||||||
|
create package pg_test as
|
||||||
|
begin
|
||||||
|
procedure packaged_selectable_sp returns(id int);
|
||||||
|
function packaged_func returns int;
|
||||||
|
procedure packaged_nonselected_sp;
|
||||||
|
end
|
||||||
|
^
|
||||||
|
|
||||||
|
create package body pg_test as
|
||||||
|
begin
|
||||||
|
procedure packaged_selectable_sp returns(id int) as
|
||||||
|
begin
|
||||||
|
for select id from test as cursor c
|
||||||
|
do begin
|
||||||
|
update test set id = -id * (select count(*) from rdb$database)
|
||||||
|
where current of c;
|
||||||
|
suspend;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
procedure packaged_nonselected_sp as
|
||||||
|
begin
|
||||||
|
for select id from test as cursor c
|
||||||
|
do begin
|
||||||
|
update test set id = -id * (select count(*) from rdb$database)
|
||||||
|
where current of c;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function packaged_func returns int as
|
||||||
|
begin
|
||||||
|
update test set id = rand()*10000000;
|
||||||
|
return (select min(id) from test);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
^
|
||||||
|
|
||||||
|
|
||||||
|
create procedure sp_main as
|
||||||
|
declare c int;
|
||||||
|
begin
|
||||||
|
for select id from standalone_selectable_sp into c do
|
||||||
|
begin
|
||||||
|
-- nop --
|
||||||
|
end
|
||||||
|
----------------------
|
||||||
|
c = standalone_func();
|
||||||
|
----------------------
|
||||||
|
execute procedure standalone_nonselected_sp;
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
for select id from pg_test.packaged_selectable_sp into c do
|
||||||
|
begin
|
||||||
|
-- nop --
|
||||||
|
end
|
||||||
|
----------------------
|
||||||
|
c = pg_test.packaged_func();
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
execute procedure pg_test.packaged_nonselected_sp;
|
||||||
|
end
|
||||||
|
^
|
||||||
|
set term ;^
|
||||||
|
commit;
|
||||||
|
|
||||||
|
--set list on;
|
||||||
|
--execute procedure sp_main;
|
||||||
|
--commit;
|
||||||
|
'''
|
||||||
|
|
||||||
|
db = db_factory(init=init_script)
|
||||||
|
|
||||||
act = python_act('db')
|
act = python_act('db')
|
||||||
|
|
||||||
expected_stdout = """
|
expected_stdout_trace = """
|
||||||
Procedure STANDALONE_SELECTABLE_SP:
|
Procedure STANDALONE_SELECTABLE_SP:
|
||||||
FOUND line with number of fetched records
|
FOUND line with number of fetched records
|
||||||
FOUND line with execution statistics
|
FOUND line with execution statistics
|
||||||
@ -72,279 +167,44 @@ expected_stdout = """
|
|||||||
FOUND line with execution statistics
|
FOUND line with execution statistics
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@pytest.mark.skip('FIXME: Not IMPLEMENTED')
|
|
||||||
@pytest.mark.version('>=3.0.8')
|
@pytest.mark.version('>=3.0.8')
|
||||||
def test_1(act: Action):
|
def test_1(act: Action, capsys):
|
||||||
pytest.fail("Not IMPLEMENTED")
|
|
||||||
|
|
||||||
# test_script_1
|
trace_cfg_items = [
|
||||||
#---
|
'time_threshold = 0',
|
||||||
# import os
|
'log_errors = true',
|
||||||
# import re
|
'log_procedure_finish = true',
|
||||||
# import subprocess
|
'log_function_finish = true',
|
||||||
# from subprocess import Popen
|
]
|
||||||
# import time
|
|
||||||
#
|
|
||||||
# os.environ["ISC_USER"] = user_name
|
sql_run='''
|
||||||
# os.environ["ISC_PASSWORD"] = user_password
|
set list on;
|
||||||
# db_conn.close()
|
execute procedure sp_main;
|
||||||
#
|
'''
|
||||||
# #--------------------------------------------
|
|
||||||
#
|
with act.trace(db_events = trace_cfg_items, encoding=locale.getpreferredencoding()):
|
||||||
# def flush_and_close( file_handle ):
|
act.isql(input = sql_run, combine_output = True)
|
||||||
# # https://docs.python.org/2/library/os.html#os.fsync
|
|
||||||
# # If you're starting with a Python file object f,
|
allowed_patterns = \
|
||||||
# # first do f.flush(), and
|
(
|
||||||
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
|
r'Procedure\s+(STANDALONE_SELECTABLE_SP:|STANDALONE_NONSELECTED_SP:|PG_TEST.PACKAGED_SELECTABLE_SP:|PG_TEST.PACKAGED_NONSELECTED_SP:|SP_MAIN:)'
|
||||||
# global os
|
,r'Function\s+(STANDALONE_FUNC:|PG_TEST.PACKAGED_FUNC:)'
|
||||||
#
|
,r'\d+\s+record(s|\(s\))?\s+fetched'
|
||||||
# file_handle.flush()
|
,r'\s+\d+\s+ms(,)?\s+\d+\s+fetch(es|\(es\))((,)?\s+\d+\s+read(s|\(s\)))?((,)?\s+\d+\s+write(s|\(s\)))?(,)?\s+\d+\s+mark(s|\(s\))'
|
||||||
# if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull:
|
)
|
||||||
# # otherwise: "OSError: [Errno 9] Bad file descriptor"!
|
allowed_patterns = [ re.compile(p, re.IGNORECASE) for p in allowed_patterns ]
|
||||||
# os.fsync(file_handle.fileno())
|
|
||||||
# file_handle.close()
|
for line in act.trace_log:
|
||||||
#
|
if line.split():
|
||||||
# #--------------------------------------------
|
if act.match_any(line, allowed_patterns):
|
||||||
#
|
if ' ms' in line and 'fetch' in line:
|
||||||
# def cleanup( f_names_list ):
|
print('FOUND line with execution statistics')
|
||||||
# global os
|
elif 'record' in line and 'fetch' in line:
|
||||||
# for i in range(len( f_names_list )):
|
print('FOUND line with number of fetched records')
|
||||||
# if type(f_names_list[i]) == file:
|
else:
|
||||||
# del_name = f_names_list[i].name
|
print(line)
|
||||||
# elif type(f_names_list[i]) == str:
|
|
||||||
# del_name = f_names_list[i]
|
act.expected_stdout = expected_stdout_trace
|
||||||
# else:
|
act.stdout = capsys.readouterr().out
|
||||||
# print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.')
|
assert act.clean_stdout == act.clean_expected_stdout
|
||||||
# print('type(f_names_list[i])=',type(f_names_list[i]))
|
|
||||||
# del_name = None
|
|
||||||
#
|
|
||||||
# if del_name and os.path.isfile( del_name ):
|
|
||||||
# os.remove( del_name )
|
|
||||||
#
|
|
||||||
# #--------------------------------------------
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# # Prepare config for trace session that will be launched by call of FBSVCMGR:
|
|
||||||
# ################
|
|
||||||
# txt = '''database= %[\\\\\\\\/]bugs.gh_6782.fdb
|
|
||||||
# {
|
|
||||||
# enabled = true
|
|
||||||
# time_threshold = 0
|
|
||||||
# log_initfini = false
|
|
||||||
# log_errors = true
|
|
||||||
# log_procedure_finish = true
|
|
||||||
# log_function_finish = true
|
|
||||||
# }
|
|
||||||
# '''
|
|
||||||
# trc_cfg=open( os.path.join(context['temp_directory'],'tmp_trace_6782.cfg'), 'w')
|
|
||||||
# trc_cfg.write(txt)
|
|
||||||
# flush_and_close( trc_cfg )
|
|
||||||
#
|
|
||||||
# #####################################################################
|
|
||||||
# # Async. launch of trace session using FBSVCMGR action_trace_start:
|
|
||||||
#
|
|
||||||
# trc_log = open( os.path.join(context['temp_directory'],'tmp_trace_6782.log'), 'w')
|
|
||||||
#
|
|
||||||
# # Execute a child program in a new process, redirecting STDERR to the same target as of STDOUT:
|
|
||||||
# p_trace = Popen( [context['fbsvcmgr_path'], "localhost:service_mgr",
|
|
||||||
# "action_trace_start","trc_cfg", trc_cfg.name],
|
|
||||||
# stdout=trc_log, stderr=subprocess.STDOUT)
|
|
||||||
#
|
|
||||||
# # Wait! Trace session is initialized not instantly!
|
|
||||||
# time.sleep(1)
|
|
||||||
#
|
|
||||||
# #####################################################################
|
|
||||||
#
|
|
||||||
# # Determine active trace session ID (for further stop):
|
|
||||||
# trc_lst = open( os.path.join(context['temp_directory'],'tmp_trace_6782.lst'), 'w')
|
|
||||||
# subprocess.call([context['fbsvcmgr_path'], "localhost:service_mgr",
|
|
||||||
# "action_trace_list"],
|
|
||||||
# stdout=trc_lst, stderr=subprocess.STDOUT
|
|
||||||
# )
|
|
||||||
# flush_and_close( trc_lst )
|
|
||||||
#
|
|
||||||
# # Session ID: 5
|
|
||||||
# # ...
|
|
||||||
# trcssn = None
|
|
||||||
# with open( trc_lst.name,'r') as f:
|
|
||||||
# for line in f:
|
|
||||||
# if 'Session ID' in line and len(line.split())>=3:
|
|
||||||
# trcssn = line.split()[2]
|
|
||||||
# break
|
|
||||||
#
|
|
||||||
# # Result: `trcssn` is ID of active trace session.
|
|
||||||
# if not trcssn:
|
|
||||||
# print("Error parsing trace session ID.")
|
|
||||||
# flush_and_close( trc_log )
|
|
||||||
#
|
|
||||||
# else:
|
|
||||||
# #####################################################################
|
|
||||||
#
|
|
||||||
# # Preparing script for ISQL:
|
|
||||||
#
|
|
||||||
# sql_cmd='''
|
|
||||||
# create table test(id int);
|
|
||||||
# insert into test(id) select row_number()over() from rdb$types rows 5;
|
|
||||||
# commit;
|
|
||||||
# -- set echo on;
|
|
||||||
# set term ^;
|
|
||||||
# create procedure standalone_selectable_sp returns(id int) as
|
|
||||||
# begin
|
|
||||||
# for select id from test as cursor c
|
|
||||||
# do begin
|
|
||||||
# update test set id = -id * (select count(*) from rdb$database)
|
|
||||||
# where current of c;
|
|
||||||
# suspend;
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# ^
|
|
||||||
#
|
|
||||||
# create procedure standalone_nonselected_sp as
|
|
||||||
# begin
|
|
||||||
# for select id from test as cursor c
|
|
||||||
# do begin
|
|
||||||
# update test set id = -id * (select count(*) from rdb$database)
|
|
||||||
# where current of c;
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# ^
|
|
||||||
#
|
|
||||||
# create function standalone_func returns int as
|
|
||||||
# begin
|
|
||||||
# update test set id = rand()*10000000;
|
|
||||||
# return (select max(id) from test);
|
|
||||||
# end
|
|
||||||
# ^
|
|
||||||
#
|
|
||||||
# create package pg_test as
|
|
||||||
# begin
|
|
||||||
# procedure packaged_selectable_sp returns(id int);
|
|
||||||
# function packaged_func returns int;
|
|
||||||
# procedure packaged_nonselected_sp;
|
|
||||||
# end
|
|
||||||
# ^
|
|
||||||
#
|
|
||||||
# create package body pg_test as
|
|
||||||
# begin
|
|
||||||
# procedure packaged_selectable_sp returns(id int) as
|
|
||||||
# begin
|
|
||||||
# for select id from test as cursor c
|
|
||||||
# do begin
|
|
||||||
# update test set id = -id * (select count(*) from rdb$database)
|
|
||||||
# where current of c;
|
|
||||||
# suspend;
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# procedure packaged_nonselected_sp as
|
|
||||||
# begin
|
|
||||||
# for select id from test as cursor c
|
|
||||||
# do begin
|
|
||||||
# update test set id = -id * (select count(*) from rdb$database)
|
|
||||||
# where current of c;
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# function packaged_func returns int as
|
|
||||||
# begin
|
|
||||||
# update test set id = rand()*10000000;
|
|
||||||
# return (select min(id) from test);
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# ^
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# create procedure sp_main as
|
|
||||||
# declare c int;
|
|
||||||
# begin
|
|
||||||
# for select id from standalone_selectable_sp into c do
|
|
||||||
# begin
|
|
||||||
# -- nop --
|
|
||||||
# end
|
|
||||||
# ----------------------
|
|
||||||
# c = standalone_func();
|
|
||||||
# ----------------------
|
|
||||||
# execute procedure standalone_nonselected_sp;
|
|
||||||
# ----------------------
|
|
||||||
#
|
|
||||||
# for select id from pg_test.packaged_selectable_sp into c do
|
|
||||||
# begin
|
|
||||||
# -- nop --
|
|
||||||
# end
|
|
||||||
# ----------------------
|
|
||||||
# c = pg_test.packaged_func();
|
|
||||||
# ----------------------
|
|
||||||
#
|
|
||||||
# execute procedure pg_test.packaged_nonselected_sp;
|
|
||||||
# end
|
|
||||||
# ^
|
|
||||||
# set term ;^
|
|
||||||
# commit;
|
|
||||||
#
|
|
||||||
# set list on;
|
|
||||||
# execute procedure sp_main;
|
|
||||||
# commit;
|
|
||||||
# '''
|
|
||||||
#
|
|
||||||
# isql_cmd=open( os.path.join(context['temp_directory'],'tmp_isql_6782.sql'), 'w')
|
|
||||||
# isql_cmd.write(sql_cmd)
|
|
||||||
# flush_and_close( isql_cmd )
|
|
||||||
#
|
|
||||||
# #######################################################################
|
|
||||||
# isql_log=open( os.path.join(context['temp_directory'],'tmp_isql_6782.log'), 'w')
|
|
||||||
# subprocess.call( [ context['isql_path'], dsn, "-i", isql_cmd.name ], stdout=isql_log,stderr=subprocess.STDOUT)
|
|
||||||
# flush_and_close( isql_log )
|
|
||||||
#
|
|
||||||
# #########################
|
|
||||||
# ### C R U C I A L ###
|
|
||||||
# #########################
|
|
||||||
# # We must stay idle here at least 1.1 second before ask fbsbcmgr to stop trace.
|
|
||||||
# # The reason is that fbsvcmgr make query to FB services only one time per second.
|
|
||||||
# # If we do not take delay here then test can fail with (most often) empty log
|
|
||||||
# # or its log can look incompleted.
|
|
||||||
# # See letter from Vlad, 04-mar-2021 13:02 (subj: "Test core_6469 on Linux...").
|
|
||||||
# ###############
|
|
||||||
# time.sleep(1.1)
|
|
||||||
# ###############
|
|
||||||
#
|
|
||||||
# # Stop trace session. We have to do this BEFORE we termitane process with PID = p_trace.
|
|
||||||
# # NOTE ONCE AGAIN: we can do this only after delay for at least 1.1 second!
|
|
||||||
# trc_lst=open(trc_lst.name, "a")
|
|
||||||
# trc_lst.seek(0,2)
|
|
||||||
# subprocess.call([ context['fbsvcmgr_path'], "localhost:service_mgr",
|
|
||||||
# "action_trace_stop","trc_id",trcssn],
|
|
||||||
# stdout=trc_lst, stderr=subprocess.STDOUT
|
|
||||||
# )
|
|
||||||
# flush_and_close( trc_lst )
|
|
||||||
#
|
|
||||||
# ###########################
|
|
||||||
# p_trace.terminate()
|
|
||||||
# flush_and_close( trc_log )
|
|
||||||
# ###########################
|
|
||||||
#
|
|
||||||
# # Check logs:
|
|
||||||
# #############
|
|
||||||
# allowed_patterns = (
|
|
||||||
# re.compile('Procedure\\s+(STANDALONE_SELECTABLE_SP:|STANDALONE_NONSELECTED_SP:|PG_TEST.PACKAGED_SELECTABLE_SP:|PG_TEST.PACKAGED_NONSELECTED_SP:|SP_MAIN:)', re.IGNORECASE)
|
|
||||||
# ,re.compile('Function\\s+(STANDALONE_FUNC:|PG_TEST.PACKAGED_FUNC:)', re.IGNORECASE)
|
|
||||||
# ,re.compile('\\d+\\s+record(s|\\(s\\))?\\s+fetched', re.IGNORECASE)
|
|
||||||
# ,re.compile('\\s+\\d+\\s+ms(,)?\\s+\\d+\\s+fetch(es|\\(es\\))((,)?\\s+\\d+\\s+read(s|\\(s\\)))?((,)?\\s+\\d+\\s+write(s|\\(s\\)))?(,)?\\s+\\d+\\s+mark(s|\\(s\\))', re.IGNORECASE)
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# with open(trc_log.name) as f:
|
|
||||||
# for line in f:
|
|
||||||
# if line.split():
|
|
||||||
# match2some = filter( None, [ p.search(line) for p in allowed_patterns ] )
|
|
||||||
# if match2some:
|
|
||||||
# if ' ms' in line and 'fetch' in line:
|
|
||||||
# print('FOUND line with execution statistics')
|
|
||||||
# elif 'record' in line and 'fetch' in line:
|
|
||||||
# print('FOUND line with number of fetched records')
|
|
||||||
# else:
|
|
||||||
# print(line)
|
|
||||||
#
|
|
||||||
# # Cleanup:
|
|
||||||
# time.sleep(1)
|
|
||||||
# cleanup( (trc_lst, trc_cfg, trc_log, isql_cmd, isql_log) )
|
|
||||||
#
|
|
||||||
#---
|
|
||||||
|
Loading…
Reference in New Issue
Block a user