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

More python tests

This commit is contained in:
Pavel Císař 2021-11-26 19:20:43 +01:00
parent 93a898b9ee
commit c66b267c73
54 changed files with 3573 additions and 2056 deletions

BIN
files/core_5078.zip Normal file

Binary file not shown.

View File

@ -352,11 +352,9 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
expected_stdout_1 = """ expected_stdout_1 = """
RESULT_OF_REQ_COMPARE_TO_ACTUAL EXPECTED: actual values were equal to required. RESULT_OF_REQ_COMPARE_TO_ACTUAL EXPECTED: actual values were equal to required.
""" """
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail
def test_1(db_1): def test_1(db_1):
pytest.fail("Test not IMPLEMENTED") pytest.skip("Test requires manipulation with firebird.conf")
#pytest.fail("Test not IMPLEMENTED")

View File

@ -136,9 +136,8 @@ expected_stdout_1 = """
user_1 = user_factory(name='TMP$C1972', password='123') user_1 = user_factory(name='TMP$C1972', password='123')
@pytest.mark.version('>=2.1.1') @pytest.mark.version('>=2.1.1')
@pytest.mark.xfail
def test_1(act_1: Action, user_1: User): def test_1(act_1: Action, user_1: User):
pytest.fail("Test not IMPLEMENTED") pytest.skip("Test can't be implement with new Python driver")
# This test is not possible to implement with new Python driver as it does not # This test is not possible to implement with new Python driver as it does not
# allow to specify `forced_writes` or `reserve_space` options on connect() as tested # allow to specify `forced_writes` or `reserve_space` options on connect() as tested
# configuration options are passed to DPB only for create_database()! # configuration options are passed to DPB only for create_database()!

View File

@ -201,8 +201,6 @@ Expected line found.
""" """
@pytest.mark.version('>=2.5.2') @pytest.mark.version('>=2.5.2')
@pytest.mark.xfail
def test_1(db_1): def test_1(db_1):
pytest.fail("Test not IMPLEMENTED") pytest.skip("Implementation is complicated, and IMHO not worth of realization")
#pytest.fail("Test not IMPLEMENTED")

View File

@ -252,8 +252,7 @@ def test_1(act_1: Action):
c = con.cursor() c = con.cursor()
c.execute('update test set x = -x') c.execute('update test set x = -x')
con.commit() con.commit()
act_1.svcmgr(switches=['localhost:service_mgr', 'user', act_1.db.user, act_1.svcmgr(switches=['action_db_stats', 'dbname',
'password', act_1.db.password, 'action_db_stats', 'dbname',
str(act_1.db.db_path), 'sts_record_versions']) str(act_1.db.db_path), 'sts_record_versions'])
act_1.stdout = '\n'.join([line for line in act_1.stdout.splitlines() if 'versions:' in line.lower()]) act_1.stdout = '\n'.join([line for line in act_1.stdout.splitlines() if 'versions:' in line.lower()])
act_1.expected_stdout = expected_stdout_1 act_1.expected_stdout = expected_stdout_1

View File

@ -2,33 +2,38 @@
# #
# id: bugs.core_4345 # id: bugs.core_4345
# title: Ability to trace stored functions execution # title: Ability to trace stored functions execution
# decription: # decription:
# Test checks two cases: 1) when execution of function is ENABLED and 2) DISABLED. # Test checks two cases: 1) when execution of function is ENABLED and 2) DISABLED.
# In 1st case we search in trace log rows which prove that function execution was actually logged, # In 1st case we search in trace log rows which prove that function execution was actually logged,
# and in 2nd case we have to ensure that trace log does NOT contain text about this event. # and in 2nd case we have to ensure that trace log does NOT contain text about this event.
# Both standalone and packaged functions are checked. # Both standalone and packaged functions are checked.
# #
# Checked on WI-V3.0.0.32328 (SS/SC/CS); WI-T4.0.0.633 # Checked on WI-V3.0.0.32328 (SS/SC/CS); WI-T4.0.0.633
# #
# 08-may-2017. # 08-may-2017.
# Refactored: additional filtering using regexp (pattern.search(line)) in order to avoid take in account # Refactored: additional filtering using regexp (pattern.search(line)) in order to avoid take in account
# start transaction events in trace (number of starting Tx became differ since 29-mar-2017 when some changes # start transaction events in trace (number of starting Tx became differ since 29-mar-2017 when some changes
# in PIPE mechanism were done, see: # in PIPE mechanism were done, see:
# https://github.com/FirebirdSQL/firebird/commit/e1232d8015b199e33391dd2550e7c5f7e3f08493 ) # https://github.com/FirebirdSQL/firebird/commit/e1232d8015b199e33391dd2550e7c5f7e3f08493 )
# #
# #
# tracker_id: CORE-4345 # tracker_id: CORE-4345
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action import time
import re
from threading import Thread, Barrier
from firebird.qa import db_factory, python_act, Action
# version: 3.0 # version: 3.0
# resources: None # resources: None
substitutions_1 = [('^((?!PARAM0|EXECUTE_FUNCTION_START|EXECUTE_FUNCTION_FINISH|SA_FUNC|PG_FUNC).)*$', ''), ('LOG_FUNC_ENABLED.*EXECUTE_FUNCTION_START', 'LOG_FUNC_ENABLED EXECUTE_FUNCTION_START'), ('LOG_FUNC_ENABLED.*EXECUTE_FUNCTION_FINISH', 'LOG_FUNC_ENABLED EXECUTE_FUNCTION_FINISH')] substitutions_1 = [('^((?!PARAM0|EXECUTE_FUNCTION_START|EXECUTE_FUNCTION_FINISH|SA_FUNC|PG_FUNC).)*$', ''),
('LOG_FUNC_ENABLED.*EXECUTE_FUNCTION_START', 'LOG_FUNC_ENABLED EXECUTE_FUNCTION_START'),
('LOG_FUNC_ENABLED.*EXECUTE_FUNCTION_FINISH', 'LOG_FUNC_ENABLED EXECUTE_FUNCTION_FINISH')]
init_script_1 = """ init_script_1 = """
set term ^; set term ^;
@ -64,41 +69,41 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# from subprocess import Popen # from subprocess import Popen
# import shutil # import shutil
# import re # import re
# #
# trace_timestamp_prefix='[.*\\s+]*20[0-9]{2}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3,4}\\s+\\(.+\\)' # trace_timestamp_prefix='[.*\\s+]*20[0-9]{2}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3,4}\\s+\\(.+\\)'
# func_start_ptn=re.compile( trace_timestamp_prefix + '\\s+(FAILED){0,1}\\s*EXECUTE_FUNCTION_START$', re.IGNORECASE) # func_start_ptn=re.compile( trace_timestamp_prefix + '\\s+(FAILED){0,1}\\s*EXECUTE_FUNCTION_START$', re.IGNORECASE)
# func_finish_ptn=re.compile( trace_timestamp_prefix + '\\s+(FAILED){0,1}\\s*EXECUTE_FUNCTION_FINISH$', re.IGNORECASE) # func_finish_ptn=re.compile( trace_timestamp_prefix + '\\s+(FAILED){0,1}\\s*EXECUTE_FUNCTION_FINISH$', re.IGNORECASE)
# func_name_ptn=re.compile('Function\\s+(SA_FUNC|PG_TEST.PG_FUNC):$') # func_name_ptn=re.compile('Function\\s+(SA_FUNC|PG_TEST.PG_FUNC):$')
# func_param_prn=re.compile('param[0-9]+\\s+=\\s+', re.IGNORECASE) # func_param_prn=re.compile('param[0-9]+\\s+=\\s+', re.IGNORECASE)
# #
# #
# db_conn.close() # db_conn.close()
# #
# # Minimal delay after we issue command fbsvcmgr action_trace_start # # Minimal delay after we issue command fbsvcmgr action_trace_start
# # and before we launch execution of checked code # # and before we launch execution of checked code
# ########################################### # ###########################################
# min_delay_after_trace_start = 1 # min_delay_after_trace_start = 1
# #
# # Minimal delay after we finish connection to database # # Minimal delay after we finish connection to database
# # and before issuing command to stop trace # # and before issuing command to stop trace
# ########################################## # ##########################################
# min_delay_before_trace_stop = 1 # min_delay_before_trace_stop = 1
# #
# # Minimal delay for trace log be flushed on disk after # # Minimal delay for trace log be flushed on disk after
# # we issue command 'fbsvcmgr action_trace_stop': # # we issue command 'fbsvcmgr action_trace_stop':
# ############################### # ###############################
# min_delay_after_trace_stop = 1 # min_delay_after_trace_stop = 1
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# def make_trace_config( is_func_logged, trccfg_name ): # def make_trace_config( is_func_logged, trccfg_name ):
# #
# # NOTES ABOUT TRACE CONFIG FOR 3.0: # # NOTES ABOUT TRACE CONFIG FOR 3.0:
# # 1) Header contains `database` clause in different format vs FB 2.5: its data must be enclosed with '{' '}' # # 1) Header contains `database` clause in different format vs FB 2.5: its data must be enclosed with '{' '}'
# # 2) Name and value must be separated by EQUALITY sign ('=') in FB-3 trace.conf, otherwise we get runtime error: # # 2) Name and value must be separated by EQUALITY sign ('=') in FB-3 trace.conf, otherwise we get runtime error:
# # element "<. . .>" have no attribute value set # # element "<. . .>" have no attribute value set
# #
# if is_func_logged.upper() == 'TRUE': # if is_func_logged.upper() == 'TRUE':
# txt30 = ''' # txt30 = '''
# database=%[\\\\\\\\/]bugs.core_4345.fdb # database=%[\\\\\\\\/]bugs.core_4345.fdb
@ -122,26 +127,26 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# log_function_start = false # log_function_start = false
# } # }
# ''' # '''
# #
# trccfg=open( trccfg_name, 'w') # trccfg=open( trccfg_name, 'w')
# trccfg.write(txt30) # trccfg.write(txt30)
# trccfg.close() # trccfg.close()
# #
# return # return
# #
# def stop_trace_session(): # def stop_trace_session():
# #
# # Save active trace session info into file for further parsing it and obtain session_id back (for stop): # # Save active trace session info into file for further parsing it and obtain session_id back (for stop):
# import os # import os
# import subprocess # import subprocess
# #
# f_trclst=open( os.path.join(context['temp_directory'],'tmp_trace_4345.lst'), 'w') # f_trclst=open( os.path.join(context['temp_directory'],'tmp_trace_4345.lst'), 'w')
# subprocess.call([context['fbsvcmgr_path'], "localhost:service_mgr", "action_trace_list"], # subprocess.call([context['fbsvcmgr_path'], "localhost:service_mgr", "action_trace_list"],
# stdout=f_trclst, # stdout=f_trclst,
# stderr=subprocess.STDOUT # stderr=subprocess.STDOUT
# ) # )
# f_trclst.close() # f_trclst.close()
# #
# trcssn=0 # trcssn=0
# with open( f_trclst.name,'r') as f: # with open( f_trclst.name,'r') as f:
# for line in f: # for line in f:
@ -149,7 +154,7 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# trcssn=line.split()[2] # trcssn=line.split()[2]
# break # break
# f.close() # f.close()
# #
# # Result: `trcssn` is ID of active trace session. Now we have to terminate it: # # Result: `trcssn` is ID of active trace session. Now we have to terminate it:
# f_trclst=open(f_trclst.name,'a') # f_trclst=open(f_trclst.name,'a')
# f_trclst.seek(0,2) # f_trclst.seek(0,2)
@ -157,12 +162,12 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# stdout=f_trclst, stderr=subprocess.STDOUT # stdout=f_trclst, stderr=subprocess.STDOUT
# ) # )
# f_trclst.close() # f_trclst.close()
# #
# os.remove(f_trclst.name) # os.remove(f_trclst.name)
# #
# return # return
# #
# #
# sql_fnc=''' # sql_fnc='''
# set list on; # set list on;
# set term ^; # set term ^;
@ -179,25 +184,25 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# commit; # commit;
# ''' # '''
# # % (123, 456) # # % (123, 456)
# #
# #
# ############################################################################## # ##############################################################################
# ### ### # ### ###
# ### C A S E - 1: l o g _ f u n c = t r u e ### # ### C A S E - 1: l o g _ f u n c = t r u e ###
# ### ### # ### ###
# ############################################################################## # ##############################################################################
# #
# # Make trace config with ENABLING logging of func. execution: # # Make trace config with ENABLING logging of func. execution:
# #
# trccfg_log_enable=os.path.join(context['temp_directory'],'tmp_trace_4345_log_enable.cfg') # trccfg_log_enable=os.path.join(context['temp_directory'],'tmp_trace_4345_log_enable.cfg')
# #
# make_trace_config( 'true', trccfg_log_enable ) # make_trace_config( 'true', trccfg_log_enable )
# #
# f_trclog_log_enable=open( os.path.join(context['temp_directory'],'tmp_trace_4345_log_enable.log'), 'w') # f_trclog_log_enable=open( os.path.join(context['temp_directory'],'tmp_trace_4345_log_enable.log'), 'w')
# #
# ##################################################### # #####################################################
# # Starting trace session in new child process (async.): # # Starting trace session in new child process (async.):
# #
# # Execute a child program in a new process, redirecting STDERR to the same target as of STDOUT: # # 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", # p_trace=Popen( [ context['fbsvcmgr_path'], "localhost:service_mgr",
# "action_trace_start", # "action_trace_start",
@ -206,63 +211,63 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# stdout=f_trclog_log_enable, # stdout=f_trclog_log_enable,
# stderr=subprocess.STDOUT # stderr=subprocess.STDOUT
# ) # )
# #
# # Wait _AT_LEAST_ 4..5 seconds in 2.5 because trace session is initialized not instantly. # # Wait _AT_LEAST_ 4..5 seconds in 2.5 because trace session is initialized not instantly.
# # If this delay is less then 2 second then trace log will be EMPTY (got on 2.5 SS and Cs). # # If this delay is less then 2 second then trace log will be EMPTY (got on 2.5 SS and Cs).
# time.sleep( min_delay_after_trace_start ) # time.sleep( min_delay_after_trace_start )
# #
# #################################################### # ####################################################
# # Make connection to database and perform script that # # Make connection to database and perform script that
# # calls two functions: standalone and packaged: # # calls two functions: standalone and packaged:
# #################################################### # ####################################################
# #
# runProgram('isql',[dsn, '-n', '-q'], sql_fnc % (123, 456) ) # runProgram('isql',[dsn, '-n', '-q'], sql_fnc % (123, 456) )
# #
# # do NOT remove this otherwise trace log can contain only message about its start before being closed! # # do NOT remove this otherwise trace log can contain only message about its start before being closed!
# time.sleep(min_delay_before_trace_stop) # time.sleep(min_delay_before_trace_stop)
# #
# ##################################################### # #####################################################
# # Getting ID of launched trace session and STOP it: # # Getting ID of launched trace session and STOP it:
# #
# stop_trace_session() # stop_trace_session()
# time.sleep(min_delay_after_trace_stop) # time.sleep(min_delay_after_trace_stop)
# #
# # Terminate child process of launched trace session (though it should already be killed): # # Terminate child process of launched trace session (though it should already be killed):
# p_trace.terminate() # p_trace.terminate()
# f_trclog_log_enable.close() # f_trclog_log_enable.close()
# #
# #
# ############# # #############
# # O U T P U T # # O U T P U T
# ############# # #############
# #
# with open( f_trclog_log_enable.name,'r') as f: # with open( f_trclog_log_enable.name,'r') as f:
# for line in f: # for line in f:
# if ( func_start_ptn.search(line) # if ( func_start_ptn.search(line)
# or func_finish_ptn.search(line) # or func_finish_ptn.search(line)
# or func_name_ptn.search(line) # or func_name_ptn.search(line)
# or func_param_prn.search(line) ): # or func_param_prn.search(line) ):
# print('LOG_FUNC_ENABLED '+line.upper()) # print('LOG_FUNC_ENABLED '+line.upper())
# f.close() # f.close()
# #
# ############################################################################# # #############################################################################
# ### ### # ### ###
# ### C A S E - 2: l o g _ f u n c = f al s e ### # ### C A S E - 2: l o g _ f u n c = f al s e ###
# ### ### # ### ###
# ############################################################################# # #############################################################################
# #
# # Make trace config with DISABLING logging of func. execution: # # Make trace config with DISABLING logging of func. execution:
# #
# trccfg_log_disable=os.path.join(context['temp_directory'],'tmp_trace_4345_log_disable.cfg') # trccfg_log_disable=os.path.join(context['temp_directory'],'tmp_trace_4345_log_disable.cfg')
# make_trace_config( 'false', trccfg_log_disable ) # make_trace_config( 'false', trccfg_log_disable )
# #
# ##################################################### # #####################################################
# # Starting trace session in new child process (async.): # # Starting trace session in new child process (async.):
# #
# # Execute a child program in a new process, redirecting STDERR to the same target as of STDOUT: # # Execute a child program in a new process, redirecting STDERR to the same target as of STDOUT:
# #
# f_trclog_log_disable=open( os.path.join(context['temp_directory'],'tmp_trace_4345_log_disable.log'), 'w') # f_trclog_log_disable=open( os.path.join(context['temp_directory'],'tmp_trace_4345_log_disable.log'), 'w')
# #
# p_trace=Popen( [context['fbsvcmgr_path'], "localhost:service_mgr", # p_trace=Popen( [context['fbsvcmgr_path'], "localhost:service_mgr",
# "action_trace_start", # "action_trace_start",
# "trc_cfg", trccfg_log_disable # "trc_cfg", trccfg_log_disable
@ -270,62 +275,63 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# stdout=f_trclog_log_disable, # stdout=f_trclog_log_disable,
# stderr=subprocess.STDOUT # stderr=subprocess.STDOUT
# ) # )
# #
# # Wait _AT_LEAST_ 4..5 seconds in 2.5 because trace session is initialized not instantly. # # Wait _AT_LEAST_ 4..5 seconds in 2.5 because trace session is initialized not instantly.
# # If this delay is less then 2 second then trace log will be EMPTY (got on 2.5 SS and Cs). # # If this delay is less then 2 second then trace log will be EMPTY (got on 2.5 SS and Cs).
# #
# time.sleep( min_delay_after_trace_start ) # time.sleep( min_delay_after_trace_start )
# #
# #
# #################################################### # ####################################################
# # Make connection to database and perform script that # # Make connection to database and perform script that
# # calls two functions: standalone and packaged: # # calls two functions: standalone and packaged:
# #################################################### # ####################################################
# #
# runProgram('isql',[dsn, '-n', '-q'], sql_fnc % (789, 987) ) # runProgram('isql',[dsn, '-n', '-q'], sql_fnc % (789, 987) )
# #
# # do NOT remove this otherwise trace log can contain only message about its start before being closed! # # do NOT remove this otherwise trace log can contain only message about its start before being closed!
# time.sleep(min_delay_before_trace_stop) # time.sleep(min_delay_before_trace_stop)
# #
# ##################################################### # #####################################################
# # Getting ID of launched trace session and STOP it: # # Getting ID of launched trace session and STOP it:
# stop_trace_session() # stop_trace_session()
# time.sleep(min_delay_after_trace_stop) # time.sleep(min_delay_after_trace_stop)
# #
# #
# # Terminate child process of launched trace session (though it should already be killed): # # Terminate child process of launched trace session (though it should already be killed):
# p_trace.terminate() # p_trace.terminate()
# f_trclog_log_disable.close() # f_trclog_log_disable.close()
# #
# ############# # #############
# # O U T P U T # # O U T P U T
# ############# # #############
# #
# with open( f_trclog_log_disable.name,'r') as f: # with open( f_trclog_log_disable.name,'r') as f:
# for line in f: # for line in f:
# if ( func_start_ptn.search(line) # if ( func_start_ptn.search(line)
# or func_finish_ptn.search(line) # or func_finish_ptn.search(line)
# or func_name_ptn.search(line) # or func_name_ptn.search(line)
# or func_param_prn.search(line) ): # or func_param_prn.search(line) ):
# print('LOG_FUNC_DISABLED '+line.upper()) # print('LOG_FUNC_DISABLED '+line.upper())
# f.close() # f.close()
# #
# time.sleep(1) # time.sleep(1)
# #
# ############################### # ###############################
# # Cleanup. # # Cleanup.
# #
# f_list = (f_trclog_log_enable, f_trclog_log_disable) # f_list = (f_trclog_log_enable, f_trclog_log_disable)
# for i in range(len(f_list)): # for i in range(len(f_list)):
# if os.path.isfile(f_list[i].name): # if os.path.isfile(f_list[i].name):
# os.remove(f_list[i].name) # os.remove(f_list[i].name)
# #
# os.remove(trccfg_log_enable) # os.remove(trccfg_log_enable)
# os.remove(trccfg_log_disable) # os.remove(trccfg_log_disable)
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
LOG_FUNC_ENABLED 2016-02-10T15:10:43.5940 (1700:00C52280) EXECUTE_FUNCTION_START LOG_FUNC_ENABLED 2016-02-10T15:10:43.5940 (1700:00C52280) EXECUTE_FUNCTION_START
@ -342,11 +348,100 @@ expected_stdout_1 = """
LOG_FUNC_ENABLED FUNCTION PG_TEST.PG_FUNC: LOG_FUNC_ENABLED FUNCTION PG_TEST.PG_FUNC:
LOG_FUNC_ENABLED PARAM0 = INTEGER, "456" LOG_FUNC_ENABLED PARAM0 = INTEGER, "456"
LOG_FUNC_ENABLED PARAM0 = BIGINT, "207936" LOG_FUNC_ENABLED PARAM0 = BIGINT, "207936"
""" """
def trace_session(act: Action, b: Barrier, logfunc: bool):
cfg30 = ['# Trace config, format for 3.0. Generated auto, do not edit!',
f'database=%[\\\\/]{act.db.db_path.name}',
'{',
' enabled = true',
' time_threshold = 0',
' log_errors = true',
' log_connections = true',
' log_transactions = true',
]
if logfunc:
cfg30.append(' log_function_start = true')
cfg30.append(' log_function_finish = true')
cfg30.append('}')
with act.connect_server() as srv:
srv.trace.start(config='\n'.join(cfg30))
b.wait()
for line in srv:
print(line.upper())
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action, capsys):
def test_1(db_1): output = []
pytest.fail("Test not IMPLEMENTED") trace_timestamp_prefix = '[.*\\s+]*20[0-9]{2}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3,4}\\s+\\(.+\\)'
func_start_ptn = re.compile(trace_timestamp_prefix + '\\s+(FAILED){0,1}\\s*EXECUTE_FUNCTION_START$', re.IGNORECASE)
func_finish_ptn = re.compile(trace_timestamp_prefix + '\\s+(FAILED){0,1}\\s*EXECUTE_FUNCTION_FINISH$', re.IGNORECASE)
func_name_ptn = re.compile('Function\\s+(SA_FUNC|PG_TEST.PG_FUNC):$')
func_param_prn = re.compile('param[0-9]+\\s+=\\s+', re.IGNORECASE)
#
func_script = """
set list on;
set term ^;
execute block as -- returns( sa_func_result bigint, pg_func_result bigint ) as
declare sa_func_result bigint;
declare pg_func_result bigint;
begin
sa_func_result = sa_func(%s);
pg_func_result = pg_test.pg_func(%s);
--suspend;
end
^
set term ;^
commit;
"""
# Case 1: Trace functions enabled
b = Barrier(2)
trace_thread = Thread(target=trace_session, args=[act_1, b, True])
trace_thread.start()
b.wait()
try:
act_1.isql(switches=['-n', '-q'], input=func_script % (123, 456))
time.sleep(2)
finally:
with act_1.connect_server() as srv:
for session in list(srv.trace.sessions.keys()):
srv.trace.stop(session_id=session)
trace_thread.join(1.0)
if trace_thread.is_alive():
pytest.fail('Trace thread still alive')
#
trace_log = capsys.readouterr().out
for line in trace_log.splitlines():
if (func_start_ptn.search(line)
or func_finish_ptn.search(line)
or func_name_ptn.search(line)
or func_param_prn.search(line) ):
output.append('LOG_FUNC_ENABLED ' + line.upper())
# Case 1: Trace functions disabled
b = Barrier(2)
trace_thread = Thread(target=trace_session, args=[act_1, b, False])
trace_thread.start()
b.wait()
try:
act_1.isql(switches=['-n', '-q'], input=func_script % (789, 987))
time.sleep(2)
finally:
with act_1.connect_server() as srv:
for session in list(srv.trace.sessions.keys()):
srv.trace.stop(session_id=session)
trace_thread.join(1.0)
if trace_thread.is_alive():
pytest.fail('Trace thread still alive')
#
trace_log += capsys.readouterr().out
for line in trace_log.splitlines():
if (func_start_ptn.search(line)
or func_finish_ptn.search(line)
or func_name_ptn.search(line)
or func_param_prn.search(line) ):
print('LOG_FUNC_DISABLED ' + line.upper())
# Test
act_1.reset()
act_1.expected_stdout = expected_stdout_1
act_1.stdout = '\n'.join(output)
assert act_1.clean_stderr == act_1.clean_expected_stderr

View File

@ -2,16 +2,17 @@
# #
# id: bugs.core_4380 # id: bugs.core_4380
# title: ISQL truncates blob when reading an empty segment # title: ISQL truncates blob when reading an empty segment
# decription: # decription:
# Checked on: 4.0.0.138 (both Windows and POSIX); 3.0.0.32484. # Checked on: 4.0.0.138 (both Windows and POSIX); 3.0.0.32484.
# #
# tracker_id: CORE-4380 # tracker_id: CORE-4380
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action import re
from firebird.qa import db_factory, python_act, Action
# version: 3.0 # version: 3.0
# resources: None # resources: None
@ -35,32 +36,32 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# import os # import os
# import subprocess # import subprocess
# import re # import re
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# # -- NB: i'm not sure that this test properly reflects the trouble described in the ticket. # # -- NB: i'm not sure that this test properly reflects the trouble described in the ticket.
# # -- At least on 3.0 Alpha 1, Alpha 2 and Beta 2 (31807) output is identical. # # -- At least on 3.0 Alpha 1, Alpha 2 and Beta 2 (31807) output is identical.
# # -- Note that value in "BLR to Source mapping" under 'Column' was changed to reflect # # -- Note that value in "BLR to Source mapping" under 'Column' was changed to reflect
# # -- real offset from the beginning of line in THIS .fbt file (right shifted on 4 character). # # -- real offset from the beginning of line in THIS .fbt file (right shifted on 4 character).
# #
# sql_script=''' set blob all; # sql_script=''' set blob all;
# set list on; # set list on;
# select rdb$debug_info from rdb$procedures; # select rdb$debug_info from rdb$procedures;
# ''' # '''
# #
# f_blob_sql = open( os.path.join(context['temp_directory'],'tmp_blob_4380.sql'), 'w') # f_blob_sql = open( os.path.join(context['temp_directory'],'tmp_blob_4380.sql'), 'w')
# f_blob_sql.write(sql_script) # f_blob_sql.write(sql_script)
# f_blob_sql.close() # f_blob_sql.close()
# #
# f_blob_log = open( os.path.join(context['temp_directory'],'tmp_blob_4380.log'), 'w') # f_blob_log = open( os.path.join(context['temp_directory'],'tmp_blob_4380.log'), 'w')
# #
# subprocess.call( [ context['isql_path'], dsn, "-i", f_blob_sql.name], # subprocess.call( [ context['isql_path'], dsn, "-i", f_blob_sql.name],
# stdout = f_blob_log, # stdout = f_blob_log,
# stderr = subprocess.STDOUT # stderr = subprocess.STDOUT
# ) # )
# f_blob_log.close() # f_blob_log.close()
# #
# # RDB$DEBUG_INFO 1a:1e1 # # RDB$DEBUG_INFO 1a:1e1
# # Parameters: # # Parameters:
# # Number Name Type # # Number Name Type
@ -80,20 +81,20 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# # ^ ^ ^ # # ^ ^ ^
# # | | | # # | | |
# # +-----------+----------+---- all of them can vary! # # +-----------+----------+---- all of them can vary!
# #
# # Print content of log with filtering lines:we are interesting only for rows # # Print content of log with filtering lines:we are interesting only for rows
# # which contain words: {'Parameters', 'Number', 'Variables', 'BLR'}. # # which contain words: {'Parameters', 'Number', 'Variables', 'BLR'}.
# # For last line (with three numbers for offset, line and col) we just check # # For last line (with three numbers for offset, line and col) we just check
# # matching of row to appropriate pattern. # # matching of row to appropriate pattern.
# #
# # NB: we remove all exsessive spaces from printed lines. # # NB: we remove all exsessive spaces from printed lines.
# #
# pattern = re.compile("[\\s]+[0-9]+[\\s]+[0-9]+[\\s]+[0-9]+") # pattern = re.compile("[\\s]+[0-9]+[\\s]+[0-9]+[\\s]+[0-9]+")
# #
# with open( f_blob_log.name,'r') as f: # with open( f_blob_log.name,'r') as f:
# for line in f: # for line in f:
# line = line.upper() # line = line.upper()
# #
# if ('PARAMETER' in line or # if ('PARAMETER' in line or
# 'NUMBER' in line or # 'NUMBER' in line or
# 'INPUT' in line or # 'INPUT' in line or
@ -101,24 +102,25 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# 'VARIABLE' in line or # 'VARIABLE' in line or
# 'BLR' in line): # 'BLR' in line):
# print(' '.join(line.split()).upper()) # print(' '.join(line.split()).upper())
# #
# if pattern.match(line): # if pattern.match(line):
# print('VALUES: <OFFSET> <LINE> <COLUMN>') # print('VALUES: <OFFSET> <LINE> <COLUMN>')
# #
# ################################################ # ################################################
# # Cleanup # # Cleanup
# #
# f_list=[] # f_list=[]
# f_list.append(f_blob_sql) # f_list.append(f_blob_sql)
# f_list.append(f_blob_log) # f_list.append(f_blob_log)
# #
# for i in range(len(f_list)): # for i in range(len(f_list)):
# if os.path.isfile(f_list[i].name): # if os.path.isfile(f_list[i].name):
# os.remove(f_list[i].name) # os.remove(f_list[i].name)
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
PARAMETERS: PARAMETERS:
@ -130,11 +132,63 @@ expected_stdout_1 = """
BLR TO SOURCE MAPPING: BLR TO SOURCE MAPPING:
BLR OFFSET LINE COLUMN BLR OFFSET LINE COLUMN
VALUES: <OFFSET> <LINE> <COLUMN> VALUES: <OFFSET> <LINE> <COLUMN>
""" """
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action, capsys):
def test_1(db_1): # -- NB: i'm not sure that this test properly reflects the trouble described in the ticket.
pytest.fail("Test not IMPLEMENTED") # -- At least on 3.0 Alpha 1, Alpha 2 and Beta 2 (31807) output is identical.
# -- Note that value in "BLR to Source mapping" under 'Column' was changed to reflect
# -- real offset from the beginning of line in THIS .fbt file (right shifted on 4 character).
sql_script = """
set blob all;
set list on;
select rdb$debug_info from rdb$procedures;
"""
act_1.isql(switches=[], input=sql_script)
# RDB$DEBUG_INFO 1a:1e1
# Parameters:
# Number Name Type
# --------------------------------------------------
# 0 A_ID INPUT
# 0 O_TXT OUTPUT
#
# Variables:
# Number Name
# -------------------------------------------
# 0 O_TXT
#
# BLR to Source mapping:
# BLR offset Line Column
# --------------------------------
# 42 2 79
# ^ ^ ^
# | | |
# +-----------+----------+---- all of them can vary!
# Print content of log with filtering lines:we are interesting only for rows
# which contain words: {'Parameters', 'Number', 'Variables', 'BLR'}.
# For last line (with three numbers for offset, line and col) we just check
# matching of row to appropriate pattern.
# NB: we remove all exsessive spaces from printed lines.
pattern = re.compile("[\\s]+[0-9]+[\\s]+[0-9]+[\\s]+[0-9]+")
for line in act_1.stdout.splitlines():
line = line.upper()
if ('PARAMETER' in line or
'NUMBER' in line or
'INPUT' in line or
'OUTPUT' in line or
'VARIABLE' in line or
'BLR' in line):
print(' '.join(line.split()).upper())
if pattern.match(line):
print('VALUES: <OFFSET> <LINE> <COLUMN>')
# Test
act_1.reset()
act_1.expected_stdout = expected_stdout_1
act_1.stdout = capsys.readouterr().out
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,7 +2,7 @@
# #
# id: bugs.core_4382 # id: bugs.core_4382
# title: User savepoints are not released on commit # title: User savepoints are not released on commit
# decription: # decription:
# Added separate code for 4.0: one need to be sure that all changes have been flushed on disk before we launch gstat. # Added separate code for 4.0: one need to be sure that all changes have been flushed on disk before we launch gstat.
# See letter from hvlad, 02.02.2019 22:30. # See letter from hvlad, 02.02.2019 22:30.
# ::: NOTE ::: # ::: NOTE :::
@ -12,19 +12,20 @@
# 4.0.0.1421: OK, 3.340s. // SS, SC, CS # 4.0.0.1421: OK, 3.340s. // SS, SC, CS
# 3.0.5.33097: OK, 1.113s. # 3.0.5.33097: OK, 1.113s.
# 2.5.9.27127: OK, 0.650s. # 2.5.9.27127: OK, 0.650s.
# #
# tracker_id: CORE-4382 # tracker_id: CORE-4382
# min_versions: ['2.5.4'] # min_versions: ['2.5.4']
# versions: 4.0 # versions: 4.0
# qmid: # qmid:
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, python_act, Action
# version: 4.0 # version: 4.0
# resources: None # resources: None
substitutions_1 = [('^((?!nodes).)*$', ''), ('Root page: [0-9]+,', ''), ('Depth', 'depth')] substitutions_1 = [('^((?!nodes).)*$', ''),
('Root page: [0-9]+,', ''), ('Depth', 'depth')]
init_script_1 = """""" init_script_1 = """"""
@ -32,12 +33,12 @@ db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# db_conn.close() # db_conn.close()
# #
# sql_prep=''' # sql_prep='''
# create table g_test (f integer); # create table g_test (f integer);
# create index g_ind on g_test (f); # create index g_ind on g_test (f);
@ -55,21 +56,43 @@ db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1)
# savepoint e; # savepoint e;
# update g_test set f=7; # update g_test set f=7;
# commit; # commit;
# select * from g_test; # select * from g_test;
# ''' # '''
# runProgram( 'isql',[ '-q', dsn], sql_prep ), # runProgram( 'isql',[ '-q', dsn], sql_prep ),
# runProgram( 'gstat',['-i', dsn] ) # runProgram( 'gstat',['-i', dsn] )
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
Root page: 203, depth: 1, leaf buckets: 1, nodes: 1 Root page: 203, depth: 1, leaf buckets: 1, nodes: 1
""" """
@pytest.mark.version('>=4.0') @pytest.mark.version('>=4.0')
@pytest.mark.xfail def test_1(act_1: Action):
def test_1(db_1): sql_scipt = """
pytest.fail("Test not IMPLEMENTED") create table g_test (f integer);
create index g_ind on g_test (f);
insert into g_test values (1);
commit;
update g_test set f=2;
savepoint a;
update g_test set f=3;
savepoint b;
update g_test set f=4;
savepoint c;
update g_test set f=5;
savepoint d;
update g_test set f=6;
savepoint e;
update g_test set f=7;
commit;
select * from g_test;
"""
act_1.isql(switches=['-q'], input=sql_scipt)
act_1.reset()
act_1.expected_stdout = expected_stdout_1
act_1.gstat(switches=['-i'])
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,16 +2,17 @@
# #
# id: bugs.core_4386 # id: bugs.core_4386
# title: Report more details for "object in use" errors # title: Report more details for "object in use" errors
# decription: # decription:
# Checked on 3.0.6.33242 (intermediate build) after discuss with dimitr. # Checked on 3.0.6.33242 (intermediate build) after discuss with dimitr.
# #
# tracker_id: CORE-4386 # tracker_id: CORE-4386
# min_versions: ['3.0.6'] # min_versions: ['3.0.6']
# versions: 3.0.6 # versions: 3.0.6
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, python_act, Action
from firebird.driver import TPB, Isolation
# version: 3.0.6 # version: 3.0.6
# resources: None # resources: None
@ -26,72 +27,72 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
#--- #---
# import os # import os
# import subprocess # import subprocess
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# db_conn.close() # db_conn.close()
# #
# CUSTOM_TX_PARAMS = ( [ fdb.isc_tpb_read_committed, fdb.isc_tpb_no_rec_version, fdb.isc_tpb_nowait ] ) # CUSTOM_TX_PARAMS = ( [ fdb.isc_tpb_read_committed, fdb.isc_tpb_no_rec_version, fdb.isc_tpb_nowait ] )
# #
# sql_ddl=''' # sql_ddl='''
# set bail on; # set bail on;
# create or alter procedure sp_worker as begin end; # create or alter procedure sp_worker as begin end;
# create or alter procedure sp_test as begin end; # create or alter procedure sp_test as begin end;
# create or alter view v_test as select 1 x from rdb$database; # create or alter view v_test as select 1 x from rdb$database;
# commit; # commit;
# #
# recreate table test1(id int,x int); # recreate table test1(id int,x int);
# recreate table test2(id int,x int); # recreate table test2(id int,x int);
# commit; # commit;
# #
# create index test1_id on test1(id); # create index test1_id on test1(id);
# commit; # commit;
# create descending index test2_id_x_desc on test2(id,x); # create descending index test2_id_x_desc on test2(id,x);
# commit; # commit;
# #
# create or alter view v_test as select id,x from test1 where id between 15 and 30; # create or alter view v_test as select id,x from test1 where id between 15 and 30;
# commit; # commit;
# #
# set term ^; # set term ^;
# create or alter procedure sp_worker(a_id int) returns(x int) as # create or alter procedure sp_worker(a_id int) returns(x int) as
# begin # begin
# for # for
# execute statement ('select v.x from v_test v where v.id = ? and exists(select * from test2 b where b.id = v.id)') (:a_id) # execute statement ('select v.x from v_test v where v.id = ? and exists(select * from test2 b where b.id = v.id)') (:a_id)
# into x # into x
# do # do
# suspend; # suspend;
# end # end
# ^ # ^
# create or alter procedure sp_test(a_id int) returns(x int) as # create or alter procedure sp_test(a_id int) returns(x int) as
# begin # begin
# for # for
# execute statement ('select x from sp_worker(?)') (:a_id) # execute statement ('select x from sp_worker(?)') (:a_id)
# into x # into x
# do # do
# suspend; # suspend;
# end # end
# ^ # ^
# set term ;^ # set term ;^
# commit; # commit;
# #
# insert into test1 values(11,111); # insert into test1 values(11,111);
# insert into test1 values(21,222); # insert into test1 values(21,222);
# insert into test1 values(31,333); # insert into test1 values(31,333);
# insert into test1 values(41,444); # insert into test1 values(41,444);
# commit; # commit;
# #
# insert into test2 select * from test1; # insert into test2 select * from test1;
# commit; # commit;
# ''' # '''
# #
# runProgram('isql', [ dsn ], sql_ddl) # runProgram('isql', [ dsn ], sql_ddl)
# #
# con1=fdb.connect(dsn = dsn) # con1=fdb.connect(dsn = dsn)
# cur1=con1.cursor() # cur1=con1.cursor()
# cur1.execute('select x from sp_test(21)') # cur1.execute('select x from sp_test(21)')
# for r in cur1: # for r in cur1:
# pass # pass
# #
# drop_commands = [ # drop_commands = [
# 'drop procedure sp_test', # 'drop procedure sp_test',
# 'drop procedure sp_worker', # 'drop procedure sp_worker',
@ -100,7 +101,7 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# 'drop index test1_id', # 'drop index test1_id',
# 'drop index test2_id_x_desc' # 'drop index test2_id_x_desc'
# ] # ]
# #
# for i,c in enumerate(drop_commands): # for i,c in enumerate(drop_commands):
# con2=fdb.connect(dsn = dsn) # con2=fdb.connect(dsn = dsn)
# tx = con2.trans( default_tpb = CUSTOM_TX_PARAMS ) # tx = con2.trans( default_tpb = CUSTOM_TX_PARAMS )
@ -109,7 +110,7 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# ############################################# # #############################################
# tx.begin() # tx.begin()
# cur2=tx.cursor() # cur2=tx.cursor()
# #
# try: # try:
# cur2.execute( c ) # cur2.execute( c )
# tx.commit() # tx.commit()
@ -119,13 +120,14 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# finally: # finally:
# cur2.close() # cur2.close()
# con2.close() # con2.close()
# #
# cur1.close() # cur1.close()
# con1.close() # con1.close()
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
Error while commiting transaction: Error while commiting transaction:
@ -169,11 +171,89 @@ expected_stdout_1 = """
- unsuccessful metadata update - unsuccessful metadata update
- object INDEX "TEST2_ID_X_DESC" is in use - object INDEX "TEST2_ID_X_DESC" is in use
335544345 335544345
""" """
@pytest.mark.version('>=3.0.6') @pytest.mark.version('>=3.0.6')
@pytest.mark.xfail def test_1(act_1: Action, capsys):
def test_1(db_1): ddl_script = """
pytest.fail("Test not IMPLEMENTED") set bail on;
create or alter procedure sp_worker as begin end;
create or alter procedure sp_test as begin end;
create or alter view v_test as select 1 x from rdb$database;
commit;
recreate table test1(id int,x int);
recreate table test2(id int,x int);
commit;
create index test1_id on test1(id);
commit;
create descending index test2_id_x_desc on test2(id,x);
commit;
create or alter view v_test as select id,x from test1 where id between 15 and 30;
commit;
set term ^;
create or alter procedure sp_worker(a_id int) returns(x int) as
begin
for
execute statement ('select v.x from v_test v where v.id = ? and exists(select * from test2 b where b.id = v.id)') (:a_id)
into x
do
suspend;
end
^
create or alter procedure sp_test(a_id int) returns(x int) as
begin
for
execute statement ('select x from sp_worker(?)') (:a_id)
into x
do
suspend;
end
^
set term ;^
commit;
insert into test1 values(11,111);
insert into test1 values(21,222);
insert into test1 values(31,333);
insert into test1 values(41,444);
commit;
insert into test2 select * from test1;
commit;
"""
act_1.isql(switches=[], input=ddl_script)
#
tpb = TPB(isolation=Isolation.READ_COMMITTED_NO_RECORD_VERSION, lock_timeout=0).get_buffer()
with act_1.db.connect() as con:
cur1 = con.cursor()
cur1.execute('select x from sp_test(21)').fetchall()
drop_commands = ['drop procedure sp_test',
'drop procedure sp_worker',
'drop view v_test',
'drop table test2',
'drop index test1_id',
'drop index test2_id_x_desc']
for cmd in drop_commands:
with act_1.db.connect() as con2:
tx = con2.transaction_manager(default_tpb=tpb)
tx.begin()
cur2 = tx.cursor()
try:
cur2.execute(cmd)
except Exception as exc:
print(exc)
#
act_1.reset()
act_1.expected_stdout = expected_stdout_1
act_1.stdout = capsys.readouterr().out
# [pcisar] 22.11.2021
# This test requires READ_COMMITTED_NO_RECORD_VERSION transaction to work, which
# requires ReadConsistency disabled in FB 4. However, it does not work as expected
# because all drop commands pass without exception even with ReadConsistency disabled.
# Not yet tested with FB3. I also expect it FAIL due to exception differences in FDB
# and new driver (this will be fixed once we make it to raise "object in use" exception)
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,51 +2,55 @@
# #
# id: bugs.core_4388 # id: bugs.core_4388
# title: SELECT WITH LOCK may enter an infinite loop for a single record # title: SELECT WITH LOCK may enter an infinite loop for a single record
# decription: # decription:
# Caution: could not reproduce on neither WI-T3.0.0.30566 Firebird 3.0 Alpha 1 nor WI-T3.0.0.30809 Firebird 3.0 Alpha 2. # Caution: could not reproduce on neither WI-T3.0.0.30566 Firebird 3.0 Alpha 1 nor WI-T3.0.0.30809 Firebird 3.0 Alpha 2.
# Any advice about how this test should be properly written will be appreciated. # Any advice about how this test should be properly written will be appreciated.
# Added separate code for 4.0 because isc_update_conflict now can be primary code of exception reason # Added separate code for 4.0 because isc_update_conflict now can be primary code of exception reason
# (after consulting with Vlad, letter 06-aug-2018 16:27). # (after consulting with Vlad, letter 06-aug-2018 16:27).
# #
# 01-apr-2020. Expected STDERR section for 4.0.x was changed BACK TO PREVIOUS set of messages, i.e.: # 01-apr-2020. Expected STDERR section for 4.0.x was changed BACK TO PREVIOUS set of messages, i.e.:
# 1. Statement failed, SQLSTATE = 40001 # 1. Statement failed, SQLSTATE = 40001
# 2. deadlock <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< THIS LINE APPEARED SINCE 4.0.0.1848 # 2. deadlock <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< THIS LINE APPEARED SINCE 4.0.0.1848
# 3. update conflicts with concurrent update # 3. update conflicts with concurrent update
# 4. concurrent transaction number is ... # 4. concurrent transaction number is ...
# Confirmed by Alex, letter 31.03.2020 12:01. # Confirmed by Alex, letter 31.03.2020 12:01.
# #
# Checked on: # Checked on:
# 3.0.4.33022: OK, 5.453s. # 3.0.4.33022: OK, 5.453s.
# 4.0.0.1158: OK, 5.313s. # 4.0.0.1158: OK, 5.313s.
# #
# 21.09.2020: removed separate section for 4.0 because error messages equal to FB 3.x. Changed 'substitution' section. # 21.09.2020: removed separate section for 4.0 because error messages equal to FB 3.x. Changed 'substitution' section.
# #
# Waiting for completion of child ISQL async process is done by call <isql_PID>.wait() instead of old (and "fragile") # Waiting for completion of child ISQL async process is done by call <isql_PID>.wait() instead of old (and "fragile")
# assumption about maximal time that it could last before forcedly terminate it. # assumption about maximal time that it could last before forcedly terminate it.
# #
# Replaced direct specification of executable 'isql' with context['isql_path'] in order to remove dependence on PATH # Replaced direct specification of executable 'isql' with context['isql_path'] in order to remove dependence on PATH
# (suggested by dimitr, letter 28.08.2020, 13:42; otherwise directory with isql must be added into PATH list). # (suggested by dimitr, letter 28.08.2020, 13:42; otherwise directory with isql must be added into PATH list).
# #
# #
# tracker_id: CORE-4388 # tracker_id: CORE-4388
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action import subprocess
import time
from pathlib import Path
from firebird.qa import db_factory, python_act, Action, temp_file
# version: 3.0 # version: 3.0
# resources: None # resources: None
substitutions_1 = [('(-)?concurrent\\s+transaction\\s+number(\\s+is)?\\s+\\d+', 'concurrent transaction'), ('After\\s+line\\s+\\d+.*', '')] substitutions_1 = [('(-)?concurrent\\s+transaction\\s+number(\\s+is)?\\s+\\d+', 'concurrent transaction'),
('After\\s+line\\s+\\d+.*', '')]
init_script_1 = """ init_script_1 = """
create table test(id int primary key, x int); create table test(id int primary key, x int);
commit; commit;
insert into test values(1, 100); insert into test values(1, 100);
commit; commit;
""" """
db_1 = db_factory(sql_dialect=3, init=init_script_1) db_1 = db_factory(sql_dialect=3, init=init_script_1)
@ -57,28 +61,28 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# from subprocess import Popen # from subprocess import Popen
# import time # import time
# import fdb # import fdb
# #
# db_conn.close() # db_conn.close()
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# #----------------------------------- # #-----------------------------------
# #
# def flush_and_close(file_handle): # def flush_and_close(file_handle):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# #
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -86,16 +90,16 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# os.remove( f_names_list[i] ) # os.remove( f_names_list[i] )
# if os.path.isfile( f_names_list[i]): # if os.path.isfile( f_names_list[i]):
# print('ERROR: can not remove file ' + f_names_list[i]) # print('ERROR: can not remove file ' + f_names_list[i])
# #
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# att1 = fdb.connect( dsn = dsn) # att1 = fdb.connect( dsn = dsn)
# #
# # Delete record but not yet commit - it's a time # # Delete record but not yet commit - it's a time
# # to make another connection: # # to make another connection:
# att1.execute_immediate("delete from test where id = 1") # att1.execute_immediate("delete from test where id = 1")
# #
# sql_cmd=''' # sql_cmd='''
# set list on; # set list on;
# -- set echo on; # -- set echo on;
@ -103,58 +107,84 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# set transaction lock timeout 20; # set transaction lock timeout 20;
# select x from test where id = 1 with lock; # select x from test where id = 1 with lock;
# ''' # '''
# #
# f_select_with_lock_sql = open( os.path.join(context['temp_directory'],'tmp_4388_select_with_lock.sql'), 'w') # f_select_with_lock_sql = open( os.path.join(context['temp_directory'],'tmp_4388_select_with_lock.sql'), 'w')
# f_select_with_lock_sql.write(sql_cmd) # f_select_with_lock_sql.write(sql_cmd)
# f_select_with_lock_sql.close() # f_select_with_lock_sql.close()
# #
# #context['isql_path'] # #context['isql_path']
# #
# f_select_with_lock_log = open( os.path.join(context['temp_directory'],'tmp_4388_select_with_lock.log'), 'w') # f_select_with_lock_log = open( os.path.join(context['temp_directory'],'tmp_4388_select_with_lock.log'), 'w')
# #
# p_hanged_isql=subprocess.Popen( [ context['isql_path'], dsn, "-n", "-i", f_select_with_lock_sql.name ], # p_hanged_isql=subprocess.Popen( [ context['isql_path'], dsn, "-n", "-i", f_select_with_lock_sql.name ],
# stdout = f_select_with_lock_log, # stdout = f_select_with_lock_log,
# stderr = subprocess.STDOUT # stderr = subprocess.STDOUT
# ) # )
# #
# # Let ISQL to be loaded and establish its attachment: # # Let ISQL to be loaded and establish its attachment:
# time.sleep(2) # time.sleep(2)
# #
# # Return to att1 and make COMMIT of deleted record: # # Return to att1 and make COMMIT of deleted record:
# ############# # #############
# att1.commit() # att1.commit()
# att1.close() # att1.close()
# ############# # #############
# #
# # Wait until ISQL complete its mission: # # Wait until ISQL complete its mission:
# p_hanged_isql.wait() # p_hanged_isql.wait()
# #
# flush_and_close(f_select_with_lock_log) # flush_and_close(f_select_with_lock_log)
# #
# with open(f_select_with_lock_log.name,'r') as f: # with open(f_select_with_lock_log.name,'r') as f:
# print(f.read()) # print(f.read())
# f.close() # f.close()
# #
# ############################### # ###############################
# # Cleanup. # # Cleanup.
# time.sleep(1) # time.sleep(1)
# f_list = [ i.name for i in (f_select_with_lock_sql, f_select_with_lock_log) ] # f_list = [ i.name for i in (f_select_with_lock_sql, f_select_with_lock_log) ]
# cleanup( f_list ) # cleanup( f_list )
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
Statement failed, SQLSTATE = 40001 Statement failed, SQLSTATE = 40001
deadlock deadlock
-update conflicts with concurrent update -update conflicts with concurrent update
-concurrent transaction number is 13 -concurrent transaction number is 13
""" """
test_script_1 = temp_file('test-script.sql')
script_out = temp_file('test-script.out')
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action, test_script_1: Path, script_out: Path):
def test_1(db_1): with act_1.db.connect() as att1:
pytest.fail("Test not IMPLEMENTED") # Delete record but not yet commit - it's a time
# to make another connection:
att1.execute_immediate("delete from test where id = 1")
test_script_1.write_text("""
set list on;
-- set echo on;
commit;
set transaction lock timeout 20;
select x from test where id = 1 with lock;
""")
try:
with open(script_out, mode='w') as output:
p_test_sql = subprocess.Popen([act_1.vars['isql'], '-n', '-i', str(test_script_1),
'-user', act_1.db.user,
'-password', act_1.db.password, act_1.db.dsn],
stdout=output, stderr=subprocess.STDOUT)
#
time.sleep(2)
finally:
att1.commit()
p_test_sql.wait()
# Check
act_1.expected_stdout = expected_stdout_1
act_1.stdout = script_out.read_text()
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,14 +2,15 @@
# #
# id: bugs.core_4398 # id: bugs.core_4398
# title: Provide ability to specify extra-long name of log when doing gbak to avoid "attempt to store 256 bytes in a clumplet" message # title: Provide ability to specify extra-long name of log when doing gbak to avoid "attempt to store 256 bytes in a clumplet" message
# decription: # decription:
# tracker_id: CORE-4398 # tracker_id: CORE-4398
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: # qmid:
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from pathlib import Path
from firebird.qa import db_factory, python_act, Action, temp_file
# version: 3.0 # version: 3.0
# resources: None # resources: None
@ -23,7 +24,7 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# import os # import os
# #
# print ('Starting backup...') # print ('Starting backup...')
# fbk = os.path.join(context['temp_directory'],'backup.fbk') # fbk = os.path.join(context['temp_directory'],'backup.fbk')
# lbk = os.path.join(context['temp_directory'],'A012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.log') # lbk = os.path.join(context['temp_directory'],'A012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.log')
@ -38,9 +39,10 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# print ('Delete log file...') # print ('Delete log file...')
# os.remove(lbk) # os.remove(lbk)
# print ('Log file deleted.') # print ('Log file deleted.')
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
Starting backup... Starting backup...
@ -49,11 +51,23 @@ expected_stdout_1 = """
Backup file deleted. Backup file deleted.
Delete log file... Delete log file...
Log file deleted. Log file deleted.
""" """
backup_file_1 = temp_file('backup.fbk')
log_file_1 = temp_file('A012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.log')
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action, capsys, log_file_1: Path, backup_file_1: Path):
def test_1(db_1): print ('Starting backup...')
pytest.fail("Test not IMPLEMENTED") act_1.gbak(switches=['-b', '-v', '-y', str(log_file_1), str(act_1.db.db_path), str(backup_file_1)])
print ('Backup finished.')
if backup_file_1.is_file():
print ('Delete backup file...')
backup_file_1.unlink()
print ('Backup file deleted.')
print ('Delete log file...')
log_file_1.unlink()
print ('Log file deleted.')
act_1.expected_stdout = expected_stdout_1
act_1.stdout = capsys.readouterr().out
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,20 +2,22 @@
# #
# id: bugs.core_4451 # id: bugs.core_4451
# title: Allow output to trace explain plan form. # title: Allow output to trace explain plan form.
# decription: # decription:
# Checked on # Checked on
# 4.0.0.1685 SS: 7.985s. # 4.0.0.1685 SS: 7.985s.
# 4.0.0.1685 CS: 8.711s. # 4.0.0.1685 CS: 8.711s.
# 3.0.5.33206 SS: 7.281s. # 3.0.5.33206 SS: 7.281s.
# 3.0.5.33206 CS: 8.278s. # 3.0.5.33206 CS: 8.278s.
# #
# tracker_id: CORE-4451 # tracker_id: CORE-4451
# min_versions: ['3.0.0'] # min_versions: ['3.0.0']
# versions: 3.0 # versions: 3.0
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action import time
from threading import Thread, Barrier
from firebird.qa import db_factory, python_act, Action
# version: 3.0 # version: 3.0
# resources: None # resources: None
@ -24,41 +26,41 @@ substitutions_1 = [('[ \t]+', ' '), ('[ \t]+[\\d]+[ \t]+ms', '')]
init_script_1 = """ init_script_1 = """
recreate table test(x int); recreate table test(x int);
""" """
db_1 = db_factory(sql_dialect=3, init=init_script_1) db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# import subprocess # import subprocess
# from subprocess import Popen # from subprocess import Popen
# import time # import time
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# fdb_file=db_conn.database_name # fdb_file=db_conn.database_name
# db_conn.close() # db_conn.close()
# #
# #
# #----------------------------------- # #-----------------------------------
# #
# def flush_and_close(file_handle): # def flush_and_close(file_handle):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# #
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -66,18 +68,18 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# os.remove( f_names_list[i] ) # os.remove( f_names_list[i] )
# if os.path.isfile( f_names_list[i]): # if os.path.isfile( f_names_list[i]):
# print('ERROR: can not remove file ' + f_names_list[i]) # print('ERROR: can not remove file ' + f_names_list[i])
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# #
# #
# ##################################################################### # #####################################################################
# # Prepare config for trace session that will be launched by call of FBSVCMGR: # # Prepare config for trace session that will be launched by call of FBSVCMGR:
# #
# txt = ''' database= # %[\\\\\\\\/]bugs.core_4451.fdb # txt = ''' database= # %[\\\\\\\\/]bugs.core_4451.fdb
# { # {
# enabled = true # enabled = true
# time_threshold = 0 # time_threshold = 0
# log_initfini = false # log_initfini = false
# print_plan = true # print_plan = true
# explain_plan = true # explain_plan = true
@ -88,40 +90,40 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# f_trccfg=open( os.path.join(context['temp_directory'],'tmp_trace_4451.cfg'), 'w') # f_trccfg=open( os.path.join(context['temp_directory'],'tmp_trace_4451.cfg'), 'w')
# f_trccfg.write(txt) # f_trccfg.write(txt)
# flush_and_close( f_trccfg ) # flush_and_close( f_trccfg )
# #
# ##################################################################### # #####################################################################
# # Async. launch of trace session using FBSVCMGR action_trace_start: # # Async. launch of trace session using FBSVCMGR action_trace_start:
# #
# f_trclog=open( os.path.join(context['temp_directory'],'tmp_trace_4451.log'), 'w') # f_trclog=open( os.path.join(context['temp_directory'],'tmp_trace_4451.log'), 'w')
# #
# # Execute a child program in a new process, redirecting STDERR to the same target as of STDOUT: # # Execute a child program in a new process, redirecting STDERR to the same target as of STDOUT:
# p_svcmgr = Popen( [ context['fbsvcmgr_path'], "localhost:service_mgr", # p_svcmgr = Popen( [ context['fbsvcmgr_path'], "localhost:service_mgr",
# "action_trace_start", # "action_trace_start",
# "trc_cfg", f_trccfg.name # "trc_cfg", f_trccfg.name
# ], # ],
# stdout=f_trclog, # stdout=f_trclog,
# stderr=subprocess.STDOUT # stderr=subprocess.STDOUT
# ) # )
# #
# # Wait! Trace session is initialized not instantly! # # Wait! Trace session is initialized not instantly!
# time.sleep(2) # time.sleep(2)
# #
# ##################################################################### # #####################################################################
# #
# # Determine active trace session ID (for further stop): # # Determine active trace session ID (for further stop):
# #
# f_trclst=open( os.path.join(context['temp_directory'],'tmp_trace_4451.lst'), 'w') # f_trclst=open( os.path.join(context['temp_directory'],'tmp_trace_4451.lst'), 'w')
# subprocess.call([context['fbsvcmgr_path'], "localhost:service_mgr", # subprocess.call([context['fbsvcmgr_path'], "localhost:service_mgr",
# "action_trace_list"], # "action_trace_list"],
# stdout=f_trclst, stderr=subprocess.STDOUT # stdout=f_trclst, stderr=subprocess.STDOUT
# ) # )
# flush_and_close( f_trclst ) # flush_and_close( f_trclst )
# #
# # Session ID: 5 # # Session ID: 5
# # user: # # user:
# # date: 2015-08-27 15:24:14 # # date: 2015-08-27 15:24:14
# # flags: active, trace # # flags: active, trace
# #
# trcssn=0 # trcssn=0
# with open( f_trclst.name,'r') as f: # with open( f_trclst.name,'r') as f:
# for line in f: # for line in f:
@ -132,79 +134,115 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# trcssn=word # trcssn=word
# i=i+1 # i=i+1
# break # break
# #
# # Result: `trcssn` is ID of active trace session. # # Result: `trcssn` is ID of active trace session.
# # We have to terminate trace session that is running on server BEFORE we termitane process `p_svcmgr` # # We have to terminate trace session that is running on server BEFORE we termitane process `p_svcmgr`
# if trcssn==0: # if trcssn==0:
# print("Error parsing trace session ID.") # print("Error parsing trace session ID.")
# flush_and_close( f_trclog ) # flush_and_close( f_trclog )
# else: # else:
# ##################################################################### # #####################################################################
# #
# # Preparing script for ISQL: # # Preparing script for ISQL:
# #
# sql_cmd='''select count(*) from test;''' # sql_cmd='''select count(*) from test;'''
# #
# so=sys.stdout # so=sys.stdout
# se=sys.stderr # se=sys.stderr
# #
# sys.stdout = open(os.devnull, 'w') # sys.stdout = open(os.devnull, 'w')
# sys.stderr = sys.stdout # sys.stderr = sys.stdout
# #
# runProgram('isql',[dsn],sql_cmd) # runProgram('isql',[dsn],sql_cmd)
# #
# sys.stdout = so # sys.stdout = so
# sys.stderr = se # sys.stderr = se
# #
# # do NOT reduce this delay! # # do NOT reduce this delay!
# time.sleep(2) # time.sleep(2)
# #
# ##################################################################### # #####################################################################
# #
# # Stop trace session: # # Stop trace session:
# #
# f_trclst=open(f_trclst.name, "a") # f_trclst=open(f_trclst.name, "a")
# f_trclst.seek(0,2) # f_trclst.seek(0,2)
# subprocess.call( [ context['fbsvcmgr_path'], "localhost:service_mgr", # subprocess.call( [ context['fbsvcmgr_path'], "localhost:service_mgr",
# "action_trace_stop", # "action_trace_stop",
# "trc_id",trcssn # "trc_id",trcssn
# ], # ],
# stdout=f_trclst, # stdout=f_trclst,
# stderr=subprocess.STDOUT # stderr=subprocess.STDOUT
# ) # )
# flush_and_close( f_trclst ) # flush_and_close( f_trclst )
# #
# p_svcmgr.terminate() # p_svcmgr.terminate()
# flush_and_close( f_trclog ) # flush_and_close( f_trclog )
# #
# # do NOT remove this delay: # # do NOT remove this delay:
# time.sleep(1) # time.sleep(1)
# #
# show_line = 0 # show_line = 0
# with open(f_trclog.name) as f: # with open(f_trclog.name) as f:
# for line in f: # for line in f:
# show_line = ( show_line + 1 if ('^' * 79) in line or show_line>0 else show_line ) # show_line = ( show_line + 1 if ('^' * 79) in line or show_line>0 else show_line )
# if show_line > 1: # if show_line > 1:
# print(line) # print(line)
# #
# # cleanup: # # cleanup:
# ########## # ##########
# time.sleep(1) # time.sleep(1)
# cleanup( [i.name for i in (f_trclst, f_trccfg, f_trclog) ] ) # cleanup( [i.name for i in (f_trclst, f_trccfg, f_trclog) ] )
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
Select Expression Select Expression
-> Aggregate -> Aggregate
-> Table "TEST" Full Scan -> Table "TEST" Full Scan
""" """
def trace_session(act: Action, b: Barrier):
cfg30 = ['# Trace config, format for 3.0. Generated auto, do not edit!',
f'database=%[\\\\/]{act.db.db_path.name}',
'{',
' enabled = true',
' time_threshold = 0',
' log_initfini = false',
' print_plan = true',
' explain_plan = true',
' log_statement_prepare = true',
' include_filter=%(from|join)[[:whitespace:]]test%',
'}']
with act.connect_server() as srv:
srv.trace.start(config='\n'.join(cfg30))
b.wait()
for line in srv:
print(line)
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action, capsys):
def test_1(db_1): b = Barrier(2)
pytest.fail("Test not IMPLEMENTED") trace_thread = Thread(target=trace_session, args=[act_1, b])
trace_thread.start()
b.wait()
act_1.isql(switches=[], input='select count(*) from test;')
time.sleep(2)
with act_1.connect_server() as srv:
for session in list(srv.trace.sessions.keys()):
srv.trace.stop(session_id=session)
trace_thread.join(1.0)
if trace_thread.is_alive():
pytest.fail('Trace thread still alive')
#
show_line = 0
for line in capsys.readouterr().out.splitlines():
show_line = (show_line + 1 if ('^' * 79) in line or show_line>0 else show_line)
if show_line > 1:
print(line)
act_1.expected_stdout = expected_stdout_1
act_1.stdout = capsys.readouterr().out
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,23 +2,21 @@
# #
# id: bugs.core_4461 # id: bugs.core_4461
# title: nbackup prints error messages to stdout instead stderr # title: nbackup prints error messages to stdout instead stderr
# decription: # decription:
# tracker_id: CORE-4461 # tracker_id: CORE-4461
# min_versions: ['2.5.4'] # min_versions: ['2.5.4']
# versions: 2.5.4 # versions: 2.5.4
# qmid: # qmid:
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, python_act, Action
# version: 2.5.4 # version: 2.5.4
# resources: None # resources: None
substitutions_1 = [('Failure: Database error', '')] substitutions_1 = [('Failure: Database error', '')]
init_script_1 = """ init_script_1 = """"""
-- NB: line `Failure: Database error` exists only in 2.5.x output.
"""
db_1 = db_factory(sql_dialect=3, init=init_script_1) db_1 = db_factory(sql_dialect=3, init=init_script_1)
@ -26,7 +24,8 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
#--- #---
# runProgram('nbackup',['-user','nonExistentFoo','-pas','invalidBar','-L',dsn]) # runProgram('nbackup',['-user','nonExistentFoo','-pas','invalidBar','-L',dsn])
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stderr_1 = """ expected_stderr_1 = """
[ [
@ -34,11 +33,13 @@ expected_stderr_1 = """
Your user name and password are not defined. Ask your database administrator to set up a Firebird login. Your user name and password are not defined. Ask your database administrator to set up a Firebird login.
SQLCODE:-902 SQLCODE:-902
] ]
""" """
@pytest.mark.version('>=2.5.4') @pytest.mark.version('>=2.5.4')
@pytest.mark.xfail def test_1(act_1: Action):
def test_1(db_1): act_1.expected_stderr = expected_stderr_1
pytest.fail("Test not IMPLEMENTED") act_1.nbackup(switches=['-user', 'nonExistentFoo', '-password', 'invalidBar',
'-L', act_1.db.dsn], credentials=False)
assert act_1.clean_stderr == act_1.clean_expected_stderr

View File

@ -2,7 +2,7 @@
# #
# id: bugs.core_4470 # id: bugs.core_4470
# title: gbak fails to restore database containing dependency between views and packaged functions # title: gbak fails to restore database containing dependency between views and packaged functions
# decription: # decription:
# Confirmed on WI-T3.0.0.30809 Firebird 3.0 Alpha 2: # Confirmed on WI-T3.0.0.30809 Firebird 3.0 Alpha 2:
# gbak: ERROR:action cancelled by trigger (0) to preserve data integrity # gbak: ERROR:action cancelled by trigger (0) to preserve data integrity
# gbak: ERROR: could not find object for GRANT # gbak: ERROR: could not find object for GRANT
@ -15,14 +15,16 @@
# 4.0.0.1633 CS: 3.438s. # 4.0.0.1633 CS: 3.438s.
# 3.0.5.33180 SS: 2.137s. # 3.0.5.33180 SS: 2.137s.
# 3.0.5.33178 CS: 2.490s. # 3.0.5.33178 CS: 2.490s.
# #
# tracker_id: CORE-4470 # tracker_id: CORE-4470
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from pathlib import Path
from firebird.qa import db_factory, python_act, Action, temp_file
from firebird.driver import SrvRestoreFlag
# version: 3.0 # version: 3.0
# resources: None # resources: None
@ -49,7 +51,7 @@ init_script_1 = """
set autoddl off; set autoddl off;
set term ^; set term ^;
create package wf as create package wf as
begin begin
function cest (t dtime) returns dtime; function cest (t dtime) returns dtime;
@ -61,18 +63,18 @@ init_script_1 = """
function current_xml returns dvc512; function current_xml returns dvc512;
function relpressure(airpressureabsolute dreal,temperature dreal,altitude dreal, humidity dreal) returns dreal; function relpressure(airpressureabsolute dreal,temperature dreal,altitude dreal, humidity dreal) returns dreal;
end^ end^
set term ;^ set term ;^
commit work; commit work;
set autoddl on; set autoddl on;
create table constants (id did generated by default as identity not null, create table constants (id did generated by default as identity not null,
typ dvc255, typ dvc255,
val dvc255, val dvc255,
keys dbigint, keys dbigint,
primary key (id)); primary key (id));
create table raw (id did generated by default as identity not null, create table raw (id did generated by default as identity not null,
readtime dtime not null, readtime dtime not null,
delay dsmallint, delay dsmallint,
@ -90,12 +92,12 @@ init_script_1 = """
uv dsmallint, uv dsmallint,
primary key (id), primary key (id),
constraint unq1_raw unique (readtime)); constraint unq1_raw unique (readtime));
create table timezone (jahr dsmallint not null, create table timezone (jahr dsmallint not null,
gmt_from dtime, gmt_from dtime,
gmt_thru dtime, gmt_thru dtime,
constraint pk_timezone primary key (jahr)); constraint pk_timezone primary key (jahr));
create index raw_idx1 on raw (hum_in); create index raw_idx1 on raw (hum_in);
create descending index raw_idx10 on raw (abs_pressure); create descending index raw_idx10 on raw (abs_pressure);
create descending index raw_idx11 on raw (readtime); create descending index raw_idx11 on raw (readtime);
@ -108,7 +110,7 @@ init_script_1 = """
create index raw_idx7 on raw (wind_gust); create index raw_idx7 on raw (wind_gust);
create index raw_idx8 on raw (wind_dir); create index raw_idx8 on raw (wind_dir);
create index raw_idx9 on raw (rain); create index raw_idx9 on raw (rain);
create view meteo (timestamp_utc, timestamp_local, tempint, humint, temp, hum, wind, wind_dir, wind_gust, wind_gust_dir, dew_point, rain, rain_rate, pressure, uv_index, solar_rad) as create view meteo (timestamp_utc, timestamp_local, tempint, humint, temp, hum, wind, wind_dir, wind_gust, wind_gust_dir, dew_point, rain, rain_rate, pressure, uv_index, solar_rad) as
select readtime, wf.fn_local_time(readtime), temp_in, hum_in, temp_out, hum_out, wind_ave / 3.6 , 22.5 * wind_dir, wind_gust / 3.6 , select readtime, wf.fn_local_time(readtime), temp_in, hum_in, temp_out, hum_out, wind_ave / 3.6 , 22.5 * wind_dir, wind_gust / 3.6 ,
22.5 * wind_dir, wf.dewpoint(temp_out, hum_out), cast(rain - lead(rain) over(order by readtime desc) as numeric (6,3)) , 0, 22.5 * wind_dir, wf.dewpoint(temp_out, hum_out), cast(rain - lead(rain) over(order by readtime desc) as numeric (6,3)) , 0,
@ -118,28 +120,28 @@ init_script_1 = """
set autoddl off; set autoddl off;
set term ^; set term ^;
create package body wf as create package body wf as
begin begin
function CEST (T dtime)returns dtime function CEST (T dtime)returns dtime
AS AS
begin begin
return dateadd (2 hour to t); return dateadd (2 hour to t);
end end
function CET (T dtime)returns dtime function CET (T dtime)returns dtime
AS AS
begin begin
return dateadd (1 hour to t); return dateadd (1 hour to t);
end end
function altitude returns dreal function altitude returns dreal
as as
begin begin
return (select c.val from constants c where c.typ='Altitude'); return (select c.val from constants c where c.typ='Altitude');
end end
function fn_local_time (t dtime)returns dtime function fn_local_time (t dtime)returns dtime
as as
declare variable jahr dsmallint; declare variable jahr dsmallint;
@ -156,7 +158,7 @@ init_script_1 = """
else else
return dateadd (1 hour to t); return dateadd (1 hour to t);
end end
function relpressure (airpressureabsolute dreal, temperature dreal, altitude dreal, humidity dreal) returns dreal function relpressure (airpressureabsolute dreal, temperature dreal, altitude dreal, humidity dreal) returns dreal
as as
declare variable g_n dreal; declare variable g_n dreal;
@ -169,7 +171,7 @@ init_script_1 = """
declare variable e_0 dreal; declare variable e_0 dreal;
declare variable f_rel dreal; declare variable f_rel dreal;
declare variable e_d dreal; declare variable e_d dreal;
begin begin
g_n = 9.80665;-- erdbeschleunigung (m/s^2) g_n = 9.80665;-- erdbeschleunigung (m/s^2)
gam = 0.0065;--temperaturabnahme in k pro geopotentiellen metern (k/gpm) gam = 0.0065;--temperaturabnahme in k pro geopotentiellen metern (k/gpm)
@ -183,13 +185,13 @@ init_script_1 = """
e_d = f_rel * e_0 * exp((17.5043 * temperature) / (241.2 + temperature));--momentaner stationsdampfdruck (hpa) e_d = f_rel * e_0 * exp((17.5043 * temperature) / (241.2 + temperature));--momentaner stationsdampfdruck (hpa)
return airpressureabsolute * exp((g_n * altitude) / (r * (temperature + t_0 + c * e_d + ((gam * altitude) / 2)))); return airpressureabsolute * exp((g_n * altitude) / (r * (temperature + t_0 + c * e_d + ((gam * altitude) / 2))));
end end
function yesterday returns dtime function yesterday returns dtime
as as
begin begin
return dateadd (-1 day to current_date); return dateadd (-1 day to current_date);
end end
function dewpoint (temp dreal, hum dsmallint) function dewpoint (temp dreal, hum dsmallint)
returns dreal returns dreal
as as
@ -204,7 +206,7 @@ init_script_1 = """
return temp - ((100 - hum) / 5.0); return temp - ((100 - hum) / 5.0);
end end
end end
function current_xml returns dvc512 function current_xml returns dvc512
as as
declare variable timestamp_utc type of column meteo.timestamp_utc; declare variable timestamp_utc type of column meteo.timestamp_utc;
@ -223,16 +225,16 @@ init_script_1 = """
declare variable pressure type of column meteo.pressure; declare variable pressure type of column meteo.pressure;
declare variable uv_index type of column meteo.uv_index; declare variable uv_index type of column meteo.uv_index;
declare variable solar_rad type of column meteo.solar_rad; declare variable solar_rad type of column meteo.solar_rad;
begin begin
select first 1 timestamp_utc,timestamp_local, tempint, humint, temp, hum, wind, wind_dir, wind_gust, wind_gust_dir, dew_point, select first 1 timestamp_utc,timestamp_local, tempint, humint, temp, hum, wind, wind_dir, wind_gust, wind_gust_dir, dew_point,
rain, rain_rate, pressure, uv_index, solar_rad rain, rain_rate, pressure, uv_index, solar_rad
from meteo order by timestamp_utc desc from meteo order by timestamp_utc desc
into :timestamp_utc, :timestamp_local, :tempint, :humint, :temp, :hum, :wind, :wind_dir, :wind_gust, :wind_gust_dir, into :timestamp_utc, :timestamp_local, :tempint, :humint, :temp, :hum, :wind, :wind_dir, :wind_gust, :wind_gust_dir,
:dew_point, :rain, :rain_rate, :pressure, :uv_index, :solar_rad; :dew_point, :rain, :rain_rate, :pressure, :uv_index, :solar_rad;
return '<current><thInt><temp>'|| return '<current><thInt><temp>'||
tempint|| tempint||
'</temp><humidity>'|| '</temp><humidity>'||
@ -255,19 +257,19 @@ init_script_1 = """
substring (timestamp_local from 1 for 19)|| substring (timestamp_local from 1 for 19)||
'</time></current>'; '</time></current>';
end end
end^ end^
set term ;^ set term ;^
commit; commit;
""" """
db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1) db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# import os # import os
# #
# fbk = os.path.join(context['temp_directory'],'core_4470-backup.fbk') # fbk = os.path.join(context['temp_directory'],'core_4470-backup.fbk')
# fbn = os.path.join(context['temp_directory'],'core_4470-restored.fdb') # fbn = os.path.join(context['temp_directory'],'core_4470-restored.fdb')
# print('Creating backup...') # print('Creating backup...')
@ -280,20 +282,26 @@ db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1)
# os.remove(fbk) # os.remove(fbk)
# if os.path.isfile(fbn): # if os.path.isfile(fbn):
# os.remove(fbn) # os.remove(fbn)
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
Creating backup...
Creating restore...
METEO METEO
WF WF
""" """
fbk_file_1 = temp_file('test.fbk')
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action, fbk_file_1: Path):
def test_1(db_1): with act_1.connect_server() as srv:
pytest.fail("Test not IMPLEMENTED") srv.database.backup(database=str(act_1.db.db_path), backup=str(fbk_file_1))
srv.wait()
srv.database.restore(backup=str(fbk_file_1), database=str(act_1.db.db_path),
flags=SrvRestoreFlag.REPLACE)
srv.wait()
act_1.expected_stdout = expected_stdout_1
act_1.isql(switches=['-q'], input='show view; show package;')
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,12 +2,12 @@
# #
# id: bugs.core_4472 # id: bugs.core_4472
# title: Message "Modifying function <F> which is currently in use" when running script with AUTODDL=OFF and <F> is called from INTERNAL function declared in other unit # title: Message "Modifying function <F> which is currently in use" when running script with AUTODDL=OFF and <F> is called from INTERNAL function declared in other unit
# decription: # decription:
# Test call delivering of firebird.log TWICE: before and after running ISQL. # Test call delivering of firebird.log TWICE: before and after running ISQL.
# Then we compare only size of obtained logs rather than content (differencs of size should be zero). # Then we compare only size of obtained logs rather than content (differencs of size should be zero).
# Result on WI-V3.0.0.32239, WI-V3.0.0.32239: Ok. # Result on WI-V3.0.0.32239, WI-V3.0.0.32239: Ok.
# #
# Result on WI-T3.0.0.30809 (Alpha2): # Result on WI-T3.0.0.30809 (Alpha2):
# Unexpected call to register plugin Remote, type 2 - ignored # Unexpected call to register plugin Remote, type 2 - ignored
# Unexpected call to register plugin Loopback, type 2 - ignored # Unexpected call to register plugin Loopback, type 2 - ignored
# Unexpected call to register plugin Legacy_Auth, type 12 - ignored # Unexpected call to register plugin Legacy_Auth, type 12 - ignored
@ -16,18 +16,19 @@
# Unexpected call to register plugin Arc4, type 16 - ignored # Unexpected call to register plugin Arc4, type 16 - ignored
# INET/inet_error: read errno = 10054 # INET/inet_error: read errno = 10054
# Modifying function FN_01 which is currently in use by active user requests # Modifying function FN_01 which is currently in use by active user requests
# #
# 13.04.2021. Adapted for run both on Windows and Linux. Checked on: # 13.04.2021. Adapted for run both on Windows and Linux. Checked on:
# Windows: 3.0.8.33445, 4.0.0.2416 # Windows: 3.0.8.33445, 4.0.0.2416
# Linux: 3.0.8.33426, 4.0.0.2416 # Linux: 3.0.8.33426, 4.0.0.2416
# #
# tracker_id: CORE-4472 # tracker_id: CORE-4472
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from difflib import unified_diff
from firebird.qa import db_factory, python_act, Action
# version: 3.0 # version: 3.0
# resources: None # resources: None
@ -43,29 +44,29 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# import os # import os
# import time # import time
# import subprocess # import subprocess
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# db_conn.close() # db_conn.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close( file_handle ): # def flush_and_close( file_handle ):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull: # if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull:
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -77,18 +78,18 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.') # print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.')
# print('type(f_names_list[i])=',type(f_names_list[i])) # print('type(f_names_list[i])=',type(f_names_list[i]))
# del_name = None # del_name = None
# #
# if del_name and os.path.isfile( del_name ): # if del_name and os.path.isfile( del_name ):
# os.remove( del_name ) # os.remove( del_name )
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# #
# fb_log_before=open( os.path.join(context['temp_directory'],'tmp_fb_log_4472_before.log'), 'w') # fb_log_before=open( os.path.join(context['temp_directory'],'tmp_fb_log_4472_before.log'), 'w')
# subprocess.call([context['fbsvcmgr_path'],"localhost:service_mgr","action_get_fb_log"], # subprocess.call([context['fbsvcmgr_path'],"localhost:service_mgr","action_get_fb_log"],
# stdout=fb_log_before, stderr=subprocess.STDOUT) # stdout=fb_log_before, stderr=subprocess.STDOUT)
# flush_and_close( fb_log_before ) # flush_and_close( fb_log_before )
# #
# sqltxt=''' # sqltxt='''
# set autoddl off; # set autoddl off;
# commit; # commit;
@ -98,7 +99,7 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# return 1; # return 1;
# end # end
# ^ # ^
# #
# create or alter procedure sp_01 # create or alter procedure sp_01
# as # as
# declare function fn_internal_01 returns int as # declare function fn_internal_01 returns int as
@ -110,56 +111,81 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# end # end
# ^ # ^
# set term ;^ # set term ;^
# commit; # commit;
# ''' # '''
# #
# f_sqllog=open( os.path.join(context['temp_directory'],'tmp_isql_4472.log'), 'w') # f_sqllog=open( os.path.join(context['temp_directory'],'tmp_isql_4472.log'), 'w')
# f_sqllog.close() # f_sqllog.close()
# runProgram('isql',[ dsn, '-q','-m','-o',f_sqllog.name],sqltxt) # runProgram('isql',[ dsn, '-q','-m','-o',f_sqllog.name],sqltxt)
# #
# fb_log_after=open( os.path.join(context['temp_directory'],'tmp_fb_log_4472_after.log'), 'w') # fb_log_after=open( os.path.join(context['temp_directory'],'tmp_fb_log_4472_after.log'), 'w')
# subprocess.call([context['fbsvcmgr_path'],"localhost:service_mgr", "action_get_fb_log"], # subprocess.call([context['fbsvcmgr_path'],"localhost:service_mgr", "action_get_fb_log"],
# stdout=fb_log_after, stderr=subprocess.STDOUT) # stdout=fb_log_after, stderr=subprocess.STDOUT)
# flush_and_close( fb_log_after ) # flush_and_close( fb_log_after )
# #
# # This log should be EMPTY: # # This log should be EMPTY:
# with open( f_sqllog.name,'r') as f: # with open( f_sqllog.name,'r') as f:
# for line in f: # for line in f:
# if line.split() and not 'Database:' in line: # if line.split() and not 'Database:' in line:
# # This line must be ignored: # # This line must be ignored:
# # Database: localhost/3333:C:\\FBTESTING\\qa # # Database: localhost/3333:C:\\FBTESTING\\qa\\fbt-repo\\tmp\\bugs.core_4472.fdb, User: SYSDBA
# bt-repo mpugs.core_4472.fdb, User: SYSDBA
# print('UNEXPECTED: ' + line) # print('UNEXPECTED: ' + line)
# #
# #
# # This difference should be ZERO: # # This difference should be ZERO:
# fb_log_diff=os.path.getsize(fb_log_after.name)-os.path.getsize(fb_log_before.name) # fb_log_diff=os.path.getsize(fb_log_after.name)-os.path.getsize(fb_log_before.name)
# #
# if fb_log_diff == 0: # if fb_log_diff == 0:
# print("OK: log was not changed.") # print("OK: log was not changed.")
# else: # else:
# print("BAD: log was increased by "+str(fb_log_diff)+" bytes.") # print("BAD: log was increased by "+str(fb_log_diff)+" bytes.")
# #
# ##################################################################### # #####################################################################
# # Cleanup: # # Cleanup:
# #
# # do NOT remove this pause otherwise some of logs will not be enable for deletion and test will finish with # # do NOT remove this pause otherwise some of logs will not be enable for deletion and test will finish with
# # Exception raised while executing Python test script. exception: WindowsError: 32 # # Exception raised while executing Python test script. exception: WindowsError: 32
# time.sleep(1) # time.sleep(1)
# #
# cleanup( (fb_log_before, fb_log_after, f_sqllog ) ) # cleanup( (fb_log_before, fb_log_after, f_sqllog ) )
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
expected_stdout_1 = """ act_1 = python_act('db_1', substitutions=substitutions_1)
OK: log was not changed.
"""
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action):
def test_1(db_1): script = """
pytest.fail("Test not IMPLEMENTED") set autoddl off;
commit;
set term ^;
create or alter function fn_01() returns int
as begin
return 1;
end
^
create or alter procedure sp_01
as
declare function fn_internal_01 returns int as
begin
if ( fn_01() > 0 ) then return 1;
else return 0;
end
begin
end
^
set term ;^
commit;
"""
with act_1.connect_server() as srv:
srv.info.get_log()
log_before = srv.readlines()
act_1.expected_stdout = ''
act_1.isql(switches=['-q', '-m'], input=script)
with act_1.connect_server() as srv:
srv.info.get_log()
log_after = srv.readlines()
assert act_1.clean_stdout == act_1.clean_expected_stdout
assert list(unified_diff(log_before, log_after)) == []

View File

@ -2,45 +2,34 @@
# #
# id: bugs.core_4503 # id: bugs.core_4503
# title: ISQL command SHOW USERS display only me # title: ISQL command SHOW USERS display only me
# decription: # decription:
# 29.07.2016: instead of issuing SHOW USERS which has unstable output (can be changed multiple times!) # 29.07.2016: instead of issuing SHOW USERS which has unstable output (can be changed multiple times!)
# it was decided to replace it with SQL query which actually is done by this command. # it was decided to replace it with SQL query which actually is done by this command.
# This query can be easily found in trace when we run SHOW USERS. # This query can be easily found in trace when we run SHOW USERS.
# Also, we limit output with only those users who is enumerated here thus one may do not warry about # Also, we limit output with only those users who is enumerated here thus one may do not warry about
# another user logins which could left in securitiN.fdb after some test failed. # another user logins which could left in securitiN.fdb after some test failed.
# #
# 29.03.2018: changed user names, replaced count of SYSDBA attachments with literal 1. # 29.03.2018: changed user names, replaced count of SYSDBA attachments with literal 1.
# Checked on: # Checked on:
# fb30Cs, build 3.0.4.32924: OK, 3.781s. # fb30Cs, build 3.0.4.32924: OK, 3.781s.
# FB30SS, build 3.0.4.32939: OK, 1.312s. # FB30SS, build 3.0.4.32939: OK, 1.312s.
# FB40CS, build 4.0.0.918: OK, 4.547s. # FB40CS, build 4.0.0.918: OK, 4.547s.
# FB40SS, build 4.0.0.943: OK, 2.094s. # FB40SS, build 4.0.0.943: OK, 2.094s.
# #
# tracker_id: CORE-4503 # tracker_id: CORE-4503
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: # qmid:
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, python_act, Action, user_factory, User
# version: 3.0 # version: 3.0
# resources: None # resources: None
substitutions_1 = [] substitutions_1 = []
init_script_1 = """ init_script_1 = ""
create or alter user TMP$C4503_BILL password '123';
create or alter user TMP$C4503_JOHN password '456';
create or alter user TMP$C4503_MICK password '789';
create or alter user TMP$C4503_BOSS password '000';
-- do NOT remove or change name 'test' - it is used in several old tests, see resources/test_user.fbr:
-- core_1083.fbt core_1845.fbt core_1148.fbt core_2729.fbt -- all of them can create user 'TEST' and do not remove it.
create or alter user test password 'test';
drop user test; -- immediatelly remove this name
commit;
"""
db_1 = db_factory(sql_dialect=3, init=init_script_1) db_1 = db_factory(sql_dialect=3, init=init_script_1)
@ -49,46 +38,46 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# import os # import os
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# db_conn.close() # db_conn.close()
# #
# con_0a = kdb.connect(dsn=dsn.encode()) # con_0a = kdb.connect(dsn=dsn.encode())
# con_0b = kdb.connect(dsn=dsn.encode()) # con_0b = kdb.connect(dsn=dsn.encode())
# #
# con_1a = kdb.connect(dsn=dsn.encode(),user='TMP$C4503_BILL',password='123') # con_1a = kdb.connect(dsn=dsn.encode(),user='TMP$C4503_BILL',password='123')
# con_1b = kdb.connect(dsn=dsn.encode(),user='TMP$C4503_BILL',password='123') # con_1b = kdb.connect(dsn=dsn.encode(),user='TMP$C4503_BILL',password='123')
# con_1c = kdb.connect(dsn=dsn.encode(),user='TMP$C4503_BILL',password='123') # con_1c = kdb.connect(dsn=dsn.encode(),user='TMP$C4503_BILL',password='123')
# con_1d = kdb.connect(dsn=dsn.encode(),user='TMP$C4503_BILL',password='123') # con_1d = kdb.connect(dsn=dsn.encode(),user='TMP$C4503_BILL',password='123')
# con_1e = kdb.connect(dsn=dsn.encode(),user='TMP$C4503_BILL',password='123') # con_1e = kdb.connect(dsn=dsn.encode(),user='TMP$C4503_BILL',password='123')
# #
# con_2a = kdb.connect(dsn=dsn.encode(),user='TMP$C4503_JOHN',password='456') # con_2a = kdb.connect(dsn=dsn.encode(),user='TMP$C4503_JOHN',password='456')
# con_2b = kdb.connect(dsn=dsn.encode(),user='TMP$C4503_JOHN',password='456') # con_2b = kdb.connect(dsn=dsn.encode(),user='TMP$C4503_JOHN',password='456')
# con_2c = kdb.connect(dsn=dsn.encode(),user='TMP$C4503_JOHN',password='456') # con_2c = kdb.connect(dsn=dsn.encode(),user='TMP$C4503_JOHN',password='456')
# con_2d = kdb.connect(dsn=dsn.encode(),user='TMP$C4503_JOHN',password='456') # con_2d = kdb.connect(dsn=dsn.encode(),user='TMP$C4503_JOHN',password='456')
# #
# con_3a = kdb.connect(dsn=dsn.encode(),user='TMP$C4503_MICK',password='789') # con_3a = kdb.connect(dsn=dsn.encode(),user='TMP$C4503_MICK',password='789')
# script = ''' # script = '''
# set list on; # set list on;
# -- "SHOW USERS" command actually runs following query: # -- "SHOW USERS" command actually runs following query:
# select # select
# case # case
# when coalesce(mon$user, sec$user_name) = current_user # when coalesce(mon$user, sec$user_name) = current_user
# then '#' # then '#'
# when sec$user_name is distinct from null # when sec$user_name is distinct from null
# then ' ' # then ' '
# else '-' # else '-'
# end is_current_user # end is_current_user
# ,coalesce(m.mon$user, u.sec$user_name) user_name # ,coalesce(m.mon$user, u.sec$user_name) user_name
# ,iif( m.mon$user = upper('SYSDBA'), 1, count(m.mon$user) ) keeps_attachments # ,iif( m.mon$user = upper('SYSDBA'), 1, count(m.mon$user) ) keeps_attachments
# from mon$attachments m # from mon$attachments m
# full join sec$users u on m.mon$user = u.sec$user_name # full join sec$users u on m.mon$user = u.sec$user_name
# where # where
# coalesce(mon$system_flag, 0) = 0 # coalesce(mon$system_flag, 0) = 0
# and coalesce(m.mon$user, u.sec$user_name) in ( upper('TMP$C4503_BILL'), upper('TMP$C4503_BOSS'), upper('TMP$C4503_JOHN'), upper('TMP$C4503_MICK'), upper('SYSDBA') ) # and coalesce(m.mon$user, u.sec$user_name) in ( upper('TMP$C4503_BILL'), upper('TMP$C4503_BOSS'), upper('TMP$C4503_JOHN'), upper('TMP$C4503_MICK'), upper('SYSDBA') )
# group by mon$user, sec$user_name # group by mon$user, sec$user_name
# order by coalesce(mon$user, sec$user_name); # order by coalesce(mon$user, sec$user_name);
# commit; # commit;
# #
# drop user TMP$C4503_BILL; # drop user TMP$C4503_BILL;
# drop user TMP$C4503_JOHN; # drop user TMP$C4503_JOHN;
# drop user TMP$C4503_MICK; # drop user TMP$C4503_MICK;
@ -96,7 +85,8 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# ''' # '''
# runProgram('isql',[dsn,'-q'],script) # runProgram('isql',[dsn,'-q'],script)
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
IS_CURRENT_USER # IS_CURRENT_USER #
@ -118,11 +108,50 @@ expected_stdout_1 = """
IS_CURRENT_USER IS_CURRENT_USER
USER_NAME TMP$C4503_MICK USER_NAME TMP$C4503_MICK
KEEPS_ATTACHMENTS 1 KEEPS_ATTACHMENTS 1
""" """
user_bill = user_factory(name='TMP$C4503_BILL', password='123')
user_john = user_factory(name='TMP$C4503_JOHN', password='456')
user_mick = user_factory(name='TMP$C4503_MICK', password='789')
user_boss = user_factory(name='TMP$C4503_BOSS', password='000')
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action, user_bill: User, user_john: User, user_mick: User, user_boss: User):
def test_1(db_1): with act_1.db.connect() as con_0a, act_1.db.connect(), \
pytest.fail("Test not IMPLEMENTED") act_1.db.connect(user='TMP$C4503_BILL', password='123'), \
act_1.db.connect(user='TMP$C4503_BILL', password='123'), \
act_1.db.connect(user='TMP$C4503_BILL', password='123'), \
act_1.db.connect(user='TMP$C4503_BILL', password='123'), \
act_1.db.connect(user='TMP$C4503_BILL', password='123'), \
act_1.db.connect(user='TMP$C4503_JOHN', password='456'), \
act_1.db.connect(user='TMP$C4503_JOHN', password='456'), \
act_1.db.connect(user='TMP$C4503_JOHN', password='456'), \
act_1.db.connect(user='TMP$C4503_JOHN', password='456'), \
act_1.db.connect(user='TMP$C4503_MICK', password='789'):
#
script = """
set list on;
-- "SHOW USERS" command actually runs following query:
select
case
when coalesce(mon$user, sec$user_name) = current_user
then '#'
when sec$user_name is distinct from null
then ' '
else '-'
end is_current_user
,coalesce(m.mon$user, u.sec$user_name) user_name
,iif( m.mon$user = upper('SYSDBA'), 1, count(m.mon$user) ) keeps_attachments
from mon$attachments m
full join sec$users u on m.mon$user = u.sec$user_name
where
coalesce(mon$system_flag, 0) = 0
and coalesce(m.mon$user, u.sec$user_name) in ( upper('TMP$C4503_BILL'), upper('TMP$C4503_BOSS'), upper('TMP$C4503_JOHN'), upper('TMP$C4503_MICK'), upper('SYSDBA') )
group by mon$user, sec$user_name
order by coalesce(mon$user, sec$user_name);
commit;
"""
act_1.expected_stdout = expected_stdout_1
act_1.isql(switches=['-q'], input=script)
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,20 +2,20 @@
# #
# id: bugs.core_4524 # id: bugs.core_4524
# title: New gbak option to enable encryption during restore # title: New gbak option to enable encryption during restore
# decription: # decription:
# Part of this test was copied from core_6071.fbt. # Part of this test was copied from core_6071.fbt.
# We create new database and try to encrypt it using IBSurgeon Demo Encryption package # We create new database and try to encrypt it using IBSurgeon Demo Encryption package
# ( https://ib-aid.com/download-demo-firebird-encryption-plugin/ ; https://ib-aid.com/download/crypt/CryptTest.zip ) # ( https://ib-aid.com/download-demo-firebird-encryption-plugin/ ; https://ib-aid.com/download/crypt/CryptTest.zip )
# License file plugins\\dbcrypt.conf with unlimited expiration was provided by IBSurgeon to Firebird Foundation (FF). # License file plugins\\dbcrypt.conf with unlimited expiration was provided by IBSurgeon to Firebird Foundation (FF).
# This file was preliminary stored in FF Test machine. # This file was preliminary stored in FF Test machine.
# Test assumes that this file and all neccessary libraries already were stored into FB_HOME and %FB_HOME%\\plugins. # Test assumes that this file and all neccessary libraries already were stored into FB_HOME and %FB_HOME%\\plugins.
# #
# We create several generators in the test DB and get number of generators page using query to RDB$PAGES (page_type=9). # We create several generators in the test DB and get number of generators page using query to RDB$PAGES (page_type=9).
# Also we get page_size and using these data we can obtain binary content of generatord page. This content futher is parsed # Also we get page_size and using these data we can obtain binary content of generatord page. This content futher is parsed
# in order to verify that generators names are readable (while DB is not yet encrypted). # in order to verify that generators names are readable (while DB is not yet encrypted).
# #
# Then we encrypt DB and make delay after this for ~1..2 seconds BEFORE detach from database. # Then we encrypt DB and make delay after this for ~1..2 seconds BEFORE detach from database.
# #
# After this we: # After this we:
# 1. Change temp DB state to full shutdown and bring it online - in order to be sure that we will able to drop this file later; # 1. Change temp DB state to full shutdown and bring it online - in order to be sure that we will able to drop this file later;
# 2. Make backup of this temp DB, using gbak utility and '-KEYHOLDER <name_of_key_holder>' command switch. # 2. Make backup of this temp DB, using gbak utility and '-KEYHOLDER <name_of_key_holder>' command switch.
@ -23,17 +23,19 @@
# 4. Make validation of just restored database by issuing command "gfix -v -full ..." # 4. Make validation of just restored database by issuing command "gfix -v -full ..."
# ( i.e. validate both data and metadata rather than online val which can check user data only). # ( i.e. validate both data and metadata rather than online val which can check user data only).
# 5. Open restored DB as binary file and attempt to read again generators names - this must fail, their names must be encrypted. # 5. Open restored DB as binary file and attempt to read again generators names - this must fail, their names must be encrypted.
# 6. Check that NO errors occured on any above mentioned steps. Also check that backup and restore STDOUT logs contain expected # 6. Check that NO errors occured on any above mentioned steps. Also check that backup and restore STDOUT logs contain expected
# text about successful completition # text about successful completition
# #
# 13.04.2021. Adapted for run both on Windows and Linux. Checked on: # 13.04.2021. Adapted for run both on Windows and Linux. Checked on:
# Windows: 4.0.0.2416 # Windows: 4.0.0.2416
# Linux: 4.0.0.2416 # Linux: 4.0.0.2416
# Note: different names for encryption plugin and keyholde rare used for Windows vs Linux: # Note: different names for encryption plugin and keyholde rare used for Windows vs Linux:
# PLUGIN_NAME = 'dbcrypt' if os.name == 'nt' else '"fbSampleDbCrypt"' # PLUGIN_NAME = 'dbcrypt' if os.name == 'nt' else '"fbSampleDbCrypt"'
# KHOLDER_NAME = 'KeyHolder' if os.name == 'nt' else "fbSampleKeyHolder" # KHOLDER_NAME = 'KeyHolder' if os.name == 'nt' else "fbSampleKeyHolder"
# #
# #
# [pcisar] 23.11.2021
# Test not implemented because it depends on 3rd party encryption plugin.
# tracker_id: CORE-4524 # tracker_id: CORE-4524
# min_versions: ['4.0'] # min_versions: ['4.0']
# versions: 4.0 # versions: 4.0
@ -53,35 +55,35 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# import time # import time
# import subprocess # import subprocess
# import binascii # import binascii
# import re # import re
# import fdb # import fdb
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# db_conn.close() # db_conn.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close( file_handle ): # def flush_and_close( file_handle ):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull: # if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull:
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -93,56 +95,56 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.') # print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.')
# print('type(f_names_list[i])=',type(f_names_list[i])) # print('type(f_names_list[i])=',type(f_names_list[i]))
# del_name = None # del_name = None
# #
# if del_name and os.path.isfile( del_name ): # if del_name and os.path.isfile( del_name ):
# os.remove( del_name ) # os.remove( del_name )
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def check_page_for_readable_values(dbname, gen_page_number, pg_size, check_sequence_values): # def check_page_for_readable_values(dbname, gen_page_number, pg_size, check_sequence_values):
# #
# global binascii # global binascii
# #
# db_handle = open( dbname, "rb") # db_handle = open( dbname, "rb")
# db_handle.seek( gen_page_number * pg_size ) # db_handle.seek( gen_page_number * pg_size )
# page_content = db_handle.read( pg_size ) # page_content = db_handle.read( pg_size )
# # read_binary_content( db_handle, gen_page_number * pg_size, pg_size ) # # read_binary_content( db_handle, gen_page_number * pg_size, pg_size )
# db_handle.close() # db_handle.close()
# page_as_hex=binascii.hexlify( page_content ) # page_as_hex=binascii.hexlify( page_content )
# #
# # Iterate for each sequence value: # # Iterate for each sequence value:
# for n in check_sequence_values: # for n in check_sequence_values:
# #
# # Get HEX representation of digital value. # # Get HEX representation of digital value.
# # NOTE: format( 830624, 'x') is 'caca0' contains five (odd number!) characters. # # NOTE: format( 830624, 'x') is 'caca0' contains five (odd number!) characters.
# hex_string = format(abs(n),'x') # hex_string = format(abs(n),'x')
# #
# # Here we 'pad' hex representation to EVEN number of digits in it, # # Here we 'pad' hex representation to EVEN number of digits in it,
# # otherwise binascii.hexlify fails with "Odd-length string error": # # otherwise binascii.hexlify fails with "Odd-length string error":
# hex_string = ''.join( ('0' * ( len(hex_string)%2 ), hex_string ) ) # hex_string = ''.join( ('0' * ( len(hex_string)%2 ), hex_string ) )
# #
# # ::: NOTE ::: # # ::: NOTE :::
# # Generator value is stored in REVERSED bytes order. # # Generator value is stored in REVERSED bytes order.
# # dec 830624 --> hex 0x0caca0 --> 0c|ac|a0 --> stored in page as three bytes: {a0; ac; 0c} # # dec 830624 --> hex 0x0caca0 --> 0c|ac|a0 --> stored in page as three bytes: {a0; ac; 0c}
# #
# # Decode string that is stored in variable 'hex_string' to HEX number, # # Decode string that is stored in variable 'hex_string' to HEX number,
# # REVERSE its bytes and convert it to string again for further search # # REVERSE its bytes and convert it to string again for further search
# # in page content: # # in page content:
# n_as_reversed_hex = binascii.hexlify( hex_string.decode('hex')[::-1] ) # n_as_reversed_hex = binascii.hexlify( hex_string.decode('hex')[::-1] )
# #
# print(n, n_as_reversed_hex, 'FOUND.' if n_as_reversed_hex in page_as_hex else 'NOT FOUND.' ) # print(n, n_as_reversed_hex, 'FOUND.' if n_as_reversed_hex in page_as_hex else 'NOT FOUND.' )
# # print(n, n_as_reversed_hex, 'UNEXPECTEDLY FOUND AT POS. ' + '{:5d}'.format( page_as_hex.index(n_as_reversed_hex) ) if n_as_reversed_hex in page_as_hex else 'Not found (expected).' ) # # print(n, n_as_reversed_hex, 'UNEXPECTEDLY FOUND AT POS. ' + '{:5d}'.format( page_as_hex.index(n_as_reversed_hex) ) if n_as_reversed_hex in page_as_hex else 'Not found (expected).' )
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# tmpfdb='$(DATABASE_LOCATION)'+'tmp_core_4524.encrypted.fdb' # tmpfdb='$(DATABASE_LOCATION)'+'tmp_core_4524.encrypted.fdb'
# tmpres='$(DATABASE_LOCATION)'+'tmp_core_4524.restored.fdb' # tmpres='$(DATABASE_LOCATION)'+'tmp_core_4524.restored.fdb'
# tmpbkp='$(DATABASE_LOCATION)'+'tmp_core_4524.encrypted.fbk' # tmpbkp='$(DATABASE_LOCATION)'+'tmp_core_4524.encrypted.fbk'
# #
# cleanup( (tmpfdb, tmpres) ) # cleanup( (tmpfdb, tmpres) )
# #
# con = fdb.create_database( dsn = 'localhost:'+tmpfdb ) # con = fdb.create_database( dsn = 'localhost:'+tmpfdb )
# #
# con.execute_immediate('create sequence gen_ba0bab start with 12192683') # con.execute_immediate('create sequence gen_ba0bab start with 12192683')
# con.execute_immediate('create sequence gen_badf00d start with 195948557') # con.execute_immediate('create sequence gen_badf00d start with 195948557')
# con.execute_immediate('create sequence gen_caca0 start with 830624') # con.execute_immediate('create sequence gen_caca0 start with 830624')
@ -151,7 +153,7 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# con.execute_immediate('create sequence gen_decade start with 14600926') # con.execute_immediate('create sequence gen_decade start with 14600926')
# con.execute_immediate('create sequence gen_7FFFFFFF start with 2147483647') # con.execute_immediate('create sequence gen_7FFFFFFF start with 2147483647')
# con.commit() # con.commit()
# #
# cur=con.cursor() # cur=con.cursor()
# get_current_seq_values=''' # get_current_seq_values='''
# execute block returns( gen_curr bigint) as # execute block returns( gen_curr bigint) as
@ -166,7 +168,7 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# end # end
# end # end
# ''' # '''
# #
# # Obtain current values of user generators: # # Obtain current values of user generators:
# # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# cur.execute(get_current_seq_values) # cur.execute(get_current_seq_values)
@ -174,8 +176,8 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# for r in cur: # for r in cur:
# check_sequence_values += r[0], # check_sequence_values += r[0],
# #print('check_sequence_values=',check_sequence_values) # #print('check_sequence_values=',check_sequence_values)
# #
# #
# # Obtain page size and number of generators page: # # Obtain page size and number of generators page:
# # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# cur.execute('select m.mon$page_size,min(rdb$page_number) from mon$database m cross join rdb$pages p where p.rdb$page_type = 9 group by 1') # cur.execute('select m.mon$page_size,min(rdb$page_number) from mon$database m cross join rdb$pages p where p.rdb$page_type = 9 group by 1')
@ -185,17 +187,17 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# gen_page_number=r[1] # gen_page_number=r[1]
# # print(r[0],r[1]) # # print(r[0],r[1])
# cur.close() # cur.close()
# #
# #
# # Read gen page, convert it to hex and check whether generator values can be found there or no: # # Read gen page, convert it to hex and check whether generator values can be found there or no:
# # Expected result: YES for all values because DB not encrypted now. # # Expected result: YES for all values because DB not encrypted now.
# # ~~~~~~~~~~~~~~~ # # ~~~~~~~~~~~~~~~
# check_page_for_readable_values(tmpfdb, gen_page_number, pg_size, check_sequence_values) # check_page_for_readable_values(tmpfdb, gen_page_number, pg_size, check_sequence_values)
# #
# ################################################ # ################################################
# ### e n c r y p t d a t a b a s e ### # ### e n c r y p t d a t a b a s e ###
# ################################################ # ################################################
# #
# # 14.04.2021. # # 14.04.2021.
# # Name of encryption plugin depends on OS: # # Name of encryption plugin depends on OS:
# # * for Windows we (currently) use plugin by IBSurgeon, its name is 'dbcrypt'; # # * for Windows we (currently) use plugin by IBSurgeon, its name is 'dbcrypt';
@ -212,7 +214,7 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# time.sleep(2) # time.sleep(2)
# # ^ # # ^
# # +-------- !! ALLOW BACKGROUND ENCRYPTION PROCESS TO COMPLETE ITS JOB !! # # +-------- !! ALLOW BACKGROUND ENCRYPTION PROCESS TO COMPLETE ITS JOB !!
# #
# ####################################### # #######################################
# # Added 14.04.2021: check that database is actually encrypted. # # Added 14.04.2021: check that database is actually encrypted.
# # Column MON$DATABASE.MON$CRYPT_STATE can have following values: # # Column MON$DATABASE.MON$CRYPT_STATE can have following values:
@ -226,9 +228,9 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# print( r[0] ) # print( r[0] )
# con.close() # con.close()
# cur.close() # cur.close()
# #
# #-------------------------- shutdown temp DB and bring it online -------------------- # #-------------------------- shutdown temp DB and bring it online --------------------
# #
# f_dbshut_log = open( os.path.join(context['temp_directory'],'tmp_dbshut_4524.log'), 'w') # f_dbshut_log = open( os.path.join(context['temp_directory'],'tmp_dbshut_4524.log'), 'w')
# subprocess.call( [ context['gfix_path'], 'localhost:'+tmpfdb, "-shut", "full", "-force", "0" ], # subprocess.call( [ context['gfix_path'], 'localhost:'+tmpfdb, "-shut", "full", "-force", "0" ],
# stdout = f_dbshut_log, # stdout = f_dbshut_log,
@ -239,13 +241,13 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# stderr = subprocess.STDOUT # stderr = subprocess.STDOUT
# ) # )
# flush_and_close( f_dbshut_log ) # flush_and_close( f_dbshut_log )
# #
# #--------------------------- backup and restore -------------------------------------- # #--------------------------- backup and restore --------------------------------------
# fn_bkp_log=open( os.path.join(context['temp_directory'],'tmp_backup_4524.log'), 'w') # fn_bkp_log=open( os.path.join(context['temp_directory'],'tmp_backup_4524.log'), 'w')
# fn_bkp_err=open( os.path.join(context['temp_directory'],'tmp_backup_4524.err'), 'w') # fn_bkp_err=open( os.path.join(context['temp_directory'],'tmp_backup_4524.err'), 'w')
# #
# # /var/tmp/fb40tmp/bin/gbak -b -v -keyholder fbSampleKeyHolder -crypt fbSampleDbCrypt localhost:/path/to/encrypted.fdb /path/to/encrypted.fbk # # /var/tmp/fb40tmp/bin/gbak -b -v -keyholder fbSampleKeyHolder -crypt fbSampleDbCrypt localhost:/path/to/encrypted.fdb /path/to/encrypted.fbk
# #
# subprocess.call([ context['gbak_path'] # subprocess.call([ context['gbak_path']
# ,"-b" # ,"-b"
# ,"-v" # ,"-v"
@ -255,14 +257,14 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# ,tmpbkp # ,tmpbkp
# ], # ],
# stdout=fn_bkp_log, stderr=fn_bkp_err) # stdout=fn_bkp_log, stderr=fn_bkp_err)
# #
# flush_and_close( fn_bkp_log ) # flush_and_close( fn_bkp_log )
# flush_and_close( fn_bkp_err ) # flush_and_close( fn_bkp_err )
# #
# #
# fn_res_log=open( os.path.join(context['temp_directory'],'tmp_restore_4524.log'), 'w') # fn_res_log=open( os.path.join(context['temp_directory'],'tmp_restore_4524.log'), 'w')
# fn_res_err=open( os.path.join(context['temp_directory'],'tmp_restore_4524.err'), 'w') # fn_res_err=open( os.path.join(context['temp_directory'],'tmp_restore_4524.err'), 'w')
# #
# # C:\\FB SS\\gbak.exe -rep -KEYHOLDER KeyHolder C:\\FBTESTING\\qa\\misc\\C4524.fbk /:C:\\FBTESTING\\qa\\misc\\c4524.restored.FDB # # C:\\FB SS\\gbak.exe -rep -KEYHOLDER KeyHolder C:\\FBTESTING\\qa\\misc\\C4524.fbk /:C:\\FBTESTING\\qa\\misc\\c4524.restored.FDB
# subprocess.call([ context['gbak_path'] # subprocess.call([ context['gbak_path']
# ,"-rep" # ,"-rep"
@ -272,51 +274,51 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# ,'localhost:' + tmpres # ,'localhost:' + tmpres
# ], # ],
# stdout=fn_res_log, stderr=fn_res_err) # stdout=fn_res_log, stderr=fn_res_err)
# #
# flush_and_close( fn_res_log ) # flush_and_close( fn_res_log )
# flush_and_close( fn_res_err ) # flush_and_close( fn_res_err )
# #
# #-------------------------- validate just restored database -------------------- # #-------------------------- validate just restored database --------------------
# #
# f_valid_log = open( os.path.join(context['temp_directory'],'tmp_valid_4524.log'), 'w') # f_valid_log = open( os.path.join(context['temp_directory'],'tmp_valid_4524.log'), 'w')
# subprocess.call( [ context['gfix_path'], 'localhost:'+tmpres, "-v", "-full" ], # subprocess.call( [ context['gfix_path'], 'localhost:'+tmpres, "-v", "-full" ],
# stdout = f_valid_log, # stdout = f_valid_log,
# stderr = subprocess.STDOUT # stderr = subprocess.STDOUT
# ) # )
# flush_and_close( f_valid_log ) # flush_and_close( f_valid_log )
# #
# #----------------------------------------------- # #-----------------------------------------------
# #
# # Read gen page in RESTORED database, convert it to hex and check whether generator values can be found there or no. # # Read gen page in RESTORED database, convert it to hex and check whether generator values can be found there or no.
# # Expected result: NOT for all values because DB was encrypted. # # Expected result: NOT for all values because DB was encrypted.
# # ~~~~~~~~~~~~~~~~ # # ~~~~~~~~~~~~~~~~
# check_page_for_readable_values(tmpres, gen_page_number, pg_size, check_sequence_values) # check_page_for_readable_values(tmpres, gen_page_number, pg_size, check_sequence_values)
# #
# #----------------------------------------------- # #-----------------------------------------------
# #
# # Check that all was fine: # # Check that all was fine:
# #
# with open(f_dbshut_log.name,'r') as f: # with open(f_dbshut_log.name,'r') as f:
# for line in f: # for line in f:
# if line.split(): # if line.split():
# print('UNEXPECTED SHUTDOWN OUTPUT: ' + line) # print('UNEXPECTED SHUTDOWN OUTPUT: ' + line)
# #
# with open(fn_bkp_err.name,'r') as f: # with open(fn_bkp_err.name,'r') as f:
# for line in f: # for line in f:
# if line.split(): # if line.split():
# print('UNEXPECTED BACKUP STDERR: ' + line) # print('UNEXPECTED BACKUP STDERR: ' + line)
# #
# with open(fn_res_err.name,'r') as f: # with open(fn_res_err.name,'r') as f:
# for line in f: # for line in f:
# if line.split(): # if line.split():
# print('UNEXPECTED RESTORE STDERR: ' + line) # print('UNEXPECTED RESTORE STDERR: ' + line)
# #
# with open(f_dbshut_log.name,'r') as f: # with open(f_dbshut_log.name,'r') as f:
# for line in f: # for line in f:
# if line.split(): # if line.split():
# print('UNEXPECTED VALIDATION OUTPUT: ' + line) # print('UNEXPECTED VALIDATION OUTPUT: ' + line)
# #
# #
# # gbak -b should finish with line: # # gbak -b should finish with line:
# # gbak:closing file, committing, and finishing. 512 bytes written # # gbak:closing file, committing, and finishing. 512 bytes written
# gbak_backup_finish_ptn=re.compile('gbak:closing\\s+file,\\s+committing,\\s+and\\s+finishing.*', re.IGNORECASE) # gbak_backup_finish_ptn=re.compile('gbak:closing\\s+file,\\s+committing,\\s+and\\s+finishing.*', re.IGNORECASE)
@ -324,26 +326,26 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# for line in f: # for line in f:
# if gbak_backup_finish_ptn.search(line): # if gbak_backup_finish_ptn.search(line):
# print('EXPECTED BACKUP FINISH FOUND: '+line.upper() ) # print('EXPECTED BACKUP FINISH FOUND: '+line.upper() )
# #
# #
# # gbak -c should finish with lines: # # gbak -c should finish with lines:
# # gbak:finishing, closing, and going home # # gbak:finishing, closing, and going home
# # gbak:adjusting the ONLINE and FORCED WRITES flags # # gbak:adjusting the ONLINE and FORCED WRITES flags
# #
# gbak_restore_finish_ptn=re.compile('gbak:adjusting\\s+the\\s+ONLINE\\s+and\\s+FORCED\\s+WRITES\\s+.*', re.IGNORECASE) # gbak_restore_finish_ptn=re.compile('gbak:adjusting\\s+the\\s+ONLINE\\s+and\\s+FORCED\\s+WRITES\\s+.*', re.IGNORECASE)
# with open(fn_res_log.name,'r') as f: # with open(fn_res_log.name,'r') as f:
# for line in f: # for line in f:
# if gbak_restore_finish_ptn.search(line): # if gbak_restore_finish_ptn.search(line):
# print('EXPECTED RESTORE FINISH FOUND: '+line.upper() ) # print('EXPECTED RESTORE FINISH FOUND: '+line.upper() )
# #
# #
# # cleanup # # cleanup
# ########## # ##########
# time.sleep(1) # time.sleep(1)
# f_list = [ i.name for i in ( f_dbshut_log, fn_bkp_log, fn_bkp_err, fn_res_log, fn_res_err, f_valid_log ) ] + [ tmpfdb, tmpres, tmpbkp ] # f_list = [ i.name for i in ( f_dbshut_log, fn_bkp_log, fn_bkp_err, fn_res_log, fn_res_err, f_valid_log ) ] + [ tmpfdb, tmpres, tmpbkp ]
# cleanup( f_list ) # cleanup( f_list )
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1) #act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
@ -367,11 +369,9 @@ expected_stdout_1 = """
2147483646 feffff7f NOT FOUND. 2147483646 feffff7f NOT FOUND.
EXPECTED BACKUP FINISH FOUND: GBAK:CLOSING FILE, COMMITTING, AND FINISHING. EXPECTED BACKUP FINISH FOUND: GBAK:CLOSING FILE, COMMITTING, AND FINISHING.
EXPECTED RESTORE FINISH FOUND: GBAK:ADJUSTING THE ONLINE AND FORCED WRITES FLAGS EXPECTED RESTORE FINISH FOUND: GBAK:ADJUSTING THE ONLINE AND FORCED WRITES FLAGS
""" """
@pytest.mark.version('>=4.0') @pytest.mark.version('>=4.0')
@pytest.mark.xfail
def test_1(db_1): def test_1(db_1):
pytest.fail("Test not IMPLEMENTED") pytest.skip("Test requires 3rd party encryption plugin")
#pytest.fail("Test not IMPLEMENTED")

View File

@ -2,7 +2,7 @@
# #
# id: bugs.core_4582 # id: bugs.core_4582
# title: Within linger period one can not change some database properties # title: Within linger period one can not change some database properties
# decription: # decription:
# Confirmed on WI-T3.0.0.31374 Beta 1: running GFIX -buffers N has NO affect if this is done within linger period # Confirmed on WI-T3.0.0.31374 Beta 1: running GFIX -buffers N has NO affect if this is done within linger period
# Results for 22.05.2017: # Results for 22.05.2017:
# fb30Cs, build 3.0.3.32725: OK, 4.703ss. # fb30Cs, build 3.0.3.32725: OK, 4.703ss.
@ -11,19 +11,25 @@
# FB40CS, build 4.0.0.645: OK, 5.047ss. # FB40CS, build 4.0.0.645: OK, 5.047ss.
# FB40SC, build 4.0.0.645: OK, 2.703ss. # FB40SC, build 4.0.0.645: OK, 2.703ss.
# FB40SS, build 4.0.0.645: OK, 2.187ss. # FB40SS, build 4.0.0.645: OK, 2.187ss.
# #
# [pcisar] 23.11.2021
# This test FAILs on v4.0.0.2496 as database couldn't be reverted to online state, not yet tested with 3.0
# tracker_id: CORE-4582 # tracker_id: CORE-4582
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: # qmid:
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, python_act, Action
from firebird.driver import DbWriteMode, DbAccessMode, ShutdownMode, ShutdownMethod, \
SrvStatFlag
# version: 3.0 # version: 3.0
# resources: None # resources: None
substitutions_1 = [('^((?!:::MSG:::|buffers|before|after|Attributes).)*$', ''), ('Page buffers([\t]|[ ])+', 'Page buffers '), ('Attributes([\t]|[ ])+', 'Attributes '), ('N/A', 'YES')] substitutions_1 = [('^((?!:::MSG:::|buffers|before|after|Attributes).)*$', ''),
('Page buffers([\t]|[ ])+', 'Page buffers '),
('Attributes([\t]|[ ])+', 'Attributes '), ('N/A', 'YES')]
init_script_1 = """ init_script_1 = """
-- Result of this test on WI-T3.0.0.31374 Beta 1: -- Result of this test on WI-T3.0.0.31374 Beta 1:
@ -40,10 +46,10 @@ init_script_1 = """
create or alter procedure sp_get_buff returns(mon_buffers int) as create or alter procedure sp_get_buff returns(mon_buffers int) as
begin begin
mon_buffers = -1; mon_buffers = -1;
if ( exists( select * from mon$attachments where mon$user containing 'cache writer' and mon$system_flag = 1 ) ) if (exists(select * from mon$attachments where mon$user containing 'cache writer' and mon$system_flag = 1))
then then
select mon$page_buffers from mon$database into mon_buffers; select mon$page_buffers from mon$database into mon_buffers;
suspend; suspend;
end end
@ -52,31 +58,31 @@ init_script_1 = """
commit; commit;
recreate table log(buf_before int, buf_after int); recreate table log(buf_before int, buf_after int);
commit; commit;
""" """
db_1 = db_factory(sql_dialect=3, init=init_script_1) db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# db_conn.close() # db_conn.close()
# #
# script=''' # script='''
# insert into log(buf_before) select mon_buffers from sp_get_buff; # insert into log(buf_before) select mon_buffers from sp_get_buff;
# commit; # commit;
# alter database set linger to 15; # alter database set linger to 15;
# commit; # commit;
# set list on; # set list on;
# select rdb$linger as ":::MSG::: linger_time" from rdb$database; # select rdb$linger as ":::MSG::: linger_time" from rdb$database;
# ''' # '''
# #
# print (':::MSG::: Starting ISQL setting new value for linger...') # print (':::MSG::: Starting ISQL setting new value for linger...')
# runProgram('isql',[dsn],script) # runProgram('isql',[dsn],script)
# print (':::MSG::: ISQL setting new value for linger finished.') # print (':::MSG::: ISQL setting new value for linger finished.')
# #
# print (':::MSG::: Starting GFIX setting new value for page buffers...') # print (':::MSG::: Starting GFIX setting new value for page buffers...')
# runProgram('gfix',[dsn,'-buffers','3791']) # runProgram('gfix',[dsn,'-buffers','3791'])
# runProgram('gfix',[dsn,'-w','async']) # runProgram('gfix',[dsn,'-w','async'])
@ -88,20 +94,21 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# print (':::MSG::: GFIX setting new value for page buffers finished.') # print (':::MSG::: GFIX setting new value for page buffers finished.')
# print (':::MSG::: Starting ISQL for extract old and new value of page buffers...') # print (':::MSG::: Starting ISQL for extract old and new value of page buffers...')
# script=''' # script='''
# set list on; # set list on;
# update log set buf_after = (select mon_buffers from sp_get_buff); # update log set buf_after = (select mon_buffers from sp_get_buff);
# commit; # commit;
# select iif( g.buf_before > 0, # select iif( g.buf_before > 0,
# iif( g.buf_before is distinct from g.buf_after, 'YES', 'NO!' ), # iif( g.buf_before is distinct from g.buf_after, 'YES', 'NO!' ),
# 'N/A' # 'N/A'
# ) as "GFIX could change buffers ? =>" # ) as "GFIX could change buffers ? =>"
# from log g; # from log g;
# ''' # '''
# runProgram('isql',[dsn],script) # runProgram('isql',[dsn],script)
# print (':::MSG::: ISQL for extract old and new value of page finished.') # print (':::MSG::: ISQL for extract old and new value of page finished.')
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
:::MSG::: Starting ISQL setting new value for linger... :::MSG::: Starting ISQL setting new value for linger...
@ -114,11 +121,64 @@ expected_stdout_1 = """
:::MSG::: Starting ISQL for extract old and new value of page buffers... :::MSG::: Starting ISQL for extract old and new value of page buffers...
GFIX could change buffers ? => YES GFIX could change buffers ? => YES
:::MSG::: ISQL for extract old and new value of page finished. :::MSG::: ISQL for extract old and new value of page finished.
""" """
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action, capsys):
def test_1(db_1): script_1 = """
pytest.fail("Test not IMPLEMENTED") insert into log(buf_before) select mon_buffers from sp_get_buff;
commit;
alter database set linger to 15;
commit;
set list on;
select rdb$linger as ":::MSG::: linger_time" from rdb$database;
"""
print (':::MSG::: Starting ISQL setting new value for linger...')
act_1.isql(switches=[], input=script_1)
print (':::MSG::: ISQL setting new value for linger finished.')
print (':::MSG::: Starting GFIX setting new value for page buffers...')
#with act_1.connect_server() as srv:
#srv.database.set_default_cache_size(database=str(act_1.db.db_path), size=3791)
#srv.database.set_write_mode(database=str(act_1.db.db_path), mode=DbWriteMode.ASYNC)
#srv.database.set_access_mode(database=str(act_1.db.db_path), mode=DbAccessMode.READ_ONLY)
#srv.database.shutdown(database=str(act_1.db.db_path), mode=ShutdownMode.SINGLE,
#method=ShutdownMethod.DENNY_ATTACHMENTS, timeout=20)
#srv.database.get_statistics(database=str(act_1.db.db_path), flags=SrvStatFlag.HDR_PAGES,
#callback=print)
#srv.database.bring_online(database=str(act_1.db.db_path))
#srv.database.set_access_mode(database=str(act_1.db.db_path), mode=DbAccessMode.READ_WRITE)
act_1.reset()
act_1.gfix(switches=['-buffers', '3791', act_1.db.dsn])
act_1.reset()
act_1.gfix(switches=['-write','async', act_1.db.dsn])
act_1.reset()
act_1.gfix(switches=['-mode','read_only', act_1.db.dsn])
act_1.reset()
act_1.gfix(switches=['-shutdown','single', '-at', '20', act_1.db.dsn])
act_1.reset()
act_1.gstat(switches=['-h'])
print(act_1.stdout)
act_1.reset()
act_1.gfix(switches=[act_1.db.dsn, '-online'])
act_1.reset()
act_1.gfix(switches=['-mode','read_write', act_1.db.dsn])
print (':::MSG::: GFIX setting new value for page buffers finished.')
print (':::MSG::: Starting ISQL for extract old and new value of page buffers...')
script_2 = """
set list on;
update log set buf_after = (select mon_buffers from sp_get_buff);
commit;
select iif( g.buf_before > 0,
iif( g.buf_before is distinct from g.buf_after, 'YES', 'NO!' ),
'N/A'
) as "GFIX could change buffers ? =>"
from log g;
"""
act_1.reset()
act_1.isql(switches=[], input=script_2)
print(act_1.stdout)
print (':::MSG::: ISQL for extract old and new value of page finished.')
# Check
act_1.expected_stdout = expected_stdout_1
act_1.stdout = capsys.readouterr().out
assert act_1.clean_expected_stdout == act_1.clean_expected_stdout

View File

@ -2,7 +2,7 @@
# #
# id: bugs.core_4645 # id: bugs.core_4645
# title: internal Firebird consistency check (cannot find tip page (165), file: tra.cpp line: 2375) # title: internal Firebird consistency check (cannot find tip page (165), file: tra.cpp line: 2375)
# decription: # decription:
# Both STDOUT and STDERR in this test should be empty. # Both STDOUT and STDERR in this test should be empty.
# Confirmed: # Confirmed:
# 1) bugcheck exception on 3.0.0.32378; 4.0.0.98: # 1) bugcheck exception on 3.0.0.32378; 4.0.0.98:
@ -11,14 +11,15 @@
# Statement failed, SQLSTATE = XX000 # Statement failed, SQLSTATE = XX000
# internal Firebird consistency check (can't continue after bugcheck) # internal Firebird consistency check (can't continue after bugcheck)
# 2) normal work on 3.0.0.32471; 4.0.0.127. # 2) normal work on 3.0.0.32471; 4.0.0.127.
# #
# tracker_id: CORE-4645 # tracker_id: CORE-4645
# min_versions: ['2.5'] # min_versions: ['2.5']
# versions: 2.5.6 # versions: 2.5.6
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, python_act, Action
from firebird.driver import DbAccessMode
# version: 2.5.6 # version: 2.5.6
# resources: None # resources: None
@ -31,7 +32,7 @@ db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# db_conn.close() # db_conn.close()
# runProgram('gfix',[dsn,'-user',user_name,'-pas',user_password,'-mode','read_only']) # runProgram('gfix',[dsn,'-user',user_name,'-pas',user_password,'-mode','read_only'])
# script=''' # script='''
@ -41,7 +42,7 @@ db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1)
# execute block as # execute block as
# declare n int = 20000; # declare n int = 20000;
# begin # begin
# while (n>0) do # while (n>0) do
# in autonomous transaction do # in autonomous transaction do
# select :n-1 from rdb$database into n; # select :n-1 from rdb$database into n;
# end # end
@ -50,14 +51,30 @@ db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1)
# commit; # commit;
# ''' # '''
# runProgram('isql',[dsn,'-user',user_name,'-password',user_password],script) # runProgram('isql',[dsn,'-user',user_name,'-password',user_password],script)
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
@pytest.mark.version('>=2.5.6') @pytest.mark.version('>=2.5.6')
@pytest.mark.xfail def test_1(act_1: Action):
def test_1(db_1): with act_1.connect_server() as srv:
pytest.fail("Test not IMPLEMENTED") srv.database.set_access_mode(database=str(act_1.db.db_path), mode=DbAccessMode.READ_ONLY)
script = """
commit;
set transaction read committed;
set term ^;
execute block as
declare n int = 20000;
begin
while (n>0) do
in autonomous transaction do
select :n-1 from rdb$database into n;
end
^
set term ;^
commit;
"""
act_1.isql(switches=[], input=script)
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,37 +2,42 @@
# #
# id: bugs.core_4648 # id: bugs.core_4648
# title: no permission for CREATE access to DATABASE (for RDB$ADMIN) # title: no permission for CREATE access to DATABASE (for RDB$ADMIN)
# decription: # decription:
# tracker_id: CORE-4648 # tracker_id: CORE-4648
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: # qmid:
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from io import BytesIO
from pathlib import Path
from firebird.qa import db_factory, python_act, Action, user_factory, User, temp_file
from firebird.driver import SrvRestoreFlag
# version: 3.0 # version: 3.0
# resources: None # resources: None
substitutions_1 = [] substitutions_1 = []
init_script_1 = """ init_script_1 = ""
set wng off;
create or alter user tmp$c4648 password '123' revoke admin role; #init_script_1 = """
commit; #set wng off;
revoke all on all from tmp$c4648; #create or alter user tmp$c4648 password '123' revoke admin role;
-- 'create ... grant admin role' or 'grant rdb$admin' are NOT neccessary #commit;
-- for enabling to creating database by non-sysdba user: #revoke all on all from tmp$c4648;
grant create database to user tmp$c4648; #-- 'create ... grant admin role' or 'grant rdb$admin' are NOT neccessary
commit; #-- for enabling to creating database by non-sysdba user:
""" #grant create database to user tmp$c4648;
#commit;
#"""
db_1 = db_factory(sql_dialect=3, init=init_script_1) db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# import os # import os
# #
# print ('Starting backup...') # print ('Starting backup...')
# fbk = os.path.join(context['temp_directory'],'tmp4648.fbk') # fbk = os.path.join(context['temp_directory'],'tmp4648.fbk')
# fdn = 'localhost:'+os.path.join(context['temp_directory'],'tmp4648.tmp') # fdn = 'localhost:'+os.path.join(context['temp_directory'],'tmp4648.tmp')
@ -45,15 +50,15 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# print ('Delete backup file...') # print ('Delete backup file...')
# os.remove(fbk) # os.remove(fbk)
# print ('Backup file deleted.') # print ('Backup file deleted.')
# #
# script = ''' # script = '''
# set list on; # set list on;
# select # select
# a.mon$user as "Who am I:" # a.mon$user as "Who am I:"
# ,left(a.mon$remote_protocol,3) as "Used protocol:" # ,left(a.mon$remote_protocol,3) as "Used protocol:"
# ,iif(m.mon$database_name containing 'tmp4648.tmp','YES','NO! ' || m.mon$database_name ) as "Connected to restored DB ?" # ,iif(m.mon$database_name containing 'tmp4648.tmp','YES','NO! ' || m.mon$database_name ) as "Connected to restored DB ?"
# ,m.mon$owner as "Owner of DB is:" # ,m.mon$owner as "Owner of DB is:"
# from mon$attachments a, mon$database m # from mon$attachments a, mon$database m
# where a.mon$attachment_id=current_connection; # where a.mon$attachment_id=current_connection;
# commit; # commit;
# drop database; # drop database;
@ -61,34 +66,71 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# print ('Starting ISQL using NON sysdba user account...') # print ('Starting ISQL using NON sysdba user account...')
# runProgram('isql',[fdn,'-q','-user','tmp$c4648','-pas','123'],script) # runProgram('isql',[fdn,'-q','-user','tmp$c4648','-pas','123'],script)
# print ('ISQL using NON sysdba user account finished.') # print ('ISQL using NON sysdba user account finished.')
# #
# script='''revoke create database from user tmp$c4648; # script='''revoke create database from user tmp$c4648;
# drop user tmp$c4648; # drop user tmp$c4648;
# commit; # commit;
# ''' # '''
# runProgram('isql',[dsn,'-q','-user',user_name,'-password',user_password],script) # runProgram('isql',[dsn,'-q','-user',user_name,'-password',user_password],script)
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
Starting backup... Starting backup...
Backup finished. Backup finished.
Starting restore using NON sysdba user account... Starting restore using NON sysdba user account...
Restore using NON sysdba user account finished. Restore using NON sysdba user account finished.
Delete backup file...
Backup file deleted.
Starting ISQL using NON sysdba user account... Starting ISQL using NON sysdba user account...
Who am I: TMP$C4648 Who am I: TMP$C4648
Used protocol: TCP Used protocol: TCP
Connected to restored DB ? YES Connected to restored DB ? YES
Owner of DB is: TMP$C4648 Owner of DB is: TMP$C4648
ISQL using NON sysdba user account finished. ISQL using NON sysdba user account finished.
""" """
user_1 = user_factory(name='tmp$c4648', password='123')
temp_db_1 = temp_file('tmp4648.fdb')
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action, user_1: User, temp_db_1: Path, capsys):
def test_1(db_1): with act_1.db.connect() as con:
pytest.fail("Test not IMPLEMENTED") c = con.cursor()
#-- 'create ... grant admin role' or 'grant rdb$admin' are NOT neccessary
#-- for enabling to creating database by non-sysdba user:
c.execute('grant create database to user tmp$c4648')
con.commit()
#
print ('Starting backup...')
backup = BytesIO()
with act_1.connect_server() as srv:
srv.database.local_backup(database=str(act_1.db.db_path), backup_stream=backup)
print ('Backup finished.')
backup.seek(0)
with act_1.connect_server(user=user_1.name, password=user_1.password) as srv:
print ('Starting restore using NON sysdba user account...')
srv.database.local_restore(database=str(temp_db_1), backup_stream=backup,
flags=SrvRestoreFlag.REPLACE)
print ('Restore using NON sysdba user account finished.')
#
script = """
set list on;
select
a.mon$user as "Who am I:"
,left(a.mon$remote_protocol,3) as "Used protocol:"
,iif(m.mon$database_name containing 'tmp4648.fdb','YES','NO! ' || m.mon$database_name ) as "Connected to restored DB ?"
,m.mon$owner as "Owner of DB is:"
from mon$attachments a, mon$database m
where a.mon$attachment_id=current_connection;
commit;
"""
print ('Starting ISQL using NON sysdba user account...')
act_1.isql(switches=['-q', '-user', 'tmp$c4648', '-pas', '123', f'localhost:{temp_db_1}'],
connect_db=False, input=script)
print(act_1.stdout)
print ('ISQL using NON sysdba user account finished.')
act_1.reset()
act_1.expected_stdout = expected_stdout_1
act_1.stdout = capsys.readouterr().out
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,21 +2,25 @@
# #
# id: bugs.core_4707 # id: bugs.core_4707
# title: Implement ability to validate tables and indices online (without exclusive access to database) # title: Implement ability to validate tables and indices online (without exclusive access to database)
# decription: # decription:
# Checked on: 4.0.0.1635 SS: 7.072s; 4.0.0.1633 CS: 7.923s; 3.0.5.33180 SS: 6.599s; 3.0.5.33178 CS: 7.189s. 2.5.9.27119 SS: 5.951s; 2.5.9.27146 SC: 5.748s. # Checked on: 4.0.0.1635 SS: 7.072s; 4.0.0.1633 CS: 7.923s; 3.0.5.33180 SS: 6.599s; 3.0.5.33178 CS: 7.189s. 2.5.9.27119 SS: 5.951s; 2.5.9.27146 SC: 5.748s.
# #
# tracker_id: CORE-4707 # tracker_id: CORE-4707
# min_versions: ['2.5.5'] # min_versions: ['2.5.5']
# versions: 2.5.5 # versions: 2.5.5
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action import subprocess
import time
from pathlib import Path
from firebird.qa import db_factory, python_act, Action, temp_file
# version: 2.5.5 # version: 2.5.5
# resources: None # resources: None
substitutions_1 = [('[\\d]{2}:[\\d]{2}:[\\d]{2}.[\\d]{2}', ''), ('Relation [\\d]{3,4}', 'Relation')] substitutions_1 = [('[\\d]{2}:[\\d]{2}:[\\d]{2}.[\\d]{2}', ''),
('Relation [\\d]{3,4}', 'Relation')]
init_script_1 = """ init_script_1 = """
set term ^; set term ^;
@ -33,16 +37,16 @@ init_script_1 = """
recreate table test2(id int primary key using index test2_pk, s varchar(1000), t computed by (s) ); recreate table test2(id int primary key using index test2_pk, s varchar(1000), t computed by (s) );
recreate table test3(id int); recreate table test3(id int);
commit; commit;
insert into test1(id, s) select gen_id(g,1), rpad('', 1000, gen_id(g,0) ) from rdb$types rows 100; insert into test1(id, s) select gen_id(g,1), rpad('', 1000, gen_id(g,0) ) from rdb$types rows 100;
insert into test2(id, s) select id, s from test1; insert into test2(id, s) select id, s from test1;
commit; commit;
create index test2_s on test2(s); create index test2_s on test2(s);
create index test2_c on test2 computed by(s); create index test2_c on test2 computed by(s);
create index test2_t on test2 computed by(t); create index test2_t on test2 computed by(t);
commit; commit;
""" """
db_1 = db_factory(sql_dialect=3, init=init_script_1) db_1 = db_factory(sql_dialect=3, init=init_script_1)
@ -53,32 +57,32 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# from subprocess import Popen # from subprocess import Popen
# import time # import time
# from fdb import services # from fdb import services
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# # Obtain engine version: # # Obtain engine version:
# engine = str(db_conn.engine_version) # convert to text because 'float' object has no attribute 'startswith' # engine = str(db_conn.engine_version) # convert to text because 'float' object has no attribute 'startswith'
# db_file = db_conn.database_name # db_file = db_conn.database_name
# db_conn.close() # db_conn.close()
# #
# #
# #----------------------------------- # #-----------------------------------
# #
# def flush_and_close(file_handle): # def flush_and_close(file_handle):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# #
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -86,10 +90,10 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# os.remove( f_names_list[i] ) # os.remove( f_names_list[i] )
# if os.path.isfile( f_names_list[i]): # if os.path.isfile( f_names_list[i]):
# print('ERROR: can not remove file ' + f_names_list[i]) # print('ERROR: can not remove file ' + f_names_list[i])
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# #
# # Following script will hang for sevral seconds (see 'lock timeout' argument - and this will serve as pause # # Following script will hang for sevral seconds (see 'lock timeout' argument - and this will serve as pause
# # during which we can launch fbsvcmgr to validate database: # # during which we can launch fbsvcmgr to validate database:
# lock_sql=''' # lock_sql='''
@ -101,9 +105,9 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# end ^ # end ^
# set term ;^ # set term ;^
# commit; # commit;
# #
# set transaction wait; # set transaction wait;
# #
# delete from test1; # delete from test1;
# insert into test3(id) values(1); # insert into test3(id) values(1);
# set list on; # set list on;
@ -121,47 +125,47 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# set term ;^ # set term ;^
# select 'EB with pause finished.' as msg_2 from rdb$database; # select 'EB with pause finished.' as msg_2 from rdb$database;
# ''' % (user_name, user_password) # ''' % (user_name, user_password)
# #
# f_hang_sql=open( os.path.join(context['temp_directory'],'tmp_4707_hang.sql'), 'w') # f_hang_sql=open( os.path.join(context['temp_directory'],'tmp_4707_hang.sql'), 'w')
# f_hang_sql.write(lock_sql) # f_hang_sql.write(lock_sql)
# flush_and_close( f_hang_sql ) # flush_and_close( f_hang_sql )
# #
# #
# ################ ############################################################################## # ################ ##############################################################################
# # Make asynchronous call of ISQL which will stay several seconds in pause due to row-level lock # # Make asynchronous call of ISQL which will stay several seconds in pause due to row-level lock
# # ############################################################################################# # # #############################################################################################
# # Execute a child program in a new process, redirecting STDERR to the same target as of STDOUT: # # Execute a child program in a new process, redirecting STDERR to the same target as of STDOUT:
# f_hang_log=open( os.path.join(context['temp_directory'],'tmp_4707_hang.log'), 'w') # f_hang_log=open( os.path.join(context['temp_directory'],'tmp_4707_hang.log'), 'w')
# p_hang = Popen([context['isql_path'], dsn, "-i", f_hang_sql.name],stdout=f_hang_log, stderr=subprocess.STDOUT) # p_hang = Popen([context['isql_path'], dsn, "-i", f_hang_sql.name],stdout=f_hang_log, stderr=subprocess.STDOUT)
# #
# # Here we should wait while ISQL will establish its connect (in separate child window, call asynchronous) and # # Here we should wait while ISQL will establish its connect (in separate child window, call asynchronous) and
# # stay in pause: # # stay in pause:
# time.sleep(2) # time.sleep(2)
# #
# ############################################################################################# # #############################################################################################
# # Make SYNC. call of fbsvcmgr in order to validate database which has locks on some relations # # Make SYNC. call of fbsvcmgr in order to validate database which has locks on some relations
# ############################################################################################# # #############################################################################################
# f_svc_log=open( os.path.join(context['temp_directory'],'tmp_4707_svc.log'), 'w') # f_svc_log=open( os.path.join(context['temp_directory'],'tmp_4707_svc.log'), 'w')
# subprocess.call([ context['fbsvcmgr_path'], 'localhost:service_mgr','action_validate','dbname', db_file,'val_lock_timeout','1'],stdout=f_svc_log, stderr=subprocess.STDOUT) # subprocess.call([ context['fbsvcmgr_path'], 'localhost:service_mgr','action_validate','dbname', db_file,'val_lock_timeout','1'],stdout=f_svc_log, stderr=subprocess.STDOUT)
# flush_and_close( f_svc_log ) # flush_and_close( f_svc_log )
# #
# ####################################################### # #######################################################
# # TERMINATE separate (child) process of ISQL that hangs # # TERMINATE separate (child) process of ISQL that hangs
# ####################################################### # #######################################################
# p_hang.terminate() # p_hang.terminate()
# flush_and_close( f_hang_log ) # flush_and_close( f_hang_log )
# #
# with open( f_hang_log.name,'r') as f: # with open( f_hang_log.name,'r') as f:
# print(f.read()) # print(f.read())
# #
# with open( f_svc_log.name,'r') as f: # with open( f_svc_log.name,'r') as f:
# print(f.read()) # print(f.read())
# #
# # cleanup: # # cleanup:
# ########## # ##########
# time.sleep(1) # time.sleep(1)
# cleanup( [i.name for i in (f_hang_sql, f_hang_log, f_svc_log) ] ) # cleanup( [i.name for i in (f_hang_sql, f_hang_log, f_svc_log) ] )
# #
# ## |||||||||||||||||||||||||||| # ## ||||||||||||||||||||||||||||
# ## ###################################||| FB 4.0+, SS and SC |||############################## # ## ###################################||| FB 4.0+, SS and SC |||##############################
# ## |||||||||||||||||||||||||||| # ## ||||||||||||||||||||||||||||
@ -177,10 +181,11 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# con4cleanup=fdb.connect( dsn = dsn, user = user_name, password = user_password ) # con4cleanup=fdb.connect( dsn = dsn, user = user_name, password = user_password )
# con4cleanup.execute_immediate('delete from mon$attachments where mon$attachment_id != current_connection') # con4cleanup.execute_immediate('delete from mon$attachments where mon$attachment_id != current_connection')
# con4cleanup.commit() # con4cleanup.commit()
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
ISQL_MSG Starting EB with infinite pause. ISQL_MSG Starting EB with infinite pause.
@ -199,11 +204,67 @@ expected_stdout_1 = """
08:37:03.17 Acquire relation lock failed 08:37:03.17 Acquire relation lock failed
08:37:03.17 Relation 130 (TEST3) : 1 ERRORS found 08:37:03.17 Relation 130 (TEST3) : 1 ERRORS found
08:37:03.17 Validation finished 08:37:03.17 Validation finished
""" """
hang_script_1 = temp_file('hang_script.sql')
hang_output_1 = temp_file('hang_script.out')
@pytest.mark.version('>=2.5.5') @pytest.mark.version('>=2.5.5')
@pytest.mark.xfail def test_1(act_1: Action, hang_script_1: Path, hang_output_1: Path, capsys, request):
def test_1(db_1): # Fializer for FB4
pytest.fail("Test not IMPLEMENTED") def drop_connections():
with act_1.db.connect() as con4cleanup:
con4cleanup.execute_immediate('delete from mon$attachments where mon$attachment_id != current_connection')
con4cleanup.commit()
request.addfinalizer(drop_connections)
# Following script will hang for sevral seconds (see 'lock timeout' argument - and this will serve as pause
# during which we can launch fbsvcmgr to validate database:
hang_script_1.write_text(f"""
set term ^;
execute block as
begin
execute statement 'drop role tmp$r4707';
when any do begin end
end ^
set term ;^
commit;
set transaction wait;
delete from test1;
insert into test3(id) values(1);
set list on;
select 'Starting EB with infinite pause.' as isql_msg from rdb$database;
set term ^;
execute block as
begin
execute statement 'update test1 set id=-id'
on external 'localhost:' || rdb$get_context('SYSTEM','DB_NAME')
as user '{act_1.db.user}' password '{act_1.db.password}'
role 'TMP$R4707' -- this will force to create new attachment, and its Tx will be paused on INFINITE time.
;
when any do begin end
end ^
set term ;^
select 'EB with pause finished.' as msg_2 from rdb$database;
""")
# Make asynchronous call of ISQL which will stay several seconds in pause due to row-level lock
with open(hang_output_1, mode='w') as hang_out:
p_hang_sql = subprocess.Popen([act_1.vars['isql'], '-i', str(hang_script_1),
'-user', act_1.db.user,
'-password', act_1.db.password, act_1.db.dsn],
stdout=hang_out, stderr=subprocess.STDOUT)
try:
time.sleep(2)
# Make SYNC. call of fbsvcmgr in order to validate database which has locks on some relations
act_1.svcmgr(switches=['action_validate', 'dbname', str(act_1.db.db_path),
'val_lock_timeout', '1'])
finally:
p_hang_sql.terminate()
#
print(hang_output_1.read_text())
print(act_1.stdout)
act_1.expected_stdout = expected_stdout_1
act_1.stdout = capsys.readouterr().out
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,19 +2,22 @@
# #
# id: bugs.core_4715 # id: bugs.core_4715
# title: Restore of shadowed database fails using -k ("restore without shadow") switch # title: Restore of shadowed database fails using -k ("restore without shadow") switch
# decription: # decription: [pcisar] 23.11.2021
# For unknow reason, on v4.0.0.2496 the gstat -h does not report active shadow
# and test thus fail.
# tracker_id: CORE-4715 # tracker_id: CORE-4715
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from pathlib import Path
from firebird.qa import db_factory, python_act, Action, temp_file
# version: 3.0 # version: 3.0
# resources: None # resources: None
substitutions_1 = [('^((?!HASH_IN_SOURCE|RDB\\$SHADOW_NUMBER|HASH_IN_RESTORED|Attributes).)*$', ''), ('[ ]+', ' '), ('[\t]*', ' ')] substitutions_1 = [('^((?!HASH_IN_SOURCE|RDB\\$SHADOW_NUMBER|HASH_IN_RESTORED|Attributes).)*$', '')]
init_script_1 = """ init_script_1 = """
-- Confirmed on WI-T3.0.0.31374: -- Confirmed on WI-T3.0.0.31374:
@ -24,14 +27,14 @@ init_script_1 = """
-- gbak:Exiting before completion due to errors -- gbak:Exiting before completion due to errors
recreate table test(s varchar(30)); recreate table test(s varchar(30));
commit; commit;
""" """
db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1) db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# import os # import os
# #
# shd=os.path.join(context['temp_directory'],'core_4715.shd') # shd=os.path.join(context['temp_directory'],'core_4715.shd')
# script = '''create shadow 1 '%s'; commit; insert into test select 'line #'||lpad(row_number()over(), 3, '0') from rdb$types rows 200; commit; set list on; select hash(list(s)) hash_in_source from test; select * from rdb$files;''' % shd # script = '''create shadow 1 '%s'; commit; insert into test select 'line #'||lpad(row_number()over(), 3, '0') from rdb$types rows 200; commit; set list on; select hash(list(s)) hash_in_source from test; select * from rdb$files;''' % shd
# runProgram('isql',[dsn,'-q','-user',user_name,'-password',user_password],script) # runProgram('isql',[dsn,'-q','-user',user_name,'-password',user_password],script)
@ -46,9 +49,10 @@ db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1)
# os.remove(fbk) # os.remove(fbk)
# if os.path.isfile(fbn): # if os.path.isfile(fbn):
# os.remove(fbn) # os.remove(fbn)
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
Creating shadow... Creating shadow...
@ -56,11 +60,30 @@ HASH_IN_SOURCE 1499836372373901520
RDB$SHADOW_NUMBER 1 RDB$SHADOW_NUMBER 1
Attributes force write, active shadow Attributes force write, active shadow
HASH_IN_RESTORED 1499836372373901520 HASH_IN_RESTORED 1499836372373901520
""" """
fbk_1 = temp_file('core_4715-shadowed.fbk')
fbn_1 = temp_file('core_4715-restored.fdb')
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action, fbk_1: Path, fbn_1: Path, capsys):
def test_1(db_1): act_1.isql(switches=['-q'],
pytest.fail("Test not IMPLEMENTED") input=f'''create shadow 1 '{fbn_1.with_suffix('.shd')}'; commit; insert into test select 'line #'||lpad(row_number()over(), 3, '0') from rdb$types rows 200; commit; set list on; select hash(list(s)) hash_in_source from test; select * from rdb$files;''')
print(act_1.stdout)
act_1.reset()
act_1.gstat(switches=['-h'])
print(act_1.stdout)
act_1.reset()
act_1.gbak(switches=['-b', act_1.db.dsn, str(fbk_1)])
act_1.reset()
act_1.gbak(switches=['-rep', '-k', str(fbk_1), str(fbn_1)])
act_1.reset()
act_1.isql(switches=['-q', '-user', act_1.db.user, '-password', act_1.db.password,
str(fbn_1)], connect_db=False,
input='set list on; select hash(list(s)) hash_in_restored from test;')
print(act_1.stdout)
#
act_1.reset()
act_1.expected_stdout = expected_stdout_1
act_1.stdout = capsys.readouterr().out
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,28 +2,28 @@
# #
# id: bugs.core_4731 # id: bugs.core_4731
# title: Prohibit an ability to issue DML or DDL statements on RDB$ tables # title: Prohibit an ability to issue DML or DDL statements on RDB$ tables
# decription: # decription:
# Integral test for verifying ability to change system tables by non-privileged user and by those # Integral test for verifying ability to change system tables by non-privileged user and by those
# who have been granted with RDB$ADMIN role. # who have been granted with RDB$ADMIN role.
# Main idea: read system tables (hereafter - 'ST') metadata and generate all possible DML and DDL # Main idea: read system tables (hereafter - 'ST') metadata and generate all possible DML and DDL
# statements that are intended to: # statements that are intended to:
# a) restrict ST by creating new table with foreign key to selected ST (if it has PK or UK); # a) restrict ST by creating new table with foreign key to selected ST (if it has PK or UK);
# b) change data by issuing INSERT / UPDATE / DELETE statements; also try SELECT ... WITH LOCK; # b) change data by issuing INSERT / UPDATE / DELETE statements; also try SELECT ... WITH LOCK;
# c) change metadata: add column, alter column (drop NULL constraint, add new contraint, add DEFAULT value), # c) change metadata: add column, alter column (drop NULL constraint, add new contraint, add DEFAULT value),
# drop column; # drop column;
# d) aux. actions: attempt to drop ST. # d) aux. actions: attempt to drop ST.
# *** 11-apr-2018: EXCLUDED attempt to create index on ST: now it is allowed, see CORE-5746 *** # *** 11-apr-2018: EXCLUDED attempt to create index on ST: now it is allowed, see CORE-5746 ***
# e) make indirect changes: apply ALTER SEQUENCE statement for system generators # e) make indirect changes: apply ALTER SEQUENCE statement for system generators
# #
# Test contains following statements and procedures: # Test contains following statements and procedures:
# 1) creating two users, one of them is granted with role RDB$ADMIN. # 1) creating two users, one of them is granted with role RDB$ADMIN.
# Both these users are granted to create/alter/drop any kinds of database objects. # Both these users are granted to create/alter/drop any kinds of database objects.
# 2) creating several user objects (domain, exception, collation, sequence, master/detail tables, trigger, # 2) creating several user objects (domain, exception, collation, sequence, master/detail tables, trigger,
# view, stanalone procedure and standalone function and package). These objects are created in order # view, stanalone procedure and standalone function and package). These objects are created in order
# to add some data in system tables that can be later actually affected by vulnerable expressions; # to add some data in system tables that can be later actually affected by vulnerable expressions;
# 3) proc sp_gen_expr_for_creating_fkeys: # 3) proc sp_gen_expr_for_creating_fkeys:
# reads definition of every system table and if it has PK/UK than generate expressions for item "a": # reads definition of every system table and if it has PK/UK than generate expressions for item "a":
# they will create completely new table with set of fields which id appropriate to build FOREIGN KEY # they will create completely new table with set of fields which id appropriate to build FOREIGN KEY
# to selected ST. Generated expressions are added to special table `vulnerable_on_sys_tables`; # to selected ST. Generated expressions are added to special table `vulnerable_on_sys_tables`;
# 4) proc sp_gen_expr_for_direct_change: # 4) proc sp_gen_expr_for_direct_change:
# reads definition of every system table and generates DML and DDL expressions for items "b" ... "e" described # reads definition of every system table and generates DML and DDL expressions for items "b" ... "e" described
@ -36,11 +36,11 @@
# 6) two calls of sp_run_vulnerable_expressions: one for non-privileged user and second for user with role RDB$ADMIN. # 6) two calls of sp_run_vulnerable_expressions: one for non-privileged user and second for user with role RDB$ADMIN.
# 7) select values of raised gdscodes (distinct) in order to check that only ONE gdscode occured (335544926). # 7) select values of raised gdscodes (distinct) in order to check that only ONE gdscode occured (335544926).
# 8) select expressions that were PASSED without exceptions. # 8) select expressions that were PASSED without exceptions.
# #
# Checked on: # Checked on:
# 3.0.4.32947: OK, SS: 22s, CS: 37s # 3.0.4.32947: OK, SS: 22s, CS: 37s
# 4.0.0.955: OK, SS: 35s, CS: 33s # 4.0.0.955: OK, SS: 35s, CS: 33s
# #
# REFACTORED 18.02.2020: most of initial code was moved into $files_location/core_4731.sql; changed test_type to 'Python'. # REFACTORED 18.02.2020: most of initial code was moved into $files_location/core_4731.sql; changed test_type to 'Python'.
# Checked 18.02.2020 afte refactoring: # Checked 18.02.2020 afte refactoring:
# 4.0.0.1773 SS: 11.759s. # 4.0.0.1773 SS: 11.759s.
@ -49,14 +49,16 @@
# 3.0.6.33247 SS: 8.431s. # 3.0.6.33247 SS: 8.431s.
# 3.0.6.33247 SC: 11.419s. # 3.0.6.33247 SC: 11.419s.
# 3.0.6.33247 CS: 10.846s. # 3.0.6.33247 CS: 10.846s.
# #
# tracker_id: CORE-4731 # tracker_id: CORE-4731
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from pathlib import Path
from firebird.qa import db_factory, python_act, Action, user_factory, User
from firebird.driver import ShutdownMode, ShutdownMethod
# version: 3.0 # version: 3.0
# resources: None # resources: None
@ -69,38 +71,38 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# import sys # import sys
# import subprocess # import subprocess
# import time # import time
# from fdb import services # from fdb import services
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# this_db = db_conn.database_name # this_db = db_conn.database_name
# db_conn.close() # db_conn.close()
# #
# dba_privileged_name = 'tmp_c4731_cooldba' # dba_privileged_name = 'tmp_c4731_cooldba'
# non_privileged_name = 'tmp_c4731_manager' # non_privileged_name = 'tmp_c4731_manager'
# #
# #----------------------------------- # #-----------------------------------
# #
# def flush_and_close(file_handle): # def flush_and_close(file_handle):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# #
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -108,91 +110,91 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# os.remove( f_names_list[i] ) # os.remove( f_names_list[i] )
# if os.path.isfile( f_names_list[i]): # if os.path.isfile( f_names_list[i]):
# print('ERROR: can not remove file ' + f_names_list[i]) # print('ERROR: can not remove file ' + f_names_list[i])
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# #
# f_sql=open(os.path.join(context['files_location'],'core_4731.sql'),'r') # f_sql=open(os.path.join(context['files_location'],'core_4731.sql'),'r')
# sql_for_prepare = f_sql.read() # sql_for_prepare = f_sql.read()
# f_sql.close() # f_sql.close()
# #
# f_pre_sql = open( os.path.join(context['temp_directory'],'tmp_core_4731_pre.sql'), 'w') # f_pre_sql = open( os.path.join(context['temp_directory'],'tmp_core_4731_pre.sql'), 'w')
# f_pre_sql.write( sql_for_prepare % dict(globals(), **locals()) ) # f_pre_sql.write( sql_for_prepare % dict(globals(), **locals()) )
# flush_and_close( f_pre_sql ) # flush_and_close( f_pre_sql )
# #
# f_pre_log = open( '.'.join( (os.path.splitext( f_pre_sql.name )[0], 'log') ), 'w') # f_pre_log = open( '.'.join( (os.path.splitext( f_pre_sql.name )[0], 'log') ), 'w')
# f_pre_err = open( '.'.join( (os.path.splitext( f_pre_sql.name )[0], 'err') ), 'w') # f_pre_err = open( '.'.join( (os.path.splitext( f_pre_sql.name )[0], 'err') ), 'w')
# subprocess.call( [ context['isql_path'], dsn, '-q', '-i', f_pre_sql.name ], stdout = f_pre_log, stderr = f_pre_err) # subprocess.call( [ context['isql_path'], dsn, '-q', '-i', f_pre_sql.name ], stdout = f_pre_log, stderr = f_pre_err)
# flush_and_close( f_pre_log ) # flush_and_close( f_pre_log )
# flush_and_close( f_pre_err ) # flush_and_close( f_pre_err )
# #
# runProgram( context['gfix_path'],[dsn, '-shut','full','-force','0'] ) # runProgram( context['gfix_path'],[dsn, '-shut','full','-force','0'] )
# runProgram( context['gfix_path'],[dsn, '-online'] ) # runProgram( context['gfix_path'],[dsn, '-online'] )
# #
# sql_run=''' # sql_run='''
# -- ################################################################################### # -- ###################################################################################
# -- R U N A S N O N - P R I V I L E G E D U S E R # -- R U N A S N O N - P R I V I L E G E D U S E R
# -- ################################################################################### # -- ###################################################################################
# execute procedure sp_run_vulnerable_expressions('%(non_privileged_name)s', '123', 'NONE'); # execute procedure sp_run_vulnerable_expressions('%(non_privileged_name)s', '123', 'NONE');
# #
# -- Note: as of build 3.0.31810, we can SKIP restoring of 'pure-state' of RDB$ tables # -- Note: as of build 3.0.31810, we can SKIP restoring of 'pure-state' of RDB$ tables
# -- after this SP because non-privileged user can NOT change enything. # -- after this SP because non-privileged user can NOT change enything.
# -- All his attempts should FAIL, system tables should be in unchanged state. # -- All his attempts should FAIL, system tables should be in unchanged state.
# #
# set list off; # set list off;
# set heading off; # set heading off;
# #
# select '-- Executed with role: '||trim(( select actual_role from vulnerable_on_sys_tables rows 1 )) # select '-- Executed with role: '||trim(( select actual_role from vulnerable_on_sys_tables rows 1 ))
# ||'. Expressions that passes WITHOUT errors:' as msg # ||'. Expressions that passes WITHOUT errors:' as msg
# from rdb$database # from rdb$database
# ; # ;
# #
# commit; -- 11-04-2018, do not remove! # commit; -- 11-04-2018, do not remove!
# set transaction no wait; # set transaction no wait;
# #
# set list on; # set list on;
# select count(*) as "-- count_of_passed: " # select count(*) as "-- count_of_passed: "
# from v_passed; # from v_passed;
# #
# set list on; # set list on;
# select * from v_passed; # select * from v_passed;
# #
# set list on; # set list on;
# select distinct vulnerable_gdscode as "-- gdscode list for blocked:" # select distinct vulnerable_gdscode as "-- gdscode list for blocked:"
# from vulnerable_on_sys_tables # from vulnerable_on_sys_tables
# where vulnerable_gdscode is distinct from -1; # where vulnerable_gdscode is distinct from -1;
# #
# -- ######################################################################################### # -- #########################################################################################
# -- R U N A S U S E R W H O I S G R A N T E D W I T H R B D $ A D M I N # -- R U N A S U S E R W H O I S G R A N T E D W I T H R B D $ A D M I N
# -- ######################################################################################### # -- #########################################################################################
# execute procedure sp_run_vulnerable_expressions('%(dba_privileged_name)s', '123', 'RDB$ADMIN'); # execute procedure sp_run_vulnerable_expressions('%(dba_privileged_name)s', '123', 'RDB$ADMIN');
# #
# set list off; # set list off;
# set heading off; # set heading off;
# #
# select '-- Executed with role: '||trim(( select actual_role from vulnerable_on_sys_tables rows 1 )) # select '-- Executed with role: '||trim(( select actual_role from vulnerable_on_sys_tables rows 1 ))
# ||'. Expressions that passes WITHOUT errors:' as msg # ||'. Expressions that passes WITHOUT errors:' as msg
# from rdb$database # from rdb$database
# ; # ;
# commit; -- 11-04-2018, do not remove! # commit; -- 11-04-2018, do not remove!
# #
# set list on; # set list on;
# select count(*) as "-- count_of_passed: " # select count(*) as "-- count_of_passed: "
# from v_passed; # from v_passed;
# #
# set list on; # set list on;
# select * from v_passed; # select * from v_passed;
# #
# set list on; # set list on;
# select distinct vulnerable_gdscode as "-- gdscode list for blocked:" # select distinct vulnerable_gdscode as "-- gdscode list for blocked:"
# from vulnerable_on_sys_tables # from vulnerable_on_sys_tables
# where vulnerable_gdscode is distinct from -1; # where vulnerable_gdscode is distinct from -1;
# #
# ---------------- # ----------------
# commit; # commit;
# #
# connect '%(dsn)s' user '%(user_name)s' password '%(user_password)s'; # connect '%(dsn)s' user '%(user_name)s' password '%(user_password)s';
# #
# -- |||||||||||||||||||||||||||| # -- ||||||||||||||||||||||||||||
# -- ###################################||| FB 4.0+, SS and SC |||############################## # -- ###################################||| FB 4.0+, SS and SC |||##############################
# -- |||||||||||||||||||||||||||| # -- ||||||||||||||||||||||||||||
@ -206,26 +208,26 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# -- SQLCODE: -901 / lock time-out on wait transaction / object <this_test_DB> is in use # -- SQLCODE: -901 / lock time-out on wait transaction / object <this_test_DB> is in use
# -- ############################################################################################# # -- #############################################################################################
# delete from mon$attachments where mon$attachment_id != current_connection; # delete from mon$attachments where mon$attachment_id != current_connection;
# commit; # commit;
# #
# drop user %(dba_privileged_name)s; # drop user %(dba_privileged_name)s;
# drop user %(non_privileged_name)s; # drop user %(non_privileged_name)s;
# commit; # commit;
# ''' % dict(globals(), **locals()) # ''' % dict(globals(), **locals())
# #
# f_sql_run = open( os.path.join(context['temp_directory'],'tmp_core_4731_run.sql'), 'w') # f_sql_run = open( os.path.join(context['temp_directory'],'tmp_core_4731_run.sql'), 'w')
# f_sql_run.write( sql_run % dict(globals(), **locals()) ) # f_sql_run.write( sql_run % dict(globals(), **locals()) )
# flush_and_close( f_sql_run ) # flush_and_close( f_sql_run )
# #
# f_run_log = open( '.'.join( (os.path.splitext( f_sql_run.name )[0], 'log') ), 'w') # f_run_log = open( '.'.join( (os.path.splitext( f_sql_run.name )[0], 'log') ), 'w')
# f_run_err = open( '.'.join( (os.path.splitext( f_sql_run.name )[0], 'err') ), 'w') # f_run_err = open( '.'.join( (os.path.splitext( f_sql_run.name )[0], 'err') ), 'w')
# subprocess.call( [ context['isql_path'], dsn, '-q', '-i', f_sql_run.name ], stdout = f_run_log, stderr = f_run_err) # subprocess.call( [ context['isql_path'], dsn, '-q', '-i', f_sql_run.name ], stdout = f_run_log, stderr = f_run_err)
# flush_and_close( f_run_log ) # flush_and_close( f_run_log )
# flush_and_close( f_run_err ) # flush_and_close( f_run_err )
# #
# # Check results: # # Check results:
# # ============== # # ==============
# #
# # 1. Print UNEXPECTED output: # # 1. Print UNEXPECTED output:
# ############################# # #############################
# for f in (f_pre_log, f_pre_err): # for f in (f_pre_log, f_pre_err):
@ -233,26 +235,27 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# for line in f: # for line in f:
# if line.strip(): # if line.strip():
# print( 'UNEXPECTED '+('STDOUT' if f == f_pre_log else 'STDERR')+' WHEN PREPARE DB: ' + line ) # print( 'UNEXPECTED '+('STDOUT' if f == f_pre_log else 'STDERR')+' WHEN PREPARE DB: ' + line )
# #
# with open( f_run_err.name,'r') as f: # with open( f_run_err.name,'r') as f:
# for line in f: # for line in f:
# if line.strip(): # if line.strip():
# print( 'UNEXPECTED STDERR WHEN RUN: ' + line ) # print( 'UNEXPECTED STDERR WHEN RUN: ' + line )
# #
# # 2. Print EXPECTED output: # # 2. Print EXPECTED output:
# ########################### # ###########################
# with open( f_run_log.name,'r') as f: # with open( f_run_log.name,'r') as f:
# for line in f: # for line in f:
# if line.strip(): # if line.strip():
# print( line ) # print( line )
# #
# # Cleanup # # Cleanup
# ######### # #########
# cleanup( [ i.name for i in (f_pre_sql,f_pre_log,f_pre_err,f_sql_run,f_run_log,f_run_err) ] ) # cleanup( [ i.name for i in (f_pre_sql,f_pre_log,f_pre_err,f_sql_run,f_run_log,f_run_err) ] )
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
-- Executed with role: NONE. Expressions that passes WITHOUT errors: -- Executed with role: NONE. Expressions that passes WITHOUT errors:
@ -285,11 +288,104 @@ expected_stdout_1 = """
VULNERABLE_EXPR update RDB$TYPES t set t.RDB$TYPE_NAME = 'C' where coalesce(rdb$system_flag,0)=0 rows 1 returning t.rdb$db_key; -- length of returned rdb$dbkey=8 VULNERABLE_EXPR update RDB$TYPES t set t.RDB$TYPE_NAME = 'C' where coalesce(rdb$system_flag,0)=0 rows 1 returning t.rdb$db_key; -- length of returned rdb$dbkey=8
VULNERABLE_EXPR update RDB$TYPES t set t.RDB$TYPE_NAME = null where coalesce(rdb$system_flag,0)=0 rows 1 returning t.rdb$db_key; -- length of returned rdb$dbkey=8 VULNERABLE_EXPR update RDB$TYPES t set t.RDB$TYPE_NAME = null where coalesce(rdb$system_flag,0)=0 rows 1 returning t.rdb$db_key; -- length of returned rdb$dbkey=8
-- gdscode list for blocked: 335544926 -- gdscode list for blocked: 335544926
""" """
dba_privileged_user = user_factory(name='tmp_c4731_cooldba', password='123')
non_privileged_user = user_factory(name='tmp_c4731_manager', password='123')
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action, dba_privileged_user: User, non_privileged_user: User, capsys):
def test_1(db_1): # Run prepare script
pytest.fail("Test not IMPLEMENTED") prep_script = (act_1.vars['files'] / 'core_4731.sql').read_text()
prep_script = prep_script % {'dba_privileged_name': dba_privileged_user.name,
'non_privileged_name': non_privileged_user.name}
act_1.isql(switches=['-q'], input=prep_script)
#
with act_1.connect_server() as srv:
srv.database.shutdown(database=str(act_1.db.db_path), mode=ShutdownMode.FULL,
method=ShutdownMethod.FORCED, timeout=0)
srv.database.bring_online(database=str(act_1.db.db_path))
#
test_script = f"""
-- ###################################################################################
-- R U N A S N O N - P R I V I L E G E D U S E R
-- ###################################################################################
execute procedure sp_run_vulnerable_expressions('{non_privileged_user.name}', '123', 'NONE');
-- Note: as of build 3.0.31810, we can SKIP restoring of 'pure-state' of RDB$ tables
-- after this SP because non-privileged user can NOT change enything.
-- All his attempts should FAIL, system tables should be in unchanged state.
set list off;
set heading off;
select '-- Executed with role: '||trim(( select actual_role from vulnerable_on_sys_tables rows 1 ))
||'. Expressions that passes WITHOUT errors:' as msg
from rdb$database
;
commit; -- 11-04-2018, do not remove!
set transaction no wait;
set list on;
select count(*) as "-- count_of_passed: "
from v_passed;
set list on;
select * from v_passed;
set list on;
select distinct vulnerable_gdscode as "-- gdscode list for blocked:"
from vulnerable_on_sys_tables
where vulnerable_gdscode is distinct from -1;
-- #########################################################################################
-- R U N A S U S E R W H O I S G R A N T E D W I T H R B D $ A D M I N
-- #########################################################################################
execute procedure sp_run_vulnerable_expressions('{dba_privileged_user.name}', '123', 'RDB$ADMIN');
set list off;
set heading off;
select '-- Executed with role: '||trim(( select actual_role from vulnerable_on_sys_tables rows 1 ))
||'. Expressions that passes WITHOUT errors:' as msg
from rdb$database
;
commit; -- 11-04-2018, do not remove!
set list on;
select count(*) as "-- count_of_passed: "
from v_passed;
set list on;
select * from v_passed;
set list on;
select distinct vulnerable_gdscode as "-- gdscode list for blocked:"
from vulnerable_on_sys_tables
where vulnerable_gdscode is distinct from -1;
----------------
commit;
connect '{act_1.db.dsn}' user '{act_1.db.user}' password '{act_1.db.password}';
-- ||||||||||||||||||||||||||||
-- ###################################||| FB 4.0+, SS and SC |||##############################
-- ||||||||||||||||||||||||||||
-- If we check SS or SC and ExtConnPoolLifeTime > 0 (config parameter FB 4.0+) then current
-- DB (bugs.core_NNNN.fdb) will be 'captured' by firebird.exe process and fbt_run utility
-- will not able to drop this database at the final point of test.
-- Moreover, DB file will be hold until all activity in firebird.exe completed and AFTER this
-- we have to wait for <ExtConnPoolLifeTime> seconds after it (discussion and small test see
-- in the letter to hvlad and dimitr 13.10.2019 11:10).
-- This means that one need to kill all connections to prevent from exception on cleanup phase:
-- SQLCODE: -901 / lock time-out on wait transaction / object <this_test_DB> is in use
-- #############################################################################################
delete from mon$attachments where mon$attachment_id != current_connection;
commit;
"""
act_1.reset()
act_1.expected_stdout = expected_stdout_1
act_1.isql(switches=['-q'], input=test_script)
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,41 +2,41 @@
# #
# id: bugs.core_4743 # id: bugs.core_4743
# title: Granted role does not work with non-ascii username # title: Granted role does not work with non-ascii username
# decription: # decription:
# Test creates non-ascii user and role, and also several kind of DB objects (table, procedure, function etc). # Test creates non-ascii user and role, and also several kind of DB objects (table, procedure, function etc).
# Then role is granted to user, and privileges for DB objects are granted to this role. # Then role is granted to user, and privileges for DB objects are granted to this role.
# All these actions are done in ISQL which is launched as separate (child) process. # All these actions are done in ISQL which is launched as separate (child) process.
# No errors must be raised in it (see 'f_ddl_log' - it must remain empty). # No errors must be raised in it (see 'f_ddl_log' - it must remain empty).
# #
# Further, we try to establish connection to the test DB using non-ascii user and role. # Further, we try to establish connection to the test DB using non-ascii user and role.
# #
# ::: NB ::: # ::: NB :::
# Attempt to use ISQL for connect with non-ascii login will FAIL with: # Attempt to use ISQL for connect with non-ascii login will FAIL with:
# Statement failed, SQLSTATE = 22021 # Statement failed, SQLSTATE = 22021
# Bad international character in tag isc_dpb_user_name # Bad international character in tag isc_dpb_user_name
# -Cannot transliterate character between character sets # -Cannot transliterate character between character sets
# -Invalid or incomplete multibyte or wide character # -Invalid or incomplete multibyte or wide character
# #
# Fortunately, this can be done without problems using fdb.connect(). # Fortunately, this can be done without problems using fdb.connect().
# #
# After connect, we obtain: # After connect, we obtain:
# * name of current user and his role (both of them must be non-ascii); # * name of current user and his role (both of them must be non-ascii);
# * privileges that was granted to this user (see query to v_current_privileges); # * privileges that was granted to this user (see query to v_current_privileges);
# #
# Finally, we disconnect, generate SQL script for drop this user and run ISQL for this # Finally, we disconnect, generate SQL script for drop this user and run ISQL for this
# (we have to do this because it seems that there is no way to drop NON-ASCII user via FDB Services). # (we have to do this because it seems that there is no way to drop NON-ASCII user via FDB Services).
# #
# NOTE: Python package 'io' is used here instead of codecs (the latter is obsolete in Python). # NOTE: Python package 'io' is used here instead of codecs (the latter is obsolete in Python).
# #
# Checked on: 4.0.0.2416 (Windows and Linux) # Checked on: 4.0.0.2416 (Windows and Linux)
# #
# tracker_id: # tracker_id:
# min_versions: ['4.0'] # min_versions: ['4.0']
# versions: 4.0 # versions: 4.0
# qmid: bugs.core_4743 # qmid: bugs.core_4743
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, python_act, Action, user_factory, User
# version: 4.0 # version: 4.0
# resources: None # resources: None
@ -49,32 +49,32 @@ db_1 = db_factory(charset='UTF8', sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# import io # import io
# #import codecs # #import codecs
# import subprocess # import subprocess
# import time # import time
# #
# db_conn.close() # db_conn.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close( file_handle ): # def flush_and_close( file_handle ):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull: # if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull:
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -86,23 +86,23 @@ db_1 = db_factory(charset='UTF8', sql_dialect=3, init=init_script_1)
# print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.') # print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.')
# print('type(f_names_list[i])=',type(f_names_list[i])) # print('type(f_names_list[i])=',type(f_names_list[i]))
# del_name = None # del_name = None
# #
# if del_name and os.path.isfile( del_name ): # if del_name and os.path.isfile( del_name ):
# os.remove( del_name ) # os.remove( del_name )
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# sql_txt=''' set bail on; # sql_txt=''' set bail on;
# set names utf8; # set names utf8;
# connect '%(dsn)s' user '%(user_name)s' password '%(user_password)s'; # connect '%(dsn)s' user '%(user_name)s' password '%(user_password)s';
# #
# create or alter user "Вася Пупкин" password '123' using plugin Srp; # create or alter user "Вася Пупкин" password '123' using plugin Srp;
# create role "Старший дворник"; # create role "Старший дворник";
# commit; # commit;
# #
# grant "Старший дворник" to "Вася Пупкин"; # grant "Старший дворник" to "Вася Пупкин";
# commit; # commit;
# #
# create table "Документы"(id int primary key, pid int references "Документы"); # create table "Документы"(id int primary key, pid int references "Документы");
# create exception "НЕ_число" 'Ваша строка не может быть преобразована в число.'; # create exception "НЕ_число" 'Ваша строка не может быть преобразована в число.';
# create sequence "ИД_документа"; # create sequence "ИД_документа";
@ -116,7 +116,7 @@ db_1 = db_factory(charset='UTF8', sql_dialect=3, init=init_script_1)
# return 0; # return 0;
# end # end
# ^ # ^
# #
# create or alter package "Утилиты" as # create or alter package "Утилиты" as
# begin # begin
# procedure pg_sp_worker; # procedure pg_sp_worker;
@ -131,7 +131,7 @@ db_1 = db_factory(charset='UTF8', sql_dialect=3, init=init_script_1)
# ^ # ^
# set term ;^ # set term ;^
# commit; # commit;
# #
# create or alter view v_current_privileges as # create or alter view v_current_privileges as
# select # select
# g.rdb$user as who_is_granted # g.rdb$user as who_is_granted
@ -166,7 +166,7 @@ db_1 = db_factory(charset='UTF8', sql_dialect=3, init=init_script_1)
# from rdb$user_privileges g # from rdb$user_privileges g
# where g.rdb$user in( current_user, current_role ) # where g.rdb$user in( current_user, current_role )
# group by 1,2,3; # group by 1,2,3;
# #
# grant select on v_current_privileges to "Старший дворник"; # grant select on v_current_privileges to "Старший дворник";
# grant select,insert,update,delete,references on "Документы" to "Старший дворник"; # grant select,insert,update,delete,references on "Документы" to "Старший дворник";
# grant usage on exception "НЕ_число" to "Старший дворник"; # grant usage on exception "НЕ_число" to "Старший дворник";
@ -178,7 +178,7 @@ db_1 = db_factory(charset='UTF8', sql_dialect=3, init=init_script_1)
# grant alter any table to "Старший дворник"; # grant alter any table to "Старший дворник";
# grant drop any table to "Старший дворник"; # grant drop any table to "Старший дворник";
# commit; # commit;
# #
# /* # /*
# DO NOT try to use ISQL for connecti using non-ascii user name! It will fail with: # DO NOT try to use ISQL for connecti using non-ascii user name! It will fail with:
# ===== # =====
@ -191,26 +191,26 @@ db_1 = db_factory(charset='UTF8', sql_dialect=3, init=init_script_1)
# Instead, FDB connect() method must be used for this. # Instead, FDB connect() method must be used for this.
# */ # */
# ''' % dict(globals(), **locals()) # ''' % dict(globals(), **locals())
# #
# f_ddl_sql = open( os.path.join(context['temp_directory'], 'tmp_4743_utf8_ddl.sql'), 'w' ) # f_ddl_sql = open( os.path.join(context['temp_directory'], 'tmp_4743_utf8_ddl.sql'), 'w' )
# f_ddl_sql.write( sql_txt ) # f_ddl_sql.write( sql_txt )
# flush_and_close( f_ddl_sql ) # flush_and_close( f_ddl_sql )
# #
# f_ddl_log = open( os.path.splitext(f_ddl_sql.name)[0]+'.log', 'w') # f_ddl_log = open( os.path.splitext(f_ddl_sql.name)[0]+'.log', 'w')
# subprocess.call( [ context['isql_path'], '-q', '-i', f_ddl_sql.name ], # subprocess.call( [ context['isql_path'], '-q', '-i', f_ddl_sql.name ],
# stdout = f_ddl_log, # stdout = f_ddl_log,
# stderr = subprocess.STDOUT # stderr = subprocess.STDOUT
# ) # )
# flush_and_close( f_ddl_log ) # flush_and_close( f_ddl_log )
# #
# with io.open(f_ddl_log.name, 'r', encoding='utf8' ) as f: # with io.open(f_ddl_log.name, 'r', encoding='utf8' ) as f:
# result_log = f.readlines() # result_log = f.readlines()
# #
# for i in result_log: # for i in result_log:
# print( i.encode('utf8') ) # do not miss '.encode()' here, otherwise get: "ordinal not in range(128)" # print( i.encode('utf8') ) # do not miss '.encode()' here, otherwise get: "ordinal not in range(128)"
# #
# f_run_log = io.open( os.path.join(context['temp_directory'], 'tmp_4743_utf8_run.log'), 'w', encoding = 'utf8' ) # f_run_log = io.open( os.path.join(context['temp_directory'], 'tmp_4743_utf8_run.log'), 'w', encoding = 'utf8' )
# #
# con = fdb.connect(dsn = dsn, user = "Вася Пупкин", password = '123', role = 'Старший дворник', charset = 'utf8', utf8params = True) # con = fdb.connect(dsn = dsn, user = "Вася Пупкин", password = '123', role = 'Старший дворник', charset = 'utf8', utf8params = True)
# cur = con.cursor() # cur = con.cursor()
# cur.execute('select m.mon$user,m.mon$role from mon$attachments m where m.mon$attachment_id = current_connection') # cur.execute('select m.mon$user,m.mon$role from mon$attachments m where m.mon$attachment_id = current_connection')
@ -218,20 +218,20 @@ db_1 = db_factory(charset='UTF8', sql_dialect=3, init=init_script_1)
# for r in cur: # for r in cur:
# for i in range(0,len(col)): # for i in range(0,len(col)):
# f_run_log.write( ' '.join((col[i][0],':',r[i], '\\n')) ) # f_run_log.write( ' '.join((col[i][0],':',r[i], '\\n')) )
# #
# cur.execute('select v.* from v_current_privileges v') # cur.execute('select v.* from v_current_privileges v')
# col = cur.description # col = cur.description
# for r in cur: # for r in cur:
# for i in range(0,len(col)): # for i in range(0,len(col)):
# if 'privilege:' not in col[i][0] or 'privilege:' in col[i][0] and r[i] == 'YES': # if 'privilege:' not in col[i][0] or 'privilege:' in col[i][0] and r[i] == 'YES':
# f_run_log.write( ' '.join((col[i][0],':',r[i], '\\n')) ) # f_run_log.write( ' '.join((col[i][0],':',r[i], '\\n')) )
# #
# flush_and_close( f_run_log ) # flush_and_close( f_run_log )
# #
# # Check that privileges actually work for current (non-ascii) user / role: # # Check that privileges actually work for current (non-ascii) user / role:
# ##################################### # #####################################
# # All following actions must not raise any exception: # # All following actions must not raise any exception:
# #
# ''' # '''
# ### DEFERRED ### # ### DEFERRED ###
# Got exception on Linux: # Got exception on Linux:
@ -242,19 +242,19 @@ db_1 = db_factory(charset='UTF8', sql_dialect=3, init=init_script_1)
# - "Документы" # - "Документы"
# -104 # -104
# 335544569 # 335544569
# #
# con.execute_immediate('insert into "Документы"(id) values(gen_id("ИД_документа",1))') # con.execute_immediate('insert into "Документы"(id) values(gen_id("ИД_документа",1))')
# cur.callproc('"Хранимка"') # cur.callproc('"Хранимка"')
# cur.execute('select "СтрВЧисло"(?) from rdb$database', (123,)) # cur.execute('select "СтрВЧисло"(?) from rdb$database', (123,))
# for r in cur: # for r in cur:
# pass # pass
# #
# cur.callproc('"Утилиты".pg_sp_worker') # cur.callproc('"Утилиты".pg_sp_worker')
# ''' # '''
# #
# cur.close() # cur.close()
# con.close() # con.close()
# #
# # Generate SQL script for DROP non-ascii user. # # Generate SQL script for DROP non-ascii user.
# ############################################## # ##############################################
# sql_txt=''' # sql_txt='''
@ -268,41 +268,123 @@ db_1 = db_factory(charset='UTF8', sql_dialect=3, init=init_script_1)
# commit; # commit;
# select count(*) non_ascii_user_after_drop from sec$users where sec$user_name ='Вася Пупкин'; # select count(*) non_ascii_user_after_drop from sec$users where sec$user_name ='Вася Пупкин';
# ''' % dict(globals(), **locals()) # ''' % dict(globals(), **locals())
# #
# f_drop_sql = open( os.path.join(context['temp_directory'], 'tmp_4743_utf8_drop.sql'), 'w' ) # f_drop_sql = open( os.path.join(context['temp_directory'], 'tmp_4743_utf8_drop.sql'), 'w' )
# f_drop_sql.write( sql_txt ) # f_drop_sql.write( sql_txt )
# flush_and_close( f_drop_sql ) # flush_and_close( f_drop_sql )
# #
# f_drop_log = open( os.path.splitext(f_drop_sql.name)[0]+'.log', 'w') # f_drop_log = open( os.path.splitext(f_drop_sql.name)[0]+'.log', 'w')
# subprocess.call( [ context['isql_path'], '-q', '-i', f_drop_sql.name ], # subprocess.call( [ context['isql_path'], '-q', '-i', f_drop_sql.name ],
# stdout = f_drop_log, # stdout = f_drop_log,
# stderr = subprocess.STDOUT # stderr = subprocess.STDOUT
# ) # )
# flush_and_close( f_drop_log ) # flush_and_close( f_drop_log )
# #
# with io.open(f_run_log.name, 'r', encoding='utf8' ) as f: # with io.open(f_run_log.name, 'r', encoding='utf8' ) as f:
# result_in_utf8 = f.readlines() # result_in_utf8 = f.readlines()
# #
# for i in result_in_utf8: # for i in result_in_utf8:
# print( i.encode('utf8') ) # print( i.encode('utf8') )
# #
# with open(f_drop_log.name,'r') as f: # with open(f_drop_log.name,'r') as f:
# for line in f: # for line in f:
# print(line) # print(line)
# #
# # cleanup: # # cleanup:
# ########### # ###########
# time.sleep(2) # time.sleep(2)
# #
# # DO NOT use here: cleanup( (f_ddl_sql, f_ddl_log, f_drop_sql, f_drop_log, f_run_log) ) -- # # DO NOT use here: cleanup( (f_ddl_sql, f_ddl_log, f_drop_sql, f_drop_log, f_run_log) ) --
# # Unrecognized type of element: <closed file 'C:\\FBTESTING\\qa\\fbt-repo\\tmp\\tmp_4743_utf8_run.log', mode 'wb' at 0x0000000005A20780> - can not be treated as file. # # Unrecognized type of element: <closed file 'C:\\FBTESTING\\qa\\fbt-repo\\tmp\\tmp_4743_utf8_run.log', mode 'wb' at 0x0000000005A20780> - can not be treated as file.
# # type(f_names_list[i])= <type 'instance'>Traceback (most recent call last): # # type(f_names_list[i])= <type 'instance'>Traceback (most recent call last):
# #
# cleanup( [i.name for i in (f_ddl_sql, f_ddl_log, f_drop_sql, f_drop_log, f_run_log)] ) # cleanup( [i.name for i in (f_ddl_sql, f_ddl_log, f_drop_sql, f_drop_log, f_run_log)] )
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
ddl_script_1 = """
grant "Старший дворник" to "Вася Пупкин";
commit;
create table "Документы"(id int primary key, pid int references "Документы");
create exception "НЕ_число" 'Ваша строка не может быть преобразована в число.';
create sequence "ИД_документа";
set term ^;
create procedure "Хранимка" as
begin
end
^
create function "СтрВЧисло"(a_text varchar(100)) returns int as
begin
return 0;
end
^
create or alter package "Утилиты" as
begin
procedure pg_sp_worker;
end
^
recreate package body "Утилиты" as
begin
procedure pg_sp_worker as
begin
end
end
^
set term ;^
commit;
create or alter view v_current_privileges as
select
g.rdb$user as who_is_granted
,g.rdb$relation_name as obj_name
,decode( g.rdb$object_type
,0,'table'
,1,'view'
,2,'trigger'
,5,'procedure'
,7,'exception'
,9,'domain'
,11,'charset'
,13,'role'
,14,'generator'
,15,'function'
,16,'blob filt'
,18,'package'
,22,'systable'
,cast(g.rdb$object_type as varchar(50))
) as obj_type
,max(iif(g.rdb$privilege='S','YES',' ')) as "privilege:select"
,max(iif(g.rdb$privilege='I','YES',' ')) as "privilege:insert"
,max(iif(g.rdb$privilege='U','YES',' ')) as "privilege:update"
,max(iif(g.rdb$privilege='D','YES',' ')) as "privilege:delete"
,max(iif(g.rdb$privilege='G','YES',' ')) as "privilege:usage"
,max(iif(g.rdb$privilege='X','YES',' ')) as "privilege:exec"
,max(iif(g.rdb$privilege='R','YES',' ')) as "privilege:refer"
,max(iif(g.rdb$privilege='C','YES',' ')) as "privilege:create"
,max(iif(g.rdb$privilege='L','YES',' ')) as "privilege:alter"
,max(iif(g.rdb$privilege='O','YES',' ')) as "privilege:drop"
,max(iif(g.rdb$privilege='M','YES',' ')) as "privilege:member"
from rdb$user_privileges g
where g.rdb$user in( current_user, current_role )
group by 1,2,3;
grant select on v_current_privileges to "Старший дворник";
grant select,insert,update,delete,references on "Документы" to "Старший дворник";
grant usage on exception "НЕ_число" to "Старший дворник";
grant usage on sequence "ИД_документа" to "Старший дворник";
grant execute on procedure "Хранимка" to "Старший дворник";
grant execute on function "СтрВЧисло" to "Старший дворник";
grant execute on package "Утилиты" to "Старший дворник";
grant create table to "Старший дворник";
grant alter any table to "Старший дворник";
grant drop any table to "Старший дворник";
commit;
"""
expected_stdout_1 = """ expected_stdout_1 = """
MON$USER : Вася Пупкин MON$USER : Вася Пупкин
@ -358,14 +440,31 @@ expected_stdout_1 = """
OBJ_NAME : Хранимка OBJ_NAME : Хранимка
OBJ_TYPE : procedure OBJ_TYPE : procedure
privilege:exec : YES privilege:exec : YES
"""
NON_ASCII_USER_BEFORE_DROP 1 non_acii_user = user_factory(name='"Вася Пупкин"', password= '123', encoding= 'utf8')
NON_ASCII_USER_AFTER_DROP 0
"""
@pytest.mark.version('>=4.0') @pytest.mark.version('>=4.0')
@pytest.mark.xfail def test_1(act_1: Action, non_acii_user: User, capsys):
def test_1(db_1): with act_1.test_role('"Старший дворник"', charset= 'utf8'):
pytest.fail("Test not IMPLEMENTED") act_1.isql(switches=['-b', '-q'], input=ddl_script_1)
print(act_1.stdout)
with act_1.db.connect(user=non_acii_user.name, password=non_acii_user.password,
role='"Старший дворник"') as con:
cur = con.cursor()
cur.execute('select m.mon$user,m.mon$role from mon$attachments m where m.mon$attachment_id = current_connection')
col = cur.description
for r in cur:
for i in range(len(col)):
print(' '.join((col[i][0], ':', r[i])))
cur.execute("select v.* from v_current_privileges v")
col = cur.description
for r in cur:
for i in range(len(col)):
if 'privilege:' not in col[i][0] or 'privilege:' in col[i][0] and r[i] == 'YES':
print(' '.join((col[i][0], ':', r[i])))
#
act_1.reset()
act_1.expected_stdout = expected_stdout_1
act_1.stdout = capsys.readouterr().out
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,25 +2,26 @@
# #
# id: bugs.core_4754 # id: bugs.core_4754
# title: Bugcheck 167 (invalid SEND request) while working with GTT from several attachments (using EXECUTE STATEMENT ... ON EXTERNAL and different roles) # title: Bugcheck 167 (invalid SEND request) while working with GTT from several attachments (using EXECUTE STATEMENT ... ON EXTERNAL and different roles)
# decription: # decription:
# We start two transactions and do DML ('insert ...') in 1st and DDL ('create index ...') in second. # We start two transactions and do DML ('insert ...') in 1st and DDL ('create index ...') in second.
# Then we issue COMMIT in DDL transaction. This should raise 'lock conflict ... table "<name>" is in use'. # Then we issue COMMIT in DDL transaction. This should raise 'lock conflict ... table "<name>" is in use'.
# On 2.5.4 this COMMIT in DDL did NOT raise any error and subsequent reconnect and DML raised bugcheck. # On 2.5.4 this COMMIT in DDL did NOT raise any error and subsequent reconnect and DML raised bugcheck.
# #
# Checked on 2.5.6.27013, 3.0.1.32524 - works OK. # Checked on 2.5.6.27013, 3.0.1.32524 - works OK.
# Bugcheck can be reproduced on 2.5.4.26856. # Bugcheck can be reproduced on 2.5.4.26856.
# #
# PS. Old ticket name: # PS. Old ticket name:
# Manipulations with GTT from several attachments (using ES/EDS and different roles) leads to: # Manipulations with GTT from several attachments (using ES/EDS and different roles) leads to:
# "internal Firebird consistency check (invalid SEND request (167), file: JrdStatement.cpp line: 325)" # "internal Firebird consistency check (invalid SEND request (167), file: JrdStatement.cpp line: 325)"
# #
# tracker_id: CORE-4754 # tracker_id: CORE-4754
# min_versions: ['2.5.5'] # min_versions: ['2.5.5']
# versions: 2.5.5 # versions: 2.5.5
# qmid: # qmid:
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, python_act, Action
from firebird.driver import TPB, Isolation, TraAccessMode, DatabaseError
# version: 2.5.5 # version: 2.5.5
# resources: None # resources: None
@ -30,26 +31,26 @@ substitutions_1 = []
init_script_1 = """ init_script_1 = """
recreate global temporary table gtt_session(x int, y int) on commit preserve rows; recreate global temporary table gtt_session(x int, y int) on commit preserve rows;
commit; commit;
""" """
db_1 = db_factory(sql_dialect=3, init=init_script_1) db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# import subprocess # import subprocess
# import fdb # import fdb
# #
# db_conn.close() # db_conn.close()
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #db_file="$(DATABASE_LOCATION)bugs.core_4754.fdb" # #db_file="$(DATABASE_LOCATION)bugs.core_4754.fdb"
# #
# customTPB = ( [ fdb.isc_tpb_read_committed, fdb.isc_tpb_rec_version, fdb.isc_tpb_nowait ] ) # customTPB = ( [ fdb.isc_tpb_read_committed, fdb.isc_tpb_rec_version, fdb.isc_tpb_nowait ] )
# con1 = fdb.connect(dsn=dsn) # con1 = fdb.connect(dsn=dsn)
# #print(con1.firebird_version) # #print(con1.firebird_version)
# #
# tx1a=con1.trans( default_tpb = customTPB ) # tx1a=con1.trans( default_tpb = customTPB )
# tx1b=con1.trans( default_tpb = customTPB ) # tx1b=con1.trans( default_tpb = customTPB )
# cur1a = tx1a.cursor() # cur1a = tx1a.cursor()
@ -64,12 +65,12 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# print('Error-1:') # print('Error-1:')
# msg = e[0] # msg = e[0]
# print(msg) # print(msg)
# #
# con1.close() # con1.close()
# #
# #
# # --------------------------------------------------------------- # # ---------------------------------------------------------------
# #
# if not msg.split(): # if not msg.split():
# # 2.5.5: control should NOT pass here at all! # # 2.5.5: control should NOT pass here at all!
# con2 = fdb.connect(dsn=dsn) # con2 = fdb.connect(dsn=dsn)
@ -93,22 +94,48 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# print('Error-2:') # print('Error-2:')
# print(e[0]) # print(e[0])
# con2.close() # con2.close()
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
Error-1: Error-1:
Error while commiting transaction: lock conflict on no wait transaction
- SQLCODE: -901 -unsuccessful metadata update
- lock conflict on no wait transaction -object TABLE "GTT_SESSION" is in use
- unsuccessful metadata update """
- object TABLE "GTT_SESSION" is in use
"""
@pytest.mark.version('>=2.5.5') @pytest.mark.version('>=2.5.5')
@pytest.mark.xfail def test_1(act_1: Action, capsys):
def test_1(db_1): custom_tpb = TPB(isolation=Isolation.READ_COMMITTED_RECORD_VERSION,
pytest.fail("Test not IMPLEMENTED") access_mode=TraAccessMode.WRITE, lock_timeout=0).get_buffer()
with act_1.db.connect() as con1:
tx1a = con1.transaction_manager(custom_tpb)
cur1a = tx1a.cursor()
tx1b = con1.transaction_manager(custom_tpb)
cur1b = tx1b.cursor()
try:
cur1a.execute("insert into gtt_session select rand()*10, rand()*10 from rdb$types")
cur1b.execute("create index gtt_session_x_y on gtt_session computed by (x+y)")
tx1b.commit() # WI-V2.5.6.27013 issues here: lock conflict on no wait transaction unsuccessful metadata update object TABLE "GTT_SESSION" is in use -901 335544345
tx1a.commit()
except DatabaseError as e:
print('Error-1:')
msg = e.args[0]
print(msg)
#
if not msg.split():
# 2.5.5: control should NOT pass here at all!
with act_1.db.connect() as con2:
try:
tx2a = con2.transaction_manager()
cur2a = tx2a.cursor()
cur2a.execute("insert into gtt_session select rand()*11, rand()*11 from rdb$types")
except DatabaseError as e:
print('Error-2:')
print(e.args[0])
#
act_1.expected_stdout = expected_stdout_1
act_1.stdout = capsys.readouterr().out
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,22 +2,26 @@
# #
# id: bugs.core_4760 # id: bugs.core_4760
# title: Can not create user with non-ascii (multi-byte) characters in it's name # title: Can not create user with non-ascii (multi-byte) characters in it's name
# decription: # decription:
# User with name Εὐκλείδης ('Euclid') encoded in UTF8 is used in this test. # User with name Εὐκλείδης ('Euclid') encoded in UTF8 is used in this test.
# #
# NB-1: connection is made using FDB connect() method: ISQL (and also its CONNECT statement) has # NB-1: connection is made using FDB connect() method: ISQL (and also its CONNECT statement) has
# problems when trying to use non-ascii names. # problems when trying to use non-ascii names.
# NB-2: separate SQL script is generated for DROP this user. # NB-2: separate SQL script is generated for DROP this user.
# #
# Checked on: 4.0.0.2416 (Windows and Linux) # Checked on: 4.0.0.2416 (Windows and Linux)
# #
# tracker_id: # [pcisar] 24.11.2021
# 1. This problem is covered by test for core_4743 as side effect
# 2. For sake of completness, it was reimplemented by simply using
# user_factory fixture.
# tracker_id:
# min_versions: ['4.0'] # min_versions: ['4.0']
# versions: 4.0 # versions: 4.0
# qmid: bugs.core_4760 # qmid: bugs.core_4760
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, python_act, Action, user_factory, User
# version: 4.0 # version: 4.0
# resources: None # resources: None
@ -30,31 +34,31 @@ db_1 = db_factory(charset='UTF8', sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# import io # import io
# import subprocess # import subprocess
# import time # import time
# #
# db_conn.close() # db_conn.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close( file_handle ): # def flush_and_close( file_handle ):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull: # if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull:
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -66,38 +70,38 @@ db_1 = db_factory(charset='UTF8', sql_dialect=3, init=init_script_1)
# print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.') # print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.')
# print('type(f_names_list[i])=',type(f_names_list[i])) # print('type(f_names_list[i])=',type(f_names_list[i]))
# del_name = None # del_name = None
# #
# if del_name and os.path.isfile( del_name ): # if del_name and os.path.isfile( del_name ):
# os.remove( del_name ) # os.remove( del_name )
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# sql_txt=''' set bail on; # sql_txt=''' set bail on;
# set names utf8; # set names utf8;
# connect '%(dsn)s' user '%(user_name)s' password '%(user_password)s'; # connect '%(dsn)s' user '%(user_name)s' password '%(user_password)s';
# #
# create or alter user "Εὐκλείδης" password '123' using plugin Srp; # create or alter user "Εὐκλείδης" password '123' using plugin Srp;
# ''' % dict(globals(), **locals()) # ''' % dict(globals(), **locals())
# #
# f_ddl_sql = open( os.path.join(context['temp_directory'], 'tmp_4760_utf8_ddl.sql'), 'w' ) # f_ddl_sql = open( os.path.join(context['temp_directory'], 'tmp_4760_utf8_ddl.sql'), 'w' )
# f_ddl_sql.write( sql_txt ) # f_ddl_sql.write( sql_txt )
# flush_and_close( f_ddl_sql ) # flush_and_close( f_ddl_sql )
# #
# f_ddl_log = open( os.path.splitext(f_ddl_sql.name)[0]+'.log', 'w') # f_ddl_log = open( os.path.splitext(f_ddl_sql.name)[0]+'.log', 'w')
# subprocess.call( [ context['isql_path'], '-q', '-i', f_ddl_sql.name ], # subprocess.call( [ context['isql_path'], '-q', '-i', f_ddl_sql.name ],
# stdout = f_ddl_log, # stdout = f_ddl_log,
# stderr = subprocess.STDOUT # stderr = subprocess.STDOUT
# ) # )
# flush_and_close( f_ddl_log ) # flush_and_close( f_ddl_log )
# #
# with io.open(f_ddl_log.name, 'r', encoding='utf8' ) as f: # with io.open(f_ddl_log.name, 'r', encoding='utf8' ) as f:
# result_log = f.readlines() # result_log = f.readlines()
# #
# for i in result_log: # for i in result_log:
# print( i.encode('utf8') ) # do not miss '.encode()' here, otherwise get: "ordinal not in range(128)" # print( i.encode('utf8') ) # do not miss '.encode()' here, otherwise get: "ordinal not in range(128)"
# #
# f_run_log = io.open( os.path.join(context['temp_directory'], 'tmp_4760_utf8_run.log'), 'w', encoding = 'utf8' ) # f_run_log = io.open( os.path.join(context['temp_directory'], 'tmp_4760_utf8_run.log'), 'w', encoding = 'utf8' )
# #
# con = fdb.connect(dsn = dsn, user = "Εὐκλείδης", password = '123', charset = 'utf8', utf8params = True) # con = fdb.connect(dsn = dsn, user = "Εὐκλείδης", password = '123', charset = 'utf8', utf8params = True)
# cur = con.cursor() # cur = con.cursor()
# cur.execute('select m.mon$user from mon$attachments m where m.mon$attachment_id = current_connection') # cur.execute('select m.mon$user from mon$attachments m where m.mon$attachment_id = current_connection')
@ -105,11 +109,11 @@ db_1 = db_factory(charset='UTF8', sql_dialect=3, init=init_script_1)
# for r in cur: # for r in cur:
# for i in range(0,len(col)): # for i in range(0,len(col)):
# f_run_log.write( ' '.join((col[i][0],':',r[i], '\\n')) ) # f_run_log.write( ' '.join((col[i][0],':',r[i], '\\n')) )
# #
# cur.close() # cur.close()
# con.close() # con.close()
# flush_and_close(f_run_log) # flush_and_close(f_run_log)
# #
# # Generate SQL script for DROP non-ascii user. # # Generate SQL script for DROP non-ascii user.
# ############################################## # ##############################################
# sql_txt=''' # sql_txt='''
@ -123,52 +127,55 @@ db_1 = db_factory(charset='UTF8', sql_dialect=3, init=init_script_1)
# commit; # commit;
# select count(*) non_ascii_user_after_drop from sec$users where sec$user_name ='Εὐκλείδης'; # select count(*) non_ascii_user_after_drop from sec$users where sec$user_name ='Εὐκλείδης';
# ''' % dict(globals(), **locals()) # ''' % dict(globals(), **locals())
# #
# f_drop_sql = open( os.path.join(context['temp_directory'], 'tmp_4760_utf8_drop.sql'), 'w' ) # f_drop_sql = open( os.path.join(context['temp_directory'], 'tmp_4760_utf8_drop.sql'), 'w' )
# f_drop_sql.write( sql_txt ) # f_drop_sql.write( sql_txt )
# flush_and_close( f_drop_sql ) # flush_and_close( f_drop_sql )
# #
# f_drop_log = open( os.path.splitext(f_drop_sql.name)[0]+'.log', 'w') # f_drop_log = open( os.path.splitext(f_drop_sql.name)[0]+'.log', 'w')
# subprocess.call( [ context['isql_path'], '-q', '-i', f_drop_sql.name ], # subprocess.call( [ context['isql_path'], '-q', '-i', f_drop_sql.name ],
# stdout = f_drop_log, # stdout = f_drop_log,
# stderr = subprocess.STDOUT # stderr = subprocess.STDOUT
# ) # )
# flush_and_close( f_drop_log ) # flush_and_close( f_drop_log )
# #
# with io.open(f_run_log.name, 'r', encoding='utf8' ) as f: # with io.open(f_run_log.name, 'r', encoding='utf8' ) as f:
# result_in_utf8 = f.readlines() # result_in_utf8 = f.readlines()
# #
# #
# for i in result_in_utf8: # for i in result_in_utf8:
# print( i.encode('utf8') ) # print( i.encode('utf8') )
# #
# with open(f_drop_log.name,'r') as f: # with open(f_drop_log.name,'r') as f:
# for line in f: # for line in f:
# print(line) # print(line)
# #
# # cleanup: # # cleanup:
# ########### # ###########
# time.sleep(2) # time.sleep(2)
# #
# # DO NOT use here: cleanup( (f_ddl_sql, f_ddl_log, f_drop_sql, f_drop_log, f_run_log) ) -- # # DO NOT use here: cleanup( (f_ddl_sql, f_ddl_log, f_drop_sql, f_drop_log, f_run_log) ) --
# # Unrecognized type of element: <closed file 'C:\\FBTESTING\\qa\\fbt-repo\\tmp\\tmp_4760_utf8_run.log', mode 'wb' at 0x0000000005A20780> - can not be treated as file. # # Unrecognized type of element: <closed file 'C:\\FBTESTING\\qa\\fbt-repo\\tmp\\tmp_4760_utf8_run.log', mode 'wb' at 0x0000000005A20780> - can not be treated as file.
# # type(f_names_list[i])= <type 'instance'>Traceback (most recent call last): # # type(f_names_list[i])= <type 'instance'>Traceback (most recent call last):
# #
# cleanup( [i.name for i in (f_ddl_sql, f_ddl_log, f_drop_sql, f_drop_log, f_run_log)] ) # cleanup( [i.name for i in (f_ddl_sql, f_ddl_log, f_drop_sql, f_drop_log, f_run_log)] )
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
expected_stdout_1 = """ act_1 = python_act('db_1', substitutions=substitutions_1)
MON$USER : Εὐκλείδης
NON_ASCII_USER_BEFORE_DROP 1 #expected_stdout_1 = """
NON_ASCII_USER_AFTER_DROP 0 #MON$USER : Εὐκλείδης
""" #NON_ASCII_USER_BEFORE_DROP 1
#NON_ASCII_USER_AFTER_DROP 0
#"""
non_ascii_user = user_factory(name='"Εὐκλείδης"', password='123', encoding='utf8')
@pytest.mark.version('>=4.0') @pytest.mark.version('>=4.0')
@pytest.mark.xfail def test_1(act_1: Action, non_ascii_user: User):
def test_1(db_1): with act_1.db.connect(user=non_ascii_user.name, password=non_ascii_user.password) as con:
pytest.fail("Test not IMPLEMENTED") pass

View File

@ -2,26 +2,26 @@
# #
# id: bugs.core_4766 # id: bugs.core_4766
# title: AV when trying to manage users list using EXECUTE STATEMENT on behalf of non-sysdba user which has RDB$ADMIN role # title: AV when trying to manage users list using EXECUTE STATEMENT on behalf of non-sysdba user which has RDB$ADMIN role
# decription: # decription:
# 05.01.2020. Refactored in order to make its code more flexible because 3.0 and 4.0 have significant differences in stdout/stderr. # 05.01.2020. Refactored in order to make its code more flexible because 3.0 and 4.0 have significant differences in stdout/stderr.
# Common SQL code was stored in fbt-repo # Common SQL code was stored in fbt-repo
# iles\\core_4766.sql with embedding variable names there from here: # iles\\core_4766.sql with embedding variable names there from here:
# %(current_auth_plugin)s, %(dsn)s et al. # %(current_auth_plugin)s, %(dsn)s et al.
# #
# Content of this file is stored in variable 'sql_text' and this variable is changed using common Python rule for substitutions: # Content of this file is stored in variable 'sql_text' and this variable is changed using common Python rule for substitutions:
# sql_text % dict(globals(), **locals() # sql_text % dict(globals(), **locals()
# #
# Then we save this variable to temporarily .sql script and run it. # Then we save this variable to temporarily .sql script and run it.
# This action is done for two possible values of auth plugin (see var. current_auth_plugin): Srp and Legacy_UserManager. # This action is done for two possible values of auth plugin (see var. current_auth_plugin): Srp and Legacy_UserManager.
# #
# As result, we have to compare only expected* data for different FB major versions. # As result, we have to compare only expected* data for different FB major versions.
# #
# ::: NB ::: # ::: NB :::
# Only Legacy_UserManager is checked for FB 4.0. Srp has totally different behaviour, at least for 4.0.0.1714. # Only Legacy_UserManager is checked for FB 4.0. Srp has totally different behaviour, at least for 4.0.0.1714.
# Sent letter to dimitr and alex, 05.01.2020 22:00. # Sent letter to dimitr and alex, 05.01.2020 22:00.
# #
# Crash is reproduced on WI-T3.0.0.31374 Firebird 3.0 Beta 1 (build 24-nov-2014). # Crash is reproduced on WI-T3.0.0.31374 Firebird 3.0 Beta 1 (build 24-nov-2014).
# #
# Checked on: # Checked on:
# 4.0.0.1743 SS: 1.452s. # 4.0.0.1743 SS: 1.452s.
# 4.0.0.1740 SC: 1.870s. # 4.0.0.1740 SC: 1.870s.
@ -29,20 +29,28 @@
# 3.0.6.33236 SS: 1.220s. # 3.0.6.33236 SS: 1.220s.
# 3.0.5.33221 SC: 5.416s. # 3.0.5.33221 SC: 5.416s.
# 3.0.5.33084 CS: 2.891s. # 3.0.5.33084 CS: 2.891s.
# #
# # [pcisar] 24.11.2021
# On FB v4.0.0.2496 this test fails as provided script file raises error in
# execute block->execute statement->create/drop user:
# Statement failed, SQLSTATE = 28000
# Your user name and password are not defined. Ask your database administrator to set up a Firebird login.
# -At block line: 3, col: 9
# Variant for FB 3 not yet implemented.
#
# tracker_id: CORE-4766 # tracker_id: CORE-4766
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0, 4.0 # versions: 3.0, 4.0
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, python_act, Action
# version: 3.0 # version: 3.0
# resources: None # resources: None
substitutions_1 = [('TCPv.*', 'TCP'), ('.*After line \\d+.*', ''), ('find/delete', 'delete'), ('TABLE PLG\\$.*', 'TABLE PLG')] substitutions_1 = [('TCPv.*', 'TCP'), ('.*After line \\d+.*', ''),
('find/delete', 'delete'), ('TABLE PLG\\$.*', 'TABLE PLG')]
init_script_1 = """""" init_script_1 = """"""
@ -50,65 +58,66 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# import sys # import sys
# import subprocess # import subprocess
# from fdb import services # from fdb import services
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# this_db = db_conn.database_name # this_db = db_conn.database_name
# db_conn.close() # db_conn.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close(file_handle): # def flush_and_close(file_handle):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb'): # if file_handle.mode not in ('r', 'rb'):
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
# if os.path.isfile( f_names_list[i]): # if os.path.isfile( f_names_list[i]):
# os.remove( f_names_list[i] ) # os.remove( f_names_list[i] )
# #-------------------------------------------- # #--------------------------------------------
# #
# f_sql=open(os.path.join(context['files_location'],'core_4766.sql'),'r') # f_sql=open(os.path.join(context['files_location'],'core_4766.sql'),'r')
# sql_text = f_sql.read() # sql_text = f_sql.read()
# f_sql.close() # f_sql.close()
# #
# for current_auth_plugin in ('Srp', 'Legacy_UserManager'): # for current_auth_plugin in ('Srp', 'Legacy_UserManager'):
# f_sql_chk = open( os.path.join(context['temp_directory'],'tmp_core_4766.' + current_auth_plugin[:3] + '.sql'), 'w') # f_sql_chk = open( os.path.join(context['temp_directory'],'tmp_core_4766.' + current_auth_plugin[:3] + '.sql'), 'w')
# f_sql_chk.write( sql_text % dict(globals(), **locals()) ) # f_sql_chk.write( sql_text % dict(globals(), **locals()) )
# flush_and_close( f_sql_chk ) # flush_and_close( f_sql_chk )
# #
# f_sql_log = open( '.'.join( (os.path.splitext( f_sql_chk.name )[0], 'log') ), 'w') # f_sql_log = open( '.'.join( (os.path.splitext( f_sql_chk.name )[0], 'log') ), 'w')
# subprocess.call( [ context['isql_path'], '-q', '-i', f_sql_chk.name ], stdout = f_sql_log, stderr = subprocess.STDOUT) # subprocess.call( [ context['isql_path'], '-q', '-i', f_sql_chk.name ], stdout = f_sql_log, stderr = subprocess.STDOUT)
# flush_and_close( f_sql_log ) # flush_and_close( f_sql_log )
# #
# with open( f_sql_log.name,'r') as f: # with open( f_sql_log.name,'r') as f:
# for line in f: # for line in f:
# if line.strip(): # if line.strip():
# print( current_auth_plugin[:3] + ': ' + line ) # print( current_auth_plugin[:3] + ': ' + line )
# #
# cleanup( (f_sql_log.name, f_sql_chk.name) ) # cleanup( (f_sql_log.name, f_sql_chk.name) )
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
Srp: BOSS_SEC_NAME TMP_4766_BOSS Srp: BOSS_SEC_NAME TMP_4766_BOSS
@ -141,18 +150,19 @@ expected_stdout_1 = """
Leg: find/delete record error Leg: find/delete record error
Leg: -no permission for DELETE access to TABLE PLG$VIEW_USERS Leg: -no permission for DELETE access to TABLE PLG$VIEW_USERS
""" """
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0,<4')
@pytest.mark.xfail @pytest.mark.xfail
def test_1(db_1): def test_1(act_1: Action):
pytest.fail("Test not IMPLEMENTED") pytest.fail("Test not IMPLEMENTED")
# version: 4.0 # version: 4.0
# resources: None # resources: None
substitutions_2 = [('TCPv.*', 'TCP'), ('.*After line \\d+.*', ''), ('find/delete', 'delete'), ('TABLE PLG\\$.*', 'TABLE PLG')] substitutions_2 = [('TCPv.*', 'TCP'), ('.*After line \\d+.*', ''),
('find/delete', 'delete'), ('TABLE PLG\\$.*', 'TABLE PLG')]
init_script_2 = """""" init_script_2 = """"""
@ -160,67 +170,67 @@ db_2 = db_factory(sql_dialect=3, init=init_script_2)
# test_script_2 # test_script_2
#--- #---
# #
# import os # import os
# import sys # import sys
# import subprocess # import subprocess
# from fdb import services # from fdb import services
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# this_db = db_conn.database_name # this_db = db_conn.database_name
# db_conn.close() # db_conn.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close(file_handle): # def flush_and_close(file_handle):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb'): # if file_handle.mode not in ('r', 'rb'):
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
# if os.path.isfile( f_names_list[i]): # if os.path.isfile( f_names_list[i]):
# os.remove( f_names_list[i] ) # os.remove( f_names_list[i] )
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# f_sql=open(os.path.join(context['files_location'],'core_4766.sql'),'r') # f_sql=open(os.path.join(context['files_location'],'core_4766.sql'),'r')
# sql_text = f_sql.read() # sql_text = f_sql.read()
# f_sql.close() # f_sql.close()
# #
# # ::: NB ::: # # ::: NB :::
# # Only Legacy_UserManager is checked for FB 4.0. Srp has totally different behaviour, at least for 4.0.0.1714. # # Only Legacy_UserManager is checked for FB 4.0. Srp has totally different behaviour, at least for 4.0.0.1714.
# # Sent letter to dimitr and alex, 05.01.2020 22:00. # # Sent letter to dimitr and alex, 05.01.2020 22:00.
# #
# for current_auth_plugin in ('Legacy_UserManager',): # for current_auth_plugin in ('Legacy_UserManager',):
# f_sql_chk = open( os.path.join(context['temp_directory'],'tmp_core_4766.' + current_auth_plugin[:3] + '.sql'), 'w') # f_sql_chk = open( os.path.join(context['temp_directory'],'tmp_core_4766.' + current_auth_plugin[:3] + '.sql'), 'w')
# f_sql_chk.write( sql_text % dict(globals(), **locals()) ) # f_sql_chk.write( sql_text % dict(globals(), **locals()) )
# flush_and_close( f_sql_chk ) # flush_and_close( f_sql_chk )
# #
# f_sql_log = open( '.'.join( (os.path.splitext( f_sql_chk.name )[0], 'log') ), 'w') # f_sql_log = open( '.'.join( (os.path.splitext( f_sql_chk.name )[0], 'log') ), 'w')
# subprocess.call( [ context['isql_path'], '-q', '-i', f_sql_chk.name ], stdout = f_sql_log, stderr = subprocess.STDOUT) # subprocess.call( [ context['isql_path'], '-q', '-i', f_sql_chk.name ], stdout = f_sql_log, stderr = subprocess.STDOUT)
# flush_and_close( f_sql_log ) # flush_and_close( f_sql_log )
# #
# with open( f_sql_log.name,'r') as f: # with open( f_sql_log.name,'r') as f:
# for line in f: # for line in f:
# if line.strip(): # if line.strip():
# print( current_auth_plugin[:3] + ': ' + line ) # print( current_auth_plugin[:3] + ': ' + line )
# #
# cleanup( (f_sql_log.name, f_sql_chk.name) ) # cleanup( (f_sql_log.name, f_sql_chk.name) )
# #
# ''' # '''
# 'substitutions':[ # 'substitutions':[
# ('TCPv.*', 'TCP'), # ('TCPv.*', 'TCP'),
@ -229,10 +239,11 @@ db_2 = db_factory(sql_dialect=3, init=init_script_2)
# ('TABLE PLG\\$.*', 'TABLE PLG') # ('TABLE PLG\\$.*', 'TABLE PLG')
# ] # ]
# ''' # '''
# #
# #
#--- #---
#act_2 = python_act('db_2', test_script_2, substitutions=substitutions_2)
act_2 = python_act('db_2', substitutions=substitutions_2)
expected_stdout_2 = """ expected_stdout_2 = """
Leg: BOSS_SEC_NAME TMP_4766_BOSS Leg: BOSS_SEC_NAME TMP_4766_BOSS
@ -252,11 +263,24 @@ expected_stdout_2 = """
Leg: find/delete record error Leg: find/delete record error
Leg: -no permission for DELETE access to TABLE PLG$VIEW_USERS Leg: -no permission for DELETE access to TABLE PLG$VIEW_USERS
Leg: -Effective user is TMP_4766_BOSS Leg: -Effective user is TMP_4766_BOSS
""" """
@pytest.mark.version('>=4.0') @pytest.mark.version('>=4.0')
@pytest.mark.xfail def test_2(act_2: Action, capsys):
def test_2(db_2): sql_text = (act_2.vars['files'] / 'core_4766.sql').read_text()
pytest.fail("Test not IMPLEMENTED") # ::: NB :::
# Only Legacy_UserManager is checked for FB 4.0. Srp has totally different behaviour, at least for 4.0.0.1714.
# Sent letter to dimitr and alex, 05.01.2020 22:00.
subs = {'dsn': act_2.db.dsn, 'user_name': act_2.db.user, 'user_password': act_2.db.password,
'current_auth_plugin': None,}
for current_auth_plugin in ['Legacy_UserManager', ]:
subs['current_auth_plugin'] = current_auth_plugin
act_2.isql(switches=['-q'], input=sql_text % subs)
for line in act_2.stdout.splitlines():
if line.strip():
print(current_auth_plugin[:3] + ': ' + line)
#
act_2.reset()
act_2.expected_stdout = expected_stdout_2
act_2.stdout = capsys.readouterr().out
assert act_2.clean_stdout == act_2.clean_expected_stdout

View File

@ -2,18 +2,18 @@
# #
# id: bugs.core_4768 # id: bugs.core_4768
# title: CREATE USER ... TAGS ( argument_1 = 'value1', ..., argument_N = 'valueN' ) - wrong results of statement when there are many arguments # title: CREATE USER ... TAGS ( argument_1 = 'value1', ..., argument_N = 'valueN' ) - wrong results of statement when there are many arguments
# decription: # decription:
# Checked on: # Checked on:
# FB30SS, build 3.0.4.32985: OK, 7.672s. # FB30SS, build 3.0.4.32985: OK, 7.672s.
# FB40SS, build 4.0.0.1000: OK, 13.094s. # FB40SS, build 4.0.0.1000: OK, 13.094s.
# #
# tracker_id: CORE-4768 # tracker_id: CORE-4768
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, python_act, Action, user_factory, User
# version: 3.0 # version: 3.0
# resources: None # resources: None
@ -26,52 +26,52 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# import subprocess # import subprocess
# import time # import time
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close(file_handle): # def flush_and_close(file_handle):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb'): # if file_handle.mode not in ('r', 'rb'):
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
# if os.path.isfile( f_names_list[i]): # if os.path.isfile( f_names_list[i]):
# os.remove( f_names_list[i] ) # os.remove( f_names_list[i] )
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# #
# TAGS_COUNT=100000 # TAGS_COUNT=100000
# #^^^^^^^^^^^^^^^^--- TAGS COUNT: THRESHOLD # #^^^^^^^^^^^^^^^^--- TAGS COUNT: THRESHOLD
# #
# f_chk_sql=open( os.path.join(context['temp_directory'],'tmp_tags_4768.sql'), 'w') # f_chk_sql=open( os.path.join(context['temp_directory'],'tmp_tags_4768.sql'), 'w')
# f_chk_sql.write('set bail on;\\n') # f_chk_sql.write('set bail on;\\n')
# f_chk_sql.write("create or alter user tmp$c4768_1 password '123' using plugin Srp tags (\\n") # f_chk_sql.write("create or alter user tmp$c4768_1 password '123' using plugin Srp tags (\\n")
# #
# for i in range(0,TAGS_COUNT): # for i in range(0,TAGS_COUNT):
# f_chk_sql.write( (' ,' if i>0 else ' ') + 'arg_'+str(i)+"='val"+str(i)+"'\\n" ) # f_chk_sql.write( (' ,' if i>0 else ' ') + 'arg_'+str(i)+"='val"+str(i)+"'\\n" )
# f_chk_sql.write(');\\n') # f_chk_sql.write(');\\n')
# f_chk_sql.write('commit;\\n') # f_chk_sql.write('commit;\\n')
# #
# sql_check='''set count on; # sql_check='''set count on;
# set list on; # set list on;
# select # select
@ -91,54 +91,81 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# drop user tmp$c4768_1 using plugin Srp; # drop user tmp$c4768_1 using plugin Srp;
# commit; # commit;
# ''' # '''
# #
# f_chk_sql.write(sql_check) # f_chk_sql.write(sql_check)
# f_chk_sql.write('commit;\\n') # f_chk_sql.write('commit;\\n')
# #
# flush_and_close( f_chk_sql ) # flush_and_close( f_chk_sql )
# #
# #
# f_tags_log = open( os.path.join(context['temp_directory'],'tmp_tags_4768.log'), 'w') # f_tags_log = open( os.path.join(context['temp_directory'],'tmp_tags_4768.log'), 'w')
# f_tags_err = open( os.path.join(context['temp_directory'],'tmp_tags_4768.err'), 'w') # f_tags_err = open( os.path.join(context['temp_directory'],'tmp_tags_4768.err'), 'w')
# #
# subprocess.call( [ context['isql_path'], dsn, "-q", "-i", f_chk_sql.name], # subprocess.call( [ context['isql_path'], dsn, "-q", "-i", f_chk_sql.name],
# stdout = f_tags_log, # stdout = f_tags_log,
# stderr = f_tags_err # stderr = f_tags_err
# ) # )
# #
# flush_and_close( f_tags_log ) # flush_and_close( f_tags_log )
# flush_and_close( f_tags_err ) # flush_and_close( f_tags_err )
# #
# with open(f_tags_log.name) as f: # with open(f_tags_log.name) as f:
# for line in f: # for line in f:
# print(line) # print(line)
# #
# with open(f_tags_err.name) as f: # with open(f_tags_err.name) as f:
# for line in f: # for line in f:
# print('UNEXPECTED STDERR: ' + line) # print('UNEXPECTED STDERR: ' + line)
# #
# # CLEANUP: # # CLEANUP:
# ########## # ##########
# time.sleep(1) # time.sleep(1)
# cleanup( [i.name for i in ( f_chk_sql, f_tags_log, f_tags_err ) ] ) # cleanup( [i.name for i in ( f_chk_sql, f_tags_log, f_tags_err ) ] )
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
USR_NAME TMP$C4768_1 USR_NAME TMP$C4768_1
SEC_PLUGIN Srp SEC_PLUGIN Srp
TAG_MIN ARG_0 TAG_MIN ARG_0
VAL_MIN VAL0 VAL_MIN VAL0
TAG_MAX ARG_99999 TAG_MAX ARG_99999
VAL_MAX VAL99999 VAL_MAX VAL99999
TAG_CNT 100000 TAG_CNT 100000
Records affected: 1 Records affected: 1
""" """
test_user_1 = user_factory(name='tmp$c4768_1', password='123')
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action, test_user_1: User):
def test_1(db_1): TAGS_COUNT = 100000
pytest.fail("Test not IMPLEMENTED") check_lines = ['set bail on;',
"create or alter user tmp$c4768_1 password '123' using plugin Srp tags ("]
for i in range(TAGS_COUNT):
check_lines.append(f"{' ,' if i > 0 else ' '}arg_{i}='val{i}'")
#check_lines.append((' ,' if i>0 else ' ') + 'arg_' + str(i) + "='val" + str(i) + "'")
check_lines.append(');')
check_lines.append('commit;')
test_script = '\n'.join(check_lines) + """
set count on;
set list on;
select
u.sec$user_name as usr_name
,u.sec$plugin sec_plugin
,upper(min( a.sec$key )) tag_min
,upper(min( a.sec$value )) val_min
,upper(max( a.sec$key )) tag_max
,upper(max( a.sec$value )) val_max
,count(*) tag_cnt
from sec$users u
left join sec$user_attributes a on u.sec$user_name = a.sec$user_name
where u.sec$user_name = upper('tmp$c4768_1')
group by 1,2 ;
commit;
"""
act_1.expected_stdout = expected_stdout_1
act_1 .isql(switches=['-q'], input=test_script)
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,26 +2,26 @@
# #
# id: bugs.core_4821 # id: bugs.core_4821
# title: grant create database to ROLE does not work: "no permission for CREATE access to DATABASE ..." # title: grant create database to ROLE does not work: "no permission for CREATE access to DATABASE ..."
# decription: # decription:
# ::: NOTE ::: Test requires that databases.conf contains 'RemoteAccess = true' for security.db # ::: NOTE ::: Test requires that databases.conf contains 'RemoteAccess = true' for security.db
# This line is added there by scenario '<QA_HOME> # This line is added there by scenario '<QA_HOME>
# undaily.bat' every time when check new FB snapshot. # undaily.bat' every time when check new FB snapshot.
# #
# We make connection to security.db and create there user and role, with granting this role to user. # We make connection to security.db and create there user and role, with granting this role to user.
# (so, ROLE also is stored in the security.db). # (so, ROLE also is stored in the security.db).
# Then we grant privilege to create DB to just created role. # Then we grant privilege to create DB to just created role.
# After this we check that: # After this we check that:
# * user can NOT create database if 'ROLE <this_role>' missed in the 'create database' statement; # * user can NOT create database if 'ROLE <this_role>' missed in the 'create database' statement;
# * user _CAN_ create database when issues: create database ... user ... password ... ROLE <this_role> # * user _CAN_ create database when issues: create database ... user ... password ... ROLE <this_role>
# #
# Then we create backup of just created database and check that: # Then we create backup of just created database and check that:
# * user can NOT restore it until specifying '-ROLE <this_role>' in gbak command line; # * user can NOT restore it until specifying '-ROLE <this_role>' in gbak command line;
# * user _CAN_ restore database when issues: gbak -rep ... -user ... -pas ... -ROLE <this_role>; # * user _CAN_ restore database when issues: gbak -rep ... -user ... -pas ... -ROLE <this_role>;
# * the same pair of checks is performed for fbsvcmgr invocation; # * the same pair of checks is performed for fbsvcmgr invocation;
# #
# Finally, we connect again to security.db and drop <this_role>. After this restore must not be allowed # Finally, we connect again to security.db and drop <this_role>. After this restore must not be allowed
# even when "ROLE <this_role>" is specified in commands gbak or fbsvcmgr. # even when "ROLE <this_role>" is specified in commands gbak or fbsvcmgr.
# #
# Checked on: # Checked on:
# 4.0.0.1713 SS: 5.250s. # 4.0.0.1713 SS: 5.250s.
# 4.0.0.1346 SC: 5.734s. # 4.0.0.1346 SC: 5.734s.
@ -29,23 +29,41 @@
# 3.0.5.33218 SS: 4.313s. # 3.0.5.33218 SS: 4.313s.
# 3.0.5.33084 SC: 4.031s. # 3.0.5.33084 SC: 4.031s.
# 3.0.5.33212 CS: 7.672s. # 3.0.5.33212 CS: 7.672s.
# #
# 13.04.2021. Adapted for run both on Windows and Linux. Checked on: # 13.04.2021. Adapted for run both on Windows and Linux. Checked on:
# Windows: 3.0.8.33445, 4.0.0.2416 # Windows: 3.0.8.33445, 4.0.0.2416
# Linux: 3.0.8.33426, 4.0.0.2416 # Linux: 3.0.8.33426, 4.0.0.2416
# #
# [pcisar] 24.11.2021
# This test FAILs because it's not possible to grant create database to role tmp$db_creator
# although it exists (in test database):
# Statement failed, SQLSTATE = 28000
# unsuccessful metadata update
# -GRANT failed
# -SQL role TMP$DB_CREATOR does not exist
# -in security database
#
# tracker_id: CORE-4821 # tracker_id: CORE-4821
# min_versions: ['3.0.5'] # min_versions: ['3.0.5']
# versions: 3.0.5 # versions: 3.0.5
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action import sys
from pathlib import Path
from firebird.qa import db_factory, python_act, Action, temp_file, user_factory, User
# version: 3.0.5 # version: 3.0.5
# resources: None # resources: None
substitutions_1 = [('no permission for CREATE access to DATABASE .*', 'no permission for CREATE access to DATABASE'), ('gbak: ERROR:failed to create database .*', 'gbak: ERROR:failed to create database'), ('-failed to create database .*', '-failed to create database'), ('CREATED_DB_NAME .*', 'CREATED_DB_NAME'), ('FDB_RESTORED_USING_GBAK .*', 'FDB_RESTORED_USING_GBAK'), ('FDB_RESTORED_USING_SMGR .*', 'FDB_RESTORED_USING_SMGR')] substitutions_1 = [('no permission for CREATE access to DATABASE .*',
'no permission for CREATE access to DATABASE'),
('gbak: ERROR:failed to create database .*',
'gbak: ERROR:failed to create database'),
('-failed to create database .*', '-failed to create database'),
('CREATED_DB_NAME .*', 'CREATED_DB_NAME'),
('FDB_RESTORED_USING_GBAK .*', 'FDB_RESTORED_USING_GBAK'),
('FDB_RESTORED_USING_SMGR .*', 'FDB_RESTORED_USING_SMGR')]
init_script_1 = """""" init_script_1 = """"""
@ -53,32 +71,32 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# this_db = db_conn.database_name # this_db = db_conn.database_name
# db_conn.close() # db_conn.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close( file_handle ): # def flush_and_close( file_handle ):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull: # if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull:
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -90,22 +108,22 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.') # print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.')
# print('type(f_names_list[i])=',type(f_names_list[i])) # print('type(f_names_list[i])=',type(f_names_list[i]))
# del_name = None # del_name = None
# #
# if del_name and os.path.isfile( del_name ): # if del_name and os.path.isfile( del_name ):
# os.remove( del_name ) # os.remove( del_name )
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# fdb_test1 = os.path.join(context['temp_directory'],'tmp_4821_test1.fdb') # fdb_test1 = os.path.join(context['temp_directory'],'tmp_4821_test1.fdb')
# fdb_test2 = os.path.join(context['temp_directory'],'tmp_4821_test2.fdb') # fdb_test2 = os.path.join(context['temp_directory'],'tmp_4821_test2.fdb')
# fbk_name = os.path.join(context['temp_directory'],'tmp_4821_test2.fbk') # fbk_name = os.path.join(context['temp_directory'],'tmp_4821_test2.fbk')
# fdb_restored_using_gbak = os.path.join(context['temp_directory'],'tmp_4821_restored.gbak.fdb') # fdb_restored_using_gbak = os.path.join(context['temp_directory'],'tmp_4821_restored.gbak.fdb')
# fdb_restored_using_smgr = os.path.join(context['temp_directory'],'tmp_4821_restored.smgr.fdb') # fdb_restored_using_smgr = os.path.join(context['temp_directory'],'tmp_4821_restored.smgr.fdb')
# fdb_restored_unexpected = os.path.join(context['temp_directory'],'tmp_4821_restored.wo_grant.fdb') # fdb_restored_unexpected = os.path.join(context['temp_directory'],'tmp_4821_restored.wo_grant.fdb')
# #
# f_list = [fdb_test1, fdb_test2, fbk_name, fdb_restored_using_gbak, fdb_restored_using_smgr, fdb_restored_unexpected] # f_list = [fdb_test1, fdb_test2, fbk_name, fdb_restored_using_gbak, fdb_restored_using_smgr, fdb_restored_unexpected]
# cleanup( f_list ) # cleanup( f_list )
# #
# sql_init=''' # sql_init='''
# set wng off; # set wng off;
# set bail on; # set bail on;
@ -126,9 +144,9 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# commit; # commit;
# --show grants; # --show grants;
# ''' # '''
# #
# runProgram('isql', [ 'localhost:security.db' ], sql_init) # runProgram('isql', [ 'localhost:security.db' ], sql_init)
# #
# sql_test=''' # sql_test='''
# create database 'localhost:%(fdb_test1)s' user tmp$c4821_boss password '123'; # create database 'localhost:%(fdb_test1)s' user tmp$c4821_boss password '123';
# select mon$database_name as created_db_name from mon$database; # select mon$database_name as created_db_name from mon$database;
@ -137,60 +155,62 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# set list on; # set list on;
# select mon$database_name as created_db_name from mon$database; # select mon$database_name as created_db_name from mon$database;
# ''' % locals() # ''' % locals()
# #
# runProgram('isql', [ '-q' ], sql_test) # runProgram('isql', [ '-q' ], sql_test)
# #
# # Must PASS because user tmp$c4821_boss is the owner of this DB: # # Must PASS because user tmp$c4821_boss is the owner of this DB:
# runProgram('gbak', [ '-b', 'localhost:' + fdb_test2, fbk_name, '-user', 'tmp$c4821_boss', '-pas', '123'] ) # runProgram('gbak', [ '-b', 'localhost:' + fdb_test2, fbk_name, '-user', 'tmp$c4821_boss', '-pas', '123'] )
# #
# # Must FAIL because we do not specify role, with text: # # Must FAIL because we do not specify role, with text:
# # "gbak: ERROR:no permission for CREATE access to DATABASE ... / gbak: ERROR:failed to create database localhost:tmp_4821_restored.gbak.fdb" # # "gbak: ERROR:no permission for CREATE access to DATABASE ... / gbak: ERROR:failed to create database localhost:tmp_4821_restored.gbak.fdb"
# runProgram('gbak', [ '-rep', fbk_name, 'localhost:'+fdb_restored_using_gbak, '-user', 'tmp$c4821_boss', '-pas', '123'] ) # runProgram('gbak', [ '-rep', fbk_name, 'localhost:'+fdb_restored_using_gbak, '-user', 'tmp$c4821_boss', '-pas', '123'] )
# #
# # Must PASS because we DO specify role: # # Must PASS because we DO specify role:
# runProgram('gbak', [ '-rep', fbk_name, 'localhost:'+fdb_restored_using_gbak, '-user', 'tmp$c4821_boss', '-pas', '123', '-role', 'tmp$db_creator'] ) # runProgram('gbak', [ '-rep', fbk_name, 'localhost:'+fdb_restored_using_gbak, '-user', 'tmp$c4821_boss', '-pas', '123', '-role', 'tmp$db_creator'] )
# #
# runProgram('isql', [ 'localhost:'+fdb_restored_using_gbak ], 'set list on; select mon$database_name as fdb_restored_using_gbak from mon$database;') # runProgram('isql', [ 'localhost:'+fdb_restored_using_gbak ], 'set list on; select mon$database_name as fdb_restored_using_gbak from mon$database;')
# #
# # Must FAIL because we do not specify role, with text: "no permission for CREATE access to DATABASE ... / failed to create database tmp_4821_restored.smgr.fdb" # # Must FAIL because we do not specify role, with text: "no permission for CREATE access to DATABASE ... / failed to create database tmp_4821_restored.smgr.fdb"
# runProgram('fbsvcmgr', [ 'localhost:service_mgr', 'user', 'tmp$c4821_boss', 'password', '123', 'action_restore', 'res_replace', 'bkp_file', fbk_name, 'dbname', fdb_restored_using_smgr ] ) # runProgram('fbsvcmgr', [ 'localhost:service_mgr', 'user', 'tmp$c4821_boss', 'password', '123', 'action_restore', 'res_replace', 'bkp_file', fbk_name, 'dbname', fdb_restored_using_smgr ] )
# #
# # Must PASS because we DO specify role: # # Must PASS because we DO specify role:
# runProgram('fbsvcmgr', [ 'localhost:service_mgr', 'user', 'tmp$c4821_boss', 'password', '123', 'role', 'tmp$db_creator', 'action_restore', 'res_replace', 'bkp_file', fbk_name, 'dbname', fdb_restored_using_smgr ] ) # runProgram('fbsvcmgr', [ 'localhost:service_mgr', 'user', 'tmp$c4821_boss', 'password', '123', 'role', 'tmp$db_creator', 'action_restore', 'res_replace', 'bkp_file', fbk_name, 'dbname', fdb_restored_using_smgr ] )
# #
# runProgram('isql', [ 'localhost:'+fdb_restored_using_smgr ], 'set list on; select mon$database_name as fdb_restored_using_smgr from mon$database;') # runProgram('isql', [ 'localhost:'+fdb_restored_using_smgr ], 'set list on; select mon$database_name as fdb_restored_using_smgr from mon$database;')
# #
# #
# # ATTENTION: now we DROP role and check that after this action user tmp$c4821_boss will not be allowed to restore DB # # ATTENTION: now we DROP role and check that after this action user tmp$c4821_boss will not be allowed to restore DB
# ############################# # #############################
# runProgram('isql', [ 'localhost:security.db' ], 'drop role tmp$db_creator; commit;') # runProgram('isql', [ 'localhost:security.db' ], 'drop role tmp$db_creator; commit;')
# #
# # Must FAIL because there is no more role which was allowed to create DB # # Must FAIL because there is no more role which was allowed to create DB
# # Error text: "gbak: ERROR:no permission for CREATE ... TMP_4821_RESTORED.WO_GRANT.FDB" # # Error text: "gbak: ERROR:no permission for CREATE ... TMP_4821_RESTORED.WO_GRANT.FDB"
# runProgram('gbak', [ '-rep', fbk_name, 'localhost:'+fdb_restored_unexpected, '-user', 'tmp$c4821_boss', '-pas', '123', '-role', 'tmp$db_creator'] ) # runProgram('gbak', [ '-rep', fbk_name, 'localhost:'+fdb_restored_unexpected, '-user', 'tmp$c4821_boss', '-pas', '123', '-role', 'tmp$db_creator'] )
# #
# # Must FAIL (because of the same reason), with text: "no permission for CREATE access to DATABASE TMP_4821_RESTORED.WO_GRANT.FDB" # # Must FAIL (because of the same reason), with text: "no permission for CREATE access to DATABASE TMP_4821_RESTORED.WO_GRANT.FDB"
# runProgram('fbsvcmgr', [ 'localhost:service_mgr', 'user', 'tmp$c4821_boss', 'password', '123', 'role', 'tmp$db_creator', 'action_restore', 'res_replace', 'bkp_file', fbk_name, 'dbname', fdb_restored_unexpected ] ) # runProgram('fbsvcmgr', [ 'localhost:service_mgr', 'user', 'tmp$c4821_boss', 'password', '123', 'role', 'tmp$db_creator', 'action_restore', 'res_replace', 'bkp_file', fbk_name, 'dbname', fdb_restored_unexpected ] )
# #
# # CLEANUP: # # CLEANUP:
# ########## # ##########
# #
# sql_fini=''' # sql_fini='''
# drop user tmp$c4821_boss using plugin Srp; # drop user tmp$c4821_boss using plugin Srp;
# commit; # commit;
# ''' # '''
# runProgram('isql', [ 'localhost:security.db' ], sql_fini) # runProgram('isql', [ 'localhost:security.db' ], sql_fini)
# #
# cleanup( f_list ) # cleanup( f_list )
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
CREATED_DB_NAME /opt/scripts/qa-rundaily/dbg-repo/tmp/tmp_4821_test2.fdb CREATED_DB_NAME /opt/scripts/qa-rundaily/dbg-repo/tmp/tmp_4821_test2.fdb
FDB_RESTORED_USING_GBAK /opt/scripts/qa-rundaily/dbg-repo/tmp/tmp_4821_restored.gbak.fdb FDB_RESTORED_USING_GBAK /opt/scripts/qa-rundaily/dbg-repo/tmp/tmp_4821_restored.gbak.fdb
FDB_RESTORED_USING_SMGR /opt/scripts/qa-rundaily/dbg-repo/tmp/tmp_4821_restored.smgr.fdb FDB_RESTORED_USING_SMGR /opt/scripts/qa-rundaily/dbg-repo/tmp/tmp_4821_restored.smgr.fdb
""" """
expected_stderr_1 = """ expected_stderr_1 = """
Statement failed, SQLSTATE = 28000 Statement failed, SQLSTATE = 28000
no permission for CREATE access to DATABASE no permission for CREATE access to DATABASE
@ -205,12 +225,87 @@ expected_stderr_1 = """
gbak:Exiting before completion due to errors gbak:Exiting before completion due to errors
no permission for CREATE access to DATABASE no permission for CREATE access to DATABASE
-failed to create database -failed to create database
-Exiting before completion due to errors -Exiting before completion due to errors
""" """
test_user_1 = user_factory(name='tmp$c4821_boss', password='123')
fdb_test1 = temp_file('tmp_4821_test1.fdb')
fdb_test2 = temp_file('tmp_4821_test2.fdb')
fbk_name = temp_file('tmp_4821_test2.fbk')
fdb_restored_using_gbak = temp_file('tmp_4821_restored.gbak.fdb')
fdb_restored_using_smgr = temp_file('tmp_4821_restored.smgr.fdb')
fdb_restored_unexpected = temp_file('tmp_4821_restored.no_grant.fdb')
@pytest.mark.version('>=3.0.5') @pytest.mark.version('>=3.0.5')
@pytest.mark.xfail def test_1(act_1: Action, test_user_1: User, capsys, fdb_test1: Path, fdb_test2: Path,
def test_1(db_1): fbk_name: Path, fdb_restored_using_gbak: Path, fdb_restored_using_smgr: Path,
pytest.fail("Test not IMPLEMENTED") fdb_restored_unexpected: Path):
with act_1.test_role('tmp$db_creator'):
with act_1.db.connect() as con:
#con.execute_immediate('revoke all on all from tmp$c4821_boss')
con.execute_immediate('grant create database to role tmp$db_creator')
con.execute_immediate('grant tmp$db_creator to tmp$c4821_boss')
con.commit()
#
sql_test = f"""
create database 'localhost:{fdb_test1}' user tmp$c4821_boss password '123';
select mon$database_name as created_db_name from mon$database;
rollback;
create database 'localhost:{fdb_test2}' user tmp$c4821_boss password '123' role tmp$db_creator;
set list on;
select mon$database_name as created_db_name from mon$database;
"""
act_1.isql(switches=['-q'], input=sql_test)
print(act_1.stdout)
# Must PASS because user tmp$c4821_boss is the owner of this DB:
act_1.reset()
act_1.gbak(switches=['-b', '-user', 'tmp$c4821_boss', '-pas', '123',
f'localhost:{fdb_test2}', str(fbk_name)],
credentials=False)
# Must FAIL because we do not specify role, with text:
# "gbak: ERROR:no permission for CREATE access to DATABASE ... / gbak: ERROR:failed to create database localhost:tmp_4821_restored.gbak.fdb"
act_1.reset()
act_1.expected_stderr = "Must FAIL because we do not specify role"
act_1.gbak(switches=['-rep', '-user', 'tmp$c4821_boss', '-pas', '123',
str(fbk_name), f'localhost:{fdb_restored_using_gbak}'],
credentials=False)
print(act_1.stderr, file=sys.stderr)
# Must PASS because we DO specify role:
act_1.reset()
act_1.gbak(switches=['-rep', '-user', 'tmp$c4821_boss', '-pas', '123', '-role', 'tmp$db_creator',
str(fbk_name), f'localhost:{fdb_restored_using_gbak}'],
credentials=False)
#
act_1.reset()
act_1.isql(switches=['-user', act_1.db.user, '-password', act_1.db.password,
f'localhost:{fdb_restored_using_gbak}'], connect_db=False,
input='set list on; select mon$database_name as fdb_restored_using_gbak from mon$database;')
print(act_1.stdout)
# Must FAIL because we do not specify role, with text: "no permission for CREATE access to DATABASE ... / failed to create database tmp_4821_restored.smgr.fdb"
act_1.reset()
act_1.expected_stderr = "Must FAIL because we do not specify role"
act_1.svcmgr(switches=['localhost:service_mgr', 'user', 'tmp$c4821_boss', 'password', '123',
'action_restore', 'res_replace', 'bkp_file', str(fbk_name),
'dbname', str(fdb_restored_using_smgr)], connect_mngr=False)
print(act_1.stderr, file=sys.stderr)
# Must PASS because we DO specify role:
act_1.reset()
act_1.svcmgr(switches=['localhost:service_mgr', 'user', 'tmp$c4821_boss', 'password', '123',
'role', 'tmp$db_creator', 'action_restore', 'res_replace',
'bkp_file', str(fbk_name), 'dbname', str(fdb_restored_using_smgr)],
connect_mngr=False)
#
act_1.reset()
act_1.isql(switches=['-user', act_1.db.user, '-password', act_1.db.password,
f'localhost:{fdb_restored_using_gbak}'], connect_db=False,
input='set list on; select mon$database_name as fdb_restored_using_smgr from mon$database;')
print(act_1.stdout)
#
act_1.reset()
act_1.expected_stdout = expected_stdout_1
act_1.expected_stderr = expected_stderr_1
act_1.stdout = capsys.readouterr().out
act_1.stderr = capsys.readouterr().err
assert act_1.clean_stdout == act_1.clean_expected_stdout
assert act_1.clean_stderr == act_1.clean_expected_stderr

View File

@ -2,7 +2,7 @@
# #
# id: bugs.core_4840 # id: bugs.core_4840
# title: Transactions with isc_tpb_autocommit can hang the server # title: Transactions with isc_tpb_autocommit can hang the server
# decription: # decription:
# Test creates trivial SP and comment for it (in UTF8 with multi-byte characters) in single Tx with autocommit = true. # Test creates trivial SP and comment for it (in UTF8 with multi-byte characters) in single Tx with autocommit = true.
# Confirmed: # Confirmed:
# 1. Crash on LI-V3.0.0.32173, WI-V3.0.0.32239 # 1. Crash on LI-V3.0.0.32173, WI-V3.0.0.32239
@ -12,20 +12,21 @@
# internal Firebird consistency check (wrong record version (185), file: vio.cpp line: 3823) # internal Firebird consistency check (wrong record version (185), file: vio.cpp line: 3823)
# === # ===
# (after this FB instance is unavaliable; no any *other* database can be attached). # (after this FB instance is unavaliable; no any *other* database can be attached).
# #
# 2. Normal work on LI-V3.0.0.32239 Rev: 62705 # 2. Normal work on LI-V3.0.0.32239 Rev: 62705
# Example of TPB creation can be found here: # Example of TPB creation can be found here:
# http://www.firebirdsql.org/file/documentation/drivers_documentation/python/3.3.0/beyond-python-db-api.html # http://www.firebirdsql.org/file/documentation/drivers_documentation/python/3.3.0/beyond-python-db-api.html
# List of allowed TPB parameters: C:\\Python27\\Lib\\site-packages # List of allowed TPB parameters: C:\\Python27\\Lib\\site-packages
# db\\ibase.py # db\\ibase.py
# #
# tracker_id: CORE-4840 # tracker_id: CORE-4840
# min_versions: ['2.5.0'] # min_versions: ['2.5.0']
# versions: 2.5 # versions: 2.5
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, python_act, Action
from firebird.driver import TPB, Isolation
# version: 2.5 # version: 2.5
# resources: None # resources: None
@ -39,20 +40,20 @@ db_1 = db_factory(charset='UTF8', sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# import fdb # import fdb
# #
# db_conn.close() # db_conn.close()
# conn = kdb.connect(dsn=dsn.encode(),user='SYSDBA',password='masterkey') # conn = kdb.connect(dsn=dsn.encode(),user='SYSDBA',password='masterkey')
# #
# customTPB = ( [ fdb.isc_tpb_read_committed, fdb.isc_tpb_rec_version, fdb.isc_tpb_autocommit ] ) # customTPB = ( [ fdb.isc_tpb_read_committed, fdb.isc_tpb_rec_version, fdb.isc_tpb_autocommit ] )
# #
# conn.begin( tpb=customTPB ) # conn.begin( tpb=customTPB )
# xcur=conn.cursor() # xcur=conn.cursor()
# #
# sp_ddl="create or alter procedure sp_test (in1 integer, in2 float)" + "returns (" + " out1 varchar(20)," + " out2 double precision, " + " out3 integer" + ") as " + " declare x integer;" + "begin" + " out1 = 'out string';" + " out2 = 2 * in2;" + " out3 = 3 * in1;" + " suspend; " + "end" # sp_ddl="create or alter procedure sp_test (in1 integer, in2 float)" + "returns (" + " out1 varchar(20)," + " out2 double precision, " + " out3 integer" + ") as " + " declare x integer;" + "begin" + " out1 = 'out string';" + " out2 = 2 * in2;" + " out3 = 3 * in1;" + " suspend; " + "end"
# #
# sp_rem="comment on procedure sp_test is 'Det är inte alla präster, som göra prästgården eller dess torp till änkesäte åt orkeslösa trotjänarinnor, hade biskopen en gång sagt om Helenas prost.'" # sp_rem="comment on procedure sp_test is 'Det är inte alla präster, som göra prästgården eller dess torp till änkesäte åt orkeslösa trotjänarinnor, hade biskopen en gång sagt om Helenas prost.'"
# #
# ### 27.01.2016: fdb 1.5 will produce # ### 27.01.2016: fdb 1.5 will produce
# ### === # ### ===
# ### ReferenceError: # ### ReferenceError:
# ### weakly-referenced object no longer exists # ### weakly-referenced object no longer exists
@ -62,30 +63,56 @@ db_1 = db_factory(charset='UTF8', sql_dialect=3, init=init_script_1)
# ###xcur.execute(xcmd) # ###xcur.execute(xcmd)
# ###xcmd=xcur.prep( sp_rem ) # ###xcmd=xcur.prep( sp_rem )
# ###xcur.execute(xcmd) # ###xcur.execute(xcmd)
# #
# # For confirmation of ticket issues it is enough to do just this: # # For confirmation of ticket issues it is enough to do just this:
# #
# xcur.execute(sp_ddl) # xcur.execute(sp_ddl)
# xcur.execute(sp_rem) # xcur.execute(sp_rem)
# #
# conn.commit() # conn.commit()
# conn.close() # conn.close()
# #
# sqltxt="set list on; select * from sp_test(12345, 3.1415926);" # sqltxt="set list on; select * from sp_test(12345, 3.1415926);"
# runProgram('isql',[dsn,'-user',user_name,'-pas',user_password,'-q'],sqltxt) # runProgram('isql',[dsn,'-user',user_name,'-pas',user_password,'-q'],sqltxt)
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
OUT1 out string OUT1 out string
OUT2 6.283185005187988 OUT2 6.283185005187988
OUT3 37035 OUT3 37035
""" """
@pytest.mark.version('>=2.5') @pytest.mark.version('>=2.5')
@pytest.mark.xfail def test_1(act_1: Action):
def test_1(db_1): with act_1.db.connect() as con:
pytest.fail("Test not IMPLEMENTED") custom_tpb = TPB(isolation=Isolation.READ_COMMITTED_RECORD_VERSION, auto_commit=True).get_buffer()
con.begin(custom_tpb)
c = con.cursor()
sp_ddl = """
create or alter procedure sp_test (in1 integer, in2 float)
returns (
out1 varchar(20),
out2 double precision,
out3 integer
) as
declare x integer;
begin
out1 = 'out string';
out2 = 2 * in2;
out3 = 3 * in1;
suspend;
end
"""
#
sp_rem = "comment on procedure sp_test is 'Det är inte alla präster, som göra prästgården eller dess torp till änkesäte åt orkeslösa trotjänarinnor, hade biskopen en gång sagt om Helenas prost.'"
# For confirmation of ticket issues it is enough to do just this:
c.execute(sp_ddl)
c.execute(sp_rem)
con.commit()
#
act_1.expected_stdout = expected_stdout_1
act_1.isql(switches=['-q'], input='set list on; select * from sp_test(12345, 3.1415926);')
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,39 +2,46 @@
# #
# id: bugs.core_4855 # id: bugs.core_4855
# title: Online validation during DML activity in other connection leads to message "Error while trying to read from file" and "page in use during flush (210), file: cch.cpp line: 2672" # title: Online validation during DML activity in other connection leads to message "Error while trying to read from file" and "page in use during flush (210), file: cch.cpp line: 2672"
# decription: # decription:
# In order to check ticket issues this test does following: # In order to check ticket issues this test does following:
# 1. Change on test database FW to OFF - this will increase DML performance. # 1. Change on test database FW to OFF - this will increase DML performance.
# 2. Create two tables: one for inserting rows ('test') and second to serve as 'signal' to stop DML: # 2. Create two tables: one for inserting rows ('test') and second to serve as 'signal' to stop DML:
# inserts will be done until 2nd table (with name = 'stop') is empty. # inserts will be done until 2nd table (with name = 'stop') is empty.
# 3. Adds to SQL script DML (execute block) that will be used in ISQL session #1: it inserts rows # 3. Adds to SQL script DML (execute block) that will be used in ISQL session #1: it inserts rows
# into 'test' and checks after each inserting table 'stop' on presence there at least one row. # into 'test' and checks after each inserting table 'stop' on presence there at least one row.
# This 'stop-row' will be inserted into 'stop' table in another ISQL session. # This 'stop-row' will be inserted into 'stop' table in another ISQL session.
# 4. Launches ISQL connection #1 in separate (child) process. This ISQL will start 'heavy DML'. # 4. Launches ISQL connection #1 in separate (child) process. This ISQL will start 'heavy DML'.
# 5. Proceeds several online-validation actions by using synchronous call of 'FBSVCMGR action_validate'. # 5. Proceeds several online-validation actions by using synchronous call of 'FBSVCMGR action_validate'.
# Adds result of each validation to log. # Adds result of each validation to log.
# 6. Launches ISQL connection #2 in separate (child) process and give to this session trivial job: # 6. Launches ISQL connection #2 in separate (child) process and give to this session trivial job:
# 'insert into stop(id) values(1); commit;'. This will cause ISQL session #1 to stop its activity # 'insert into stop(id) values(1); commit;'. This will cause ISQL session #1 to stop its activity
# because it runs in transaction with TIL = RC. # because it runs in transaction with TIL = RC.
# 7. Outputs log of ISQL-1 and online validation results. # 7. Outputs log of ISQL-1 and online validation results.
# #
# Tested on WI-V3.0.0.32008, SS, SC and CS. Result: OK. # Tested on WI-V3.0.0.32008, SS, SC and CS. Result: OK.
# Updated since WI-V3.0.0.32064: reduced key-length from extremely huge (1000) to "normal" value 36, # Updated since WI-V3.0.0.32064: reduced key-length from extremely huge (1000) to "normal" value 36,
# otherwise get 'Statement failed, SQLSTATE = 54000 / Implementation limit exceeded / -Maximum index level reached'. # otherwise get 'Statement failed, SQLSTATE = 54000 / Implementation limit exceeded / -Maximum index level reached'.
# (since CORE-4914 was fixed, see: http://sourceforge.net/p/firebird/code/62266 ) # (since CORE-4914 was fixed, see: http://sourceforge.net/p/firebird/code/62266 )
# #
# tracker_id: CORE-4855 # tracker_id: CORE-4855
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action import subprocess
import time
from pathlib import Path
from firebird.qa import db_factory, python_act, Action, temp_file
from firebird.driver import DbWriteMode
# version: 3.0 # version: 3.0
# resources: None # resources: None
substitutions_1 = [('[0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9]', ''), ('Relation [0-9]{3,4}', 'Relation'), ('Statement failed, SQLSTATE = HY008', ''), ('operation was cancelled', ''), ('After line .*', '')] substitutions_1 = [('[0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9]', ''),
('Relation [0-9]{3,4}', 'Relation'),
('Statement failed, SQLSTATE = HY008', ''),
('operation was cancelled', ''), ('After line .*', '')]
init_script_1 = """""" init_script_1 = """"""
@ -42,41 +49,41 @@ db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# #
# import os # import os
# import subprocess # import subprocess
# from subprocess import Popen # from subprocess import Popen
# #import signal # #import signal
# import time # import time
# from fdb import services # from fdb import services
# #
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# # Obtain engine version: # # Obtain engine version:
# engine = str(db_conn.engine_version) # convert to text because 'float' object has no attribute 'startswith' # engine = str(db_conn.engine_version) # convert to text because 'float' object has no attribute 'startswith'
# dbname=db_conn.database_name # dbname=db_conn.database_name
# db_conn.close() # db_conn.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close(file_handle): # def flush_and_close(file_handle):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb'): # if file_handle.mode not in ('r', 'rb'):
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -87,37 +94,37 @@ db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1)
# else: # else:
# print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.') # print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.')
# del_name = None # del_name = None
# #
# if del_name and os.path.isfile( del_name ): # if del_name and os.path.isfile( del_name ):
# os.remove( del_name ) # os.remove( del_name )
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# ##################################################################### # #####################################################################
# # Change database FW to OFF in order to increase speed of insertions and output its header info: # # Change database FW to OFF in order to increase speed of insertions and output its header info:
# #
# fwoff_log=open( os.path.join(context['temp_directory'],'tmp_fw_off_4855.log'), 'w') # fwoff_log=open( os.path.join(context['temp_directory'],'tmp_fw_off_4855.log'), 'w')
# subprocess.call([ context['fbsvcmgr_path'], "localhost:service_mgr", # subprocess.call([ context['fbsvcmgr_path'], "localhost:service_mgr",
# "action_properties", # "action_properties",
# "prp_write_mode", "prp_wm_async", # "prp_write_mode", "prp_wm_async",
# "dbname", dbname ], # "dbname", dbname ],
# stdout=fwoff_log, stderr=subprocess.STDOUT) # stdout=fwoff_log, stderr=subprocess.STDOUT)
# #
# fwoff_log.seek(0,2) # fwoff_log.seek(0,2)
# subprocess.call([ context['fbsvcmgr_path'], "localhost:service_mgr", # subprocess.call([ context['fbsvcmgr_path'], "localhost:service_mgr",
# "action_db_stats", # "action_db_stats",
# "dbname", dbname, "sts_hdr_pages"], # "dbname", dbname, "sts_hdr_pages"],
# stdout=fwoff_log, stderr=subprocess.STDOUT) # stdout=fwoff_log, stderr=subprocess.STDOUT)
# flush_and_close( fwoff_log ) # flush_and_close( fwoff_log )
# #
# ##################################################################### # #####################################################################
# # Preparing script for ISQL that will do 'heavy DML': # # Preparing script for ISQL that will do 'heavy DML':
# #
# sql_cmd=''' # sql_cmd='''
# recreate sequence g; # recreate sequence g;
# recreate table test(id int, s varchar( 36 ) unique using index test_s_unq); # recreate table test(id int, s varchar( 36 ) unique using index test_s_unq);
# recreate table stop(id int); # recreate table stop(id int);
# commit; # commit;
# set list on; # set list on;
# set transaction read committed; # set transaction read committed;
# set term ^; # set term ^;
@ -134,34 +141,34 @@ db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1)
# set term ;^ # set term ;^
# commit; # commit;
# ''' # '''
# #
# f_heavy_dml_cmd=open( os.path.join(context['temp_directory'],'tmp_isql_4855.sql'), 'w') # f_heavy_dml_cmd=open( os.path.join(context['temp_directory'],'tmp_isql_4855.sql'), 'w')
# f_heavy_dml_cmd.write(sql_cmd) # f_heavy_dml_cmd.write(sql_cmd)
# flush_and_close( f_heavy_dml_cmd ) # flush_and_close( f_heavy_dml_cmd )
# #
# ##################################################################### # #####################################################################
# # Starting ISQL in separate process with doing 'heavy DML' (bulk-inserts) until table 'stop' # # Starting ISQL in separate process with doing 'heavy DML' (bulk-inserts) until table 'stop'
# # remains empty (this table will get one row in separate ISQL session, see below p_stopper): # # remains empty (this table will get one row in separate ISQL session, see below p_stopper):
# #
# f_heavy_dml_log=open( os.path.join(context['temp_directory'],'tmp_isql_4855.log'), 'w') # f_heavy_dml_log=open( os.path.join(context['temp_directory'],'tmp_isql_4855.log'), 'w')
# p_heavy_dml = Popen([ context['isql_path'] , dsn, "-i", f_heavy_dml_cmd.name ], stdout=f_heavy_dml_log, stderr=subprocess.STDOUT) # p_heavy_dml = Popen([ context['isql_path'] , dsn, "-i", f_heavy_dml_cmd.name ], stdout=f_heavy_dml_log, stderr=subprocess.STDOUT)
# #
# # Here we have to wait for sure that ISQL could establish its connect and starts DML # # Here we have to wait for sure that ISQL could establish its connect and starts DML
# # before we will run online-validation: # # before we will run online-validation:
# #
# # 16.03.2016: increased time delay because under some circumstances ISQL could not establish connect # # 16.03.2016: increased time delay because under some circumstances ISQL could not establish connect
# # and this lead validation to start verify table TEST (which was not expected). # # and this lead validation to start verify table TEST (which was not expected).
# # Detected many times on CS/SC. # # Detected many times on CS/SC.
# #
# time.sleep(4) # time.sleep(4)
# #
# #
# ##################################################################### # #####################################################################
# # Doing online-validation. # # Doing online-validation.
# # Use subprocess.call() with waiting in main thread for it will finish. # # Use subprocess.call() with waiting in main thread for it will finish.
# #
# val_log=open( os.path.join(context['temp_directory'],'tmp_onval_4855.log'), 'w') # val_log=open( os.path.join(context['temp_directory'],'tmp_onval_4855.log'), 'w')
# #
# val_log.write('Iteration #1:\\n') # val_log.write('Iteration #1:\\n')
# val_log.seek(0,2) # val_log.seek(0,2)
# subprocess.call( [ context['fbsvcmgr_path'], "localhost:service_mgr", # subprocess.call( [ context['fbsvcmgr_path'], "localhost:service_mgr",
@ -170,11 +177,11 @@ db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1)
# ], # ],
# stdout=val_log, stderr=subprocess.STDOUT # stdout=val_log, stderr=subprocess.STDOUT
# ) # )
# #
# time.sleep(2) # time.sleep(2)
# #
# # Iteration #2: # # Iteration #2:
# #
# val_log.seek(0,2) # val_log.seek(0,2)
# val_log.write('\\n\\nIteration #2:\\n') # val_log.write('\\n\\nIteration #2:\\n')
# val_log.seek(0,2) # val_log.seek(0,2)
@ -183,43 +190,43 @@ db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1)
# "dbname",dbname], # "dbname",dbname],
# stdout=val_log, stderr=subprocess.STDOUT) # stdout=val_log, stderr=subprocess.STDOUT)
# flush_and_close( val_log ) # flush_and_close( val_log )
# #
# ##################################################################### # #####################################################################
# #
# # Stopping ISQL that is doing now 'heavy DML' (bulk-inserts): # # Stopping ISQL that is doing now 'heavy DML' (bulk-inserts):
# #
# f_stopper_cmd=open( os.path.join(context['temp_directory'],'tmp_stop_4855.sql'), 'w') # f_stopper_cmd=open( os.path.join(context['temp_directory'],'tmp_stop_4855.sql'), 'w')
# f_stopper_cmd.write('insert into stop(id) values(1); commit;') # f_stopper_cmd.write('insert into stop(id) values(1); commit;')
# flush_and_close( f_stopper_cmd ) # flush_and_close( f_stopper_cmd )
# p_stopper = subprocess.call([ context['isql_path'], dsn, "-i", f_stopper_cmd.name]) # p_stopper = subprocess.call([ context['isql_path'], dsn, "-i", f_stopper_cmd.name])
# #
# # Stop working ISQL. NB: in rare cases this can lead to: # # Stop working ISQL. NB: in rare cases this can lead to:
# # + Statement failed, SQLSTATE = HY008 # # + Statement failed, SQLSTATE = HY008
# # + operation was cancelled # # + operation was cancelled
# # + After line ... in file .../tmp_isql_4855.sql # # + After line ... in file .../tmp_isql_4855.sql
# #
# p_heavy_dml.terminate() # p_heavy_dml.terminate()
# flush_and_close( f_heavy_dml_log ) # flush_and_close( f_heavy_dml_log )
# #
# with open( f_heavy_dml_log.name,'r') as f: # with open( f_heavy_dml_log.name,'r') as f:
# print(f.read()) # print(f.read())
# #
# with open( val_log.name,'r') as f: # with open( val_log.name,'r') as f:
# print(f.read()) # print(f.read())
# #
# #
# ##################################################################### # #####################################################################
# # Cleanup: # # Cleanup:
# time.sleep(1) # time.sleep(1)
# cleanup( (fwoff_log, val_log, f_heavy_dml_cmd, f_heavy_dml_log, f_stopper_cmd) ) # cleanup( (fwoff_log, val_log, f_heavy_dml_cmd, f_heavy_dml_log, f_stopper_cmd) )
# #
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
INSERTED_ROWS OK, LOT OF.
Iteration #1: Iteration #1:
21:16:28.31 Validation started 21:16:28.31 Validation started
21:16:28.31 Relation 128 (TEST) 21:16:28.31 Relation 128 (TEST)
@ -238,11 +245,63 @@ Iteration #2:
21:16:35.09 process pointer page 0 of 1 21:16:35.09 process pointer page 0 of 1
21:16:35.09 Relation 129 (STOP) is ok 21:16:35.09 Relation 129 (STOP) is ok
21:16:35.09 Validation finished 21:16:35.09 Validation finished
""" INSERTED_ROWS OK, LOT OF.
"""
heavy_script_1 = temp_file('heavy_script.sql')
heavy_output_1 = temp_file('heavy_script.out')
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action, heavy_script_1: Path, heavy_output_1: Path, capsys):
def test_1(db_1): # Change database FW to OFF in order to increase speed of insertions and output its header info
pytest.fail("Test not IMPLEMENTED") with act_1.connect_server() as srv:
srv.database.set_write_mode(database=str(act_1.db.db_path), mode=DbWriteMode.ASYNC)
# Preparing script for ISQL that will do 'heavy DML'
heavy_script_1.write_text("""
recreate sequence g;
recreate table test(id int, s varchar( 36 ) unique using index test_s_unq);
recreate table stop(id int);
commit;
set list on;
set transaction read committed;
set term ^;
execute block returns( inserted_rows varchar(20) ) as
begin
while ( not exists(select * from stop) ) do
begin
insert into test(id, s) values( gen_id(g,1), rpad('', 36, uuid_to_char(gen_uuid())) );
end
inserted_rows = iif(gen_id(g,0) > 0, 'OK, LOT OF.', 'FAIL: ZERO!');
suspend;
end
^
set term ;^
commit;
""")
with open(heavy_output_1, mode='w') as heavy_out:
p_heavy_sql = subprocess.Popen([act_1.vars['isql'], '-i', str(heavy_script_1),
'-user', act_1.db.user,
'-password', act_1.db.password, act_1.db.dsn],
stdout=heavy_out, stderr=subprocess.STDOUT)
try:
time.sleep(4)
# Run validation twice
with act_1.connect_server() as srv:
print('Iteration #1:')
srv.database.validate(database=str(act_1.db.db_path), lock_timeout=1,
callback=print)
print('Iteration #2:')
srv.database.validate(database=str(act_1.db.db_path), lock_timeout=1,
callback=print)
# Stopping ISQL that is doing now 'heavy DML' (bulk-inserts):
act_1.isql(switches=[], input='insert into stop(id) values(1); commit;')
finally:
p_heavy_sql.terminate()
#
print(heavy_output_1.read_text())
# Check
act_1.reset()
act_1.expected_stdout = expected_stdout_1
act_1.stdout = capsys.readouterr().out
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,9 +2,9 @@
# #
# id: bugs.core_4864 # id: bugs.core_4864
# title: CREATE DATABASE fail with ISQL # title: CREATE DATABASE fail with ISQL
# decription: # decription:
# Test obtains full path to $fb_home via FBSVCMGR info_get_env. # Test obtains full path to $fb_home via FBSVCMGR info_get_env.
# Then it makes copy of file 'databases.conf' that is in $fb_home directory because # Then it makes copy of file 'databases.conf' that is in $fb_home directory because
# following lines will be added to that 'databases.conf': # following lines will be added to that 'databases.conf':
# === # ===
# tmp_alias_4864 = ... # tmp_alias_4864 = ...
@ -12,7 +12,7 @@
# SecurityDatabase = tmp_alias_4864 # SecurityDatabase = tmp_alias_4864
# } # }
# === # ===
# Then we run ISQL and give to it command to create database which definition # Then we run ISQL and give to it command to create database which definition
# should be taken from 'databases.conf', as it was explained in the ticket by Alex: # should be taken from 'databases.conf', as it was explained in the ticket by Alex:
# === # ===
# create database 'tmp_alias_4864' user 'SYSDBA'; # create database 'tmp_alias_4864' user 'SYSDBA';
@ -21,9 +21,9 @@
# to be sure that we really got proper result. # to be sure that we really got proper result.
# ............................................. # .............................................
# ::: NB ::: # ::: NB :::
# It is impossible to check ability to create new user in new database that was made by such way: # It is impossible to check ability to create new user in new database that was made by such way:
# plugin 'Srp' is required that currently is replaced before any test with 'Legacy' one. # plugin 'Srp' is required that currently is replaced before any test with 'Legacy' one.
# #
# tracker_id: CORE-4864 # tracker_id: CORE-4864
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
@ -43,36 +43,36 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# import time # import time
# import shutil # import shutil
# import subprocess # import subprocess
# from subprocess import Popen # from subprocess import Popen
# from fdb import services # from fdb import services
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# db_conn.close() # db_conn.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close(file_handle): # def flush_and_close(file_handle):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb'): # if file_handle.mode not in ('r', 'rb'):
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -83,31 +83,31 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# else: # else:
# print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.') # print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.')
# del_name = None # del_name = None
# #
# if del_name and os.path.isfile( del_name ): # if del_name and os.path.isfile( del_name ):
# os.remove( del_name ) # os.remove( del_name )
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# svc = services.connect(host='localhost') # svc = services.connect(host='localhost')
# fb_home=svc.get_home_directory() # fb_home=svc.get_home_directory()
# svc.close() # svc.close()
# #
# dbconf = os.path.join(fb_home,'databases.conf') # dbconf = os.path.join(fb_home,'databases.conf')
# dbcbak = os.path.join(fb_home,'databases.bak') # dbcbak = os.path.join(fb_home,'databases.bak')
# shutil.copy2( dbconf, dbcbak ) # shutil.copy2( dbconf, dbcbak )
# #
# tmp_fdb = os.path.join(context['temp_directory'],'tmp_4864.fdb') # tmp_fdb = os.path.join(context['temp_directory'],'tmp_4864.fdb')
# #
# cleanup( (tmp_fdb,) ) # cleanup( (tmp_fdb,) )
# #
# f_dbconf=open(dbconf,'a') # f_dbconf=open(dbconf,'a')
# f_dbconf.seek(0, 2) # f_dbconf.seek(0, 2)
# f_dbconf.write("\\n\\n# Created temply by fbtest, CORE-4864. Should be removed auto.") # f_dbconf.write("\\n\\n# Created temply by fbtest, CORE-4864. Should be removed auto.")
# f_dbconf.write("\\n\\ntmp_alias_4864 = " + tmp_fdb ) # f_dbconf.write("\\n\\ntmp_alias_4864 = " + tmp_fdb )
# f_dbconf.write("\\n{\\n SecurityDatabase = tmp_alias_4864 \\n}\\n") # f_dbconf.write("\\n{\\n SecurityDatabase = tmp_alias_4864 \\n}\\n")
# f_dbconf.close() # f_dbconf.close()
# #
# isql_script=''' # isql_script='''
# create database 'tmp_alias_4864' user 'SYSDBA'; # create database 'tmp_alias_4864' user 'SYSDBA';
# set list on; # set list on;
@ -125,45 +125,43 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# drop database; # drop database;
# quit; # quit;
# ''' # '''
# #
# f_isql_cmd=open( os.path.join(context['temp_directory'],'tmp_create_4864.sql'), 'w') # f_isql_cmd=open( os.path.join(context['temp_directory'],'tmp_create_4864.sql'), 'w')
# f_isql_cmd.write( isql_script ) # f_isql_cmd.write( isql_script )
# flush_and_close( f_isql_cmd ) # flush_and_close( f_isql_cmd )
# #
# f_isql_log=open( os.path.join(context['temp_directory'],'tmp_create_4864.log'), 'w') # f_isql_log=open( os.path.join(context['temp_directory'],'tmp_create_4864.log'), 'w')
# subprocess.call([context['isql_path'],"-q","-i",f_isql_cmd.name], stdout=f_isql_log, stderr=subprocess.STDOUT) # subprocess.call([context['isql_path'],"-q","-i",f_isql_cmd.name], stdout=f_isql_log, stderr=subprocess.STDOUT)
# flush_and_close( f_isql_log ) # flush_and_close( f_isql_log )
# #
# shutil.move(dbcbak, dbconf) # shutil.move(dbcbak, dbconf)
# #
# with open( f_isql_log.name,'r') as f: # with open( f_isql_log.name,'r') as f:
# print(f.read()) # print(f.read())
# #
# ##################################################################### # #####################################################################
# # Cleanup: # # Cleanup:
# #
# # do NOT remove this pause otherwise some of logs will not be enable for deletion and test will finish with # # do NOT remove this pause otherwise some of logs will not be enable for deletion and test will finish with
# # Exception raised while executing Python test script. exception: WindowsError: 32 # # Exception raised while executing Python test script. exception: WindowsError: 32
# time.sleep(1) # time.sleep(1)
# cleanup( (f_isql_log, f_isql_cmd, tmp_fdb) ) # cleanup( (f_isql_log, f_isql_cmd, tmp_fdb) )
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1) #act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
MON$SEC_DATABASE Self MON$SEC_DATABASE Self
MON$ATTACHMENT_NAME tmp_alias_4864 MON$ATTACHMENT_NAME tmp_alias_4864
MON$USER SYSDBA MON$USER SYSDBA
MON$REMOTE_PROTOCOL <null> MON$REMOTE_PROTOCOL <null>
MON$REMOTE_ADDRESS <null> MON$REMOTE_ADDRESS <null>
MON$REMOTE_PROCESS <null> MON$REMOTE_PROCESS <null>
MON$AUTH_METHOD User name in DPB MON$AUTH_METHOD User name in DPB
""" """
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail
def test_1(db_1): def test_1(db_1):
pytest.fail("Test not IMPLEMENTED") pytest.skip("Test requires manipulation with firebird.conf")
#pytest.fail("Test not IMPLEMENTED")

View File

@ -2,21 +2,21 @@
# #
# id: bugs.core_4879 # id: bugs.core_4879
# title: Minor inconvenience in user management via services API # title: Minor inconvenience in user management via services API
# decription: # decription:
# Confirmed weird error message on 3.0.0.31374. # Confirmed weird error message on 3.0.0.31374.
# Command: # Command:
# fbsvcmgr.exe localhost:service_mgr user sysdba password masterkey action_add_user dbname employee sec_username foo sec_password 123 # fbsvcmgr.exe localhost:service_mgr user sysdba password masterkey action_add_user dbname employee sec_username foo sec_password 123
# - failed with: # - failed with:
# unexpected item in service parameter block, expected isc_spb_sec_username # unexpected item in service parameter block, expected isc_spb_sec_username
# Checked on 4.0.0.2307; 3.0.8.33393. # Checked on 4.0.0.2307; 3.0.8.33393.
# #
# tracker_id: CORE-4879 # tracker_id: CORE-4879
# min_versions: ['3.0.0'] # min_versions: ['3.0.0']
# versions: 3.0 # versions: 3.0
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, python_act, Action
# version: 3.0 # version: 3.0
# resources: None # resources: None
@ -33,24 +33,24 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# import subprocess # import subprocess
# from subprocess import Popen # from subprocess import Popen
# import time # import time
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close(file_handle): # def flush_and_close(file_handle):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb'): # if file_handle.mode not in ('r', 'rb'):
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -58,15 +58,15 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# os.remove( f_names_list[i] ) # os.remove( f_names_list[i] )
# if os.path.isfile( f_names_list[i]): # if os.path.isfile( f_names_list[i]):
# print('ERROR: can not remove file ' + f_names_list[i]) # print('ERROR: can not remove file ' + f_names_list[i])
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# this_fdb=db_conn.database_name # this_fdb=db_conn.database_name
# db_conn.close() # db_conn.close()
# #
# f_svc_log=open( os.path.join(context['temp_directory'],'tmp_4879_fbsvc.log'), "w", buffering = 0) # f_svc_log=open( os.path.join(context['temp_directory'],'tmp_4879_fbsvc.log'), "w", buffering = 0)
# # C:\\FB\\old.3b1 # # C:\\FB\\old.3b1
# bsvcmgr.exe localhost:service_mgr user sysdba password masterkey action_add_user dbname employee sec_username foo sec_password 123 # bsvcmgr.exe localhost:service_mgr user sysdba password masterkey action_add_user dbname employee sec_username foo sec_password 123
@ -74,33 +74,39 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# stdout=f_svc_log, # stdout=f_svc_log,
# stderr=subprocess.STDOUT # stderr=subprocess.STDOUT
# ) # )
# #
# subprocess.call( [context['fbsvcmgr_path'], "localhost:service_mgr", "action_delete_user", "dbname", this_fdb, "sec_username", "TMP$C4879" ], # subprocess.call( [context['fbsvcmgr_path'], "localhost:service_mgr", "action_delete_user", "dbname", this_fdb, "sec_username", "TMP$C4879" ],
# stdout=f_svc_log, # stdout=f_svc_log,
# stderr=subprocess.STDOUT # stderr=subprocess.STDOUT
# ) # )
# #
# flush_and_close(f_svc_log) # flush_and_close(f_svc_log)
# #
# with open( f_svc_log.name,'r') as f: # with open( f_svc_log.name,'r') as f:
# for line in f: # for line in f:
# if line.split(): # if line.split():
# print('UNEXPECTED OUTPUT in '+f_svc_log.name+': '+line) # print('UNEXPECTED OUTPUT in '+f_svc_log.name+': '+line)
# #
# #
# # CLEANUP # # CLEANUP
# ######### # #########
# time.sleep(1) # time.sleep(1)
# cleanup( (f_svc_log.name,) ) # cleanup( (f_svc_log.name,) )
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action):
def test_1(db_1): act_1.expected_stderr = 'We do not expect any error output'
pytest.fail("Test not IMPLEMENTED") act_1.svcmgr(switches=['action_add_user', 'dbname', str(act_1.db.db_path),
'sec_username', 'TMP$C4879', 'sec_password', '123'])
# There should be no output at all
assert act_1.clean_stderr == ''
assert act_1.clean_stdout == ''
# add_user passed, so remove it
act_1.reset()
act_1.svcmgr(switches=['action_delete_user', 'dbname', str(act_1.db.db_path),
'sec_username', 'TMP$C4879'])

View File

@ -2,37 +2,39 @@
# #
# id: bugs.core_4880 # id: bugs.core_4880
# title: Increase speed of creating package when number of its functions more than several hundreds # title: Increase speed of creating package when number of its functions more than several hundreds
# decription: # decription:
# This test uses TWO auto-generated scripts, both of them have been packed due to their size in files/core_4880.zip # This test uses TWO auto-generated scripts, both of them have been packed due to their size in files/core_4880.zip
# and are unpacked at runtime here. # and are unpacked at runtime here.
# First script, 'core_4880_fnc.tmp', creates 5'000 STANDALONE functions and adds into log timestamps of start and finish. # First script, 'core_4880_fnc.tmp', creates 5'000 STANDALONE functions and adds into log timestamps of start and finish.
# Second script, 'core_4880_pkg.tmp', creates PACKAGE with head and body also of 5'000 functions and also adds into log # Second script, 'core_4880_pkg.tmp', creates PACKAGE with head and body also of 5'000 functions and also adds into log
# timestamps for start of 'create package' and finish of 'create package BODY' statements. # timestamps for start of 'create package' and finish of 'create package BODY' statements.
# Both scripts use simplest body of functions. # Both scripts use simplest body of functions.
# #
# After both scripts will be finishec, number of seconds is compared for creation: # After both scripts will be finishec, number of seconds is compared for creation:
# 1) standalone functions and 2) package header and body with the same number of functions. # 1) standalone functions and 2) package header and body with the same number of functions.
# Then, we evaluate maxValue and minValue in this pair and result of division: maxValue / minValue, casted to num(12,2). # Then, we evaluate maxValue and minValue in this pair and result of division: maxValue / minValue, casted to num(12,2).
# #
# Numerous runs showed that this ratio (N) is about 1.2 ... 1.5, and it never was more than 1.8. # Numerous runs showed that this ratio (N) is about 1.2 ... 1.5, and it never was more than 1.8.
# It was decided to use N = 2 as max acceptable ratio between time for creation of package and for standalone funcions. # It was decided to use N = 2 as max acceptable ratio between time for creation of package and for standalone funcions.
# If any kind of objects (package or s/alone funcs) will be created more than N times than another, expected_stdout # If any kind of objects (package or s/alone funcs) will be created more than N times than another, expected_stdout
# will contain phrase about regression. # will contain phrase about regression.
# #
# Checked on WI-V3.0.0.32008, machine: P-IV 3.0 Ghz RAM 2Gb, OS = Win XP. # Checked on WI-V3.0.0.32008, machine: P-IV 3.0 Ghz RAM 2Gb, OS = Win XP.
# Duration of test on that machine is about 45-55 seconds. # Duration of test on that machine is about 45-55 seconds.
# #
# 13.04.2021. Adapted for run both on Windows and Linux. Checked on: # 13.04.2021. Adapted for run both on Windows and Linux. Checked on:
# Windows: 3.0.8.33445, 4.0.0.2416 # Windows: 3.0.8.33445, 4.0.0.2416
# Linux: 3.0.8.33426, 4.0.0.2416 # Linux: 3.0.8.33426, 4.0.0.2416
# #
# tracker_id: CORE-4880 # tracker_id: CORE-4880
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: # qmid:
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from zipfile import Path
from firebird.qa import db_factory, python_act, Action
from firebird.driver import DbWriteMode
# version: 3.0 # version: 3.0
# resources: None # resources: None
@ -41,15 +43,15 @@ substitutions_1 = []
init_script_1 = """ init_script_1 = """
recreate table log( recreate table log(
standalone_func_beg timestamp default null standalone_func_beg timestamp default null,
,standalone_func_end timestamp default null standalone_func_end timestamp default null,
,pkg_head_n_body_beg timestamp default null pkg_head_n_body_beg timestamp default null,
,pkg_head_n_body_end timestamp default null pkg_head_n_body_end timestamp default null
); );
commit; commit;
insert into log default values; insert into log default values;
commit; commit;
""" """
db_1 = db_factory(sql_dialect=3, init=init_script_1) db_1 = db_factory(sql_dialect=3, init=init_script_1)
@ -57,26 +59,26 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
#--- #---
# import os # import os
# import zipfile # import zipfile
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# db_conn.close() # db_conn.close()
# runProgram('gfix',['-w','async',dsn]) # runProgram('gfix',['-w','async',dsn])
# #
# zf = zipfile.ZipFile( os.path.join(context['files_location'],'core_4880.zip') ) # zf = zipfile.ZipFile( os.path.join(context['files_location'],'core_4880.zip') )
# zf.extractall( context['temp_directory'] ) # zf.extractall( context['temp_directory'] )
# zf.close() # zf.close()
# runProgram('isql',[dsn,'-q','-i', os.path.join(context['temp_directory'],'core_4880_fnc.tmp') ]) # runProgram('isql',[dsn,'-q','-i', os.path.join(context['temp_directory'],'core_4880_fnc.tmp') ])
# runProgram('isql',[dsn,'-q','-i', os.path.join(context['temp_directory'],'core_4880_pkg.tmp') ]) # runProgram('isql',[dsn,'-q','-i', os.path.join(context['temp_directory'],'core_4880_pkg.tmp') ])
# #
# os.remove( os.path.join(context['temp_directory'],'core_4880_fnc.tmp') ) # os.remove( os.path.join(context['temp_directory'],'core_4880_fnc.tmp') )
# os.remove( os.path.join(context['temp_directory'],'core_4880_pkg.tmp') ) # os.remove( os.path.join(context['temp_directory'],'core_4880_pkg.tmp') )
# #
# script="""set list on; # script="""set list on;
# set term ^; # set term ^;
# execute block as # execute block as
# begin # begin
# rdb$set_context('USER_SESSION', 'MAX_ACCEPTABLE_RATIO', '2'); # rdb$set_context('USER_SESSION', 'MAX_ACCEPTABLE_RATIO', '2');
# -- ^ # -- ^
# -- ################# # -- #################
@ -85,21 +87,21 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# end # end
# ^ # ^
# set term ;^ # set term ;^
# #
# select iif( x.ratio < cast( rdb$get_context('USER_SESSION', 'MAX_ACCEPTABLE_RATIO') as int ), # select iif( x.ratio < cast( rdb$get_context('USER_SESSION', 'MAX_ACCEPTABLE_RATIO') as int ),
# 'Ratio is acceptable', # 'Ratio is acceptable',
# 'Regression, ratio >= ' || rdb$get_context('USER_SESSION', 'MAX_ACCEPTABLE_RATIO') || 'x' # 'Regression, ratio >= ' || rdb$get_context('USER_SESSION', 'MAX_ACCEPTABLE_RATIO') || 'x'
# ) as result_msg # ) as result_msg
# --, x.* # --, x.*
# from ( # from (
# select # select
# standalone_func_sec # standalone_func_sec
# ,pkg_head_n_body_sec # ,pkg_head_n_body_sec
# ,cast( iif( pkg_head_n_body_sec > standalone_func_sec, 1.00 * pkg_head_n_body_sec / standalone_func_sec, 1.00 * standalone_func_sec / pkg_head_n_body_sec ) as numeric(12,2) ) as ratio # ,cast( iif( pkg_head_n_body_sec > standalone_func_sec, 1.00 * pkg_head_n_body_sec / standalone_func_sec, 1.00 * standalone_func_sec / pkg_head_n_body_sec ) as numeric(12,2) ) as ratio
# ,cast( 1.00 * pkg_head_n_body_sec / standalone_func_sec as numeric(12,2) ) package_vs_standalone # ,cast( 1.00 * pkg_head_n_body_sec / standalone_func_sec as numeric(12,2) ) package_vs_standalone
# ,cast( 1.00 * standalone_func_sec / pkg_head_n_body_sec as numeric(12,2) ) standalone_vs_package # ,cast( 1.00 * standalone_func_sec / pkg_head_n_body_sec as numeric(12,2) ) standalone_vs_package
# from ( # from (
# select # select
# nullif( datediff(second from standalone_func_beg to standalone_func_end), 0) standalone_func_sec # nullif( datediff(second from standalone_func_beg to standalone_func_end), 0) standalone_func_sec
# ,nullif( datediff(second from pkg_head_n_body_beg to pkg_head_n_body_end), 0) pkg_head_n_body_sec # ,nullif( datediff(second from pkg_head_n_body_beg to pkg_head_n_body_end), 0) pkg_head_n_body_sec
# from log # from log
@ -108,15 +110,63 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# """ # """
# runProgram('isql',[dsn,'-q'],script) # runProgram('isql',[dsn,'-q'],script)
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
RESULT_MSG Ratio is acceptable RESULT_MSG Ratio is acceptable
""" """
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action):
def test_1(db_1): with act_1.connect_server() as srv:
pytest.fail("Test not IMPLEMENTED") srv.database.set_write_mode(database=str(act_1.db.db_path), mode=DbWriteMode.ASYNC)
# Read FNC scripts from zip file and execute it
script_file = Path(act_1.vars['files'] / 'core_4880.zip',
at='core_4880_fnc.tmp')
act_1.script = script_file.read_text()
act_1.execute()
# Read PKG scripts from zip file and execute it
script_file = Path(act_1.vars['files'] / 'core_4880.zip',
at='core_4880_pkg.tmp')
act_1.script = script_file.read_text()
act_1.execute()
# Check
test_script = """
set list on;
set term ^;
execute block as
begin
rdb$set_context('USER_SESSION', 'MAX_ACCEPTABLE_RATIO', '2');
-- ^
-- #################
-- T H R E S H O L D
-- #################
end
^
set term ;^
select iif(x.ratio < cast( rdb$get_context('USER_SESSION', 'MAX_ACCEPTABLE_RATIO') as int ),
'Ratio is acceptable',
'Regression, ratio >= ' || rdb$get_context('USER_SESSION', 'MAX_ACCEPTABLE_RATIO') || 'x'
) as result_msg
--, x.*
from (
select
standalone_func_sec,
pkg_head_n_body_sec,
cast(iif( pkg_head_n_body_sec > standalone_func_sec, 1.00 * pkg_head_n_body_sec / standalone_func_sec, 1.00 * standalone_func_sec / pkg_head_n_body_sec ) as numeric(12,2) ) as ratio,
cast(1.00 * pkg_head_n_body_sec / standalone_func_sec as numeric(12,2)) package_vs_standalone,
cast(1.00 * standalone_func_sec / pkg_head_n_body_sec as numeric(12,2)) standalone_vs_package
from (
select
nullif(datediff(second from standalone_func_beg to standalone_func_end), 0) standalone_func_sec,
nullif(datediff(second from pkg_head_n_body_beg to pkg_head_n_body_end), 0) pkg_head_n_body_sec
from log
)
) x;
"""
act_1.reset()
act_1.expected_stdout = expected_stdout_1
act_1.isql(switches=['-q'], input=test_script)
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,10 +2,9 @@
# #
# id: bugs.core_4882 # id: bugs.core_4882
# title: ISQL input command (or -i option) reads large (> 64K) lines incorrectly # title: ISQL input command (or -i option) reads large (> 64K) lines incorrectly
# decription: # decription:
# This test verifies ability of parsing multiple statements that are going as `single-lined stream`. # This test verifies ability of parsing multiple statements that are going as `single-lined stream`.
# Source for this test is file `fbt-repo # Source for this test is file `fbt-repo\\files\\core_4882.sql`.
# iles\\core_4882.sql`.
# It contains almost all source code of test that emulates OLTP workload - without initial script for data filling. # It contains almost all source code of test that emulates OLTP workload - without initial script for data filling.
# This test can be found here: svn://svn.code.sf.net/p/firebird/code/qa/oltp-emul/ # This test can be found here: svn://svn.code.sf.net/p/firebird/code/qa/oltp-emul/
# Three files were taken from it: oltp30_DDL.sql, oltp30_sp.sql and oltp_main_filling.sql - with total size ~600 Kb. # Three files were taken from it: oltp30_DDL.sql, oltp30_sp.sql and oltp_main_filling.sql - with total size ~600 Kb.
@ -15,14 +14,14 @@
# NOTE: before this file also contained lines with bulk of begin..end blocks but since CORE-4884 was fixed that number # NOTE: before this file also contained lines with bulk of begin..end blocks but since CORE-4884 was fixed that number
# is limited to 512. With this limit single-line statement of begin-end blocks will have length less than 64K. For that # is limited to 512. With this limit single-line statement of begin-end blocks will have length less than 64K. For that
# reason these lines were removed from here to the test for CORE-4884. # reason these lines were removed from here to the test for CORE-4884.
# #
# tracker_id: CORE-4882 # tracker_id: CORE-4882
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: # qmid:
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, python_act, Action
# version: 3.0 # version: 3.0
# resources: None # resources: None
@ -36,13 +35,14 @@ db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# import os # import os
# #
# db_conn.close() # db_conn.close()
# scriptfile=open(os.path.join(context['files_location'],'core_4882.sql'),'r') # scriptfile=open(os.path.join(context['files_location'],'core_4882.sql'),'r')
# scriptfile.close() # scriptfile.close()
# runProgram('isql',[dsn,'-user',user_name,'-pas',user_password,'-i',scriptfile.name]) # runProgram('isql',[dsn,'-user',user_name,'-pas',user_password,'-i',scriptfile.name])
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
MSG oltp30_DDL.sql start MSG oltp30_DDL.sql start
@ -51,11 +51,12 @@ expected_stdout_1 = """
MSG oltp30_sp.sql finish MSG oltp30_sp.sql finish
MSG oltp_main_filling.sql start MSG oltp_main_filling.sql start
MSG oltp_main_filling.sql finish MSG oltp_main_filling.sql finish
""" """
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action):
def test_1(db_1): act_1.expected_stdout = expected_stdout_1
pytest.fail("Test not IMPLEMENTED") act_1.isql(switches=[], input_file=act_1.vars['files'] / 'core_4882.sql')
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,7 +2,7 @@
# #
# id: bugs.core_4889 # id: bugs.core_4889
# title: FBSVCMGR with `action_trace_start` prevents in 3.0 SuperServer from connecting using local protocol # title: FBSVCMGR with `action_trace_start` prevents in 3.0 SuperServer from connecting using local protocol
# decription: # decription:
# Confirmed failing to create embedded attach on build 31948. # Confirmed failing to create embedded attach on build 31948.
# Confirmed successful work on build 32268, architectures: SS, SC and CS. # Confirmed successful work on build 32268, architectures: SS, SC and CS.
# 10.12.2019. Additional check: # 10.12.2019. Additional check:
@ -10,18 +10,20 @@
# 4.0.0.1685 CS: 12.078s. # 4.0.0.1685 CS: 12.078s.
# 3.0.5.33206 SS: 10.827s. # 3.0.5.33206 SS: 10.827s.
# 3.0.5.33206 CS: 11.793s. # 3.0.5.33206 CS: 11.793s.
# #
# 13.04.2021. Adapted for run both on Windows and Linux. Checked on: # 13.04.2021. Adapted for run both on Windows and Linux. Checked on:
# Windows: 3.0.8.33445, 4.0.0.2416 # Windows: 3.0.8.33445, 4.0.0.2416
# Linux: 3.0.8.33426, 4.0.0.2416 # Linux: 3.0.8.33426, 4.0.0.2416
# #
# tracker_id: CORE-4889 # tracker_id: CORE-4889
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action import time
from threading import Thread, Barrier
from firebird.qa import db_factory, python_act, Action
# version: 3.0 # version: 3.0
# resources: None # resources: None
@ -34,36 +36,36 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# import subprocess # import subprocess
# from subprocess import Popen # from subprocess import Popen
# import time # import time
# #
# fdb_file='$(DATABASE_LOCATION)bugs.core_4889.fdb' # fdb_file='$(DATABASE_LOCATION)bugs.core_4889.fdb'
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# db_conn.close() # db_conn.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close( file_handle ): # def flush_and_close( file_handle ):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull: # if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull:
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -75,19 +77,19 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.') # print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.')
# print('type(f_names_list[i])=',type(f_names_list[i])) # print('type(f_names_list[i])=',type(f_names_list[i]))
# del_name = None # del_name = None
# #
# if del_name and os.path.isfile( del_name ): # if del_name and os.path.isfile( del_name ):
# os.remove( del_name ) # os.remove( del_name )
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# #
# # Prepare config for trace session that will be launched by call of FBSVCMGR: # # Prepare config for trace session that will be launched by call of FBSVCMGR:
# ################ # ################
# txt = '''database= %[\\\\\\\\/]bugs.core_4889.fdb # txt = '''database= %[\\\\\\\\/]bugs.core_4889.fdb
# { # {
# enabled = true # enabled = true
# time_threshold = 0 # time_threshold = 0
# log_errors = true # log_errors = true
# log_statement_finish = true # log_statement_finish = true
# } # }
@ -95,36 +97,36 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# trc_cfg=open( os.path.join(context['temp_directory'],'tmp_trace_4889.cfg'), 'w') # trc_cfg=open( os.path.join(context['temp_directory'],'tmp_trace_4889.cfg'), 'w')
# trc_cfg.write(txt) # trc_cfg.write(txt)
# flush_and_close( trc_cfg ) # flush_and_close( trc_cfg )
# #
# ##################################################################### # #####################################################################
# # Async. launch of trace session using FBSVCMGR action_trace_start: # # Async. launch of trace session using FBSVCMGR action_trace_start:
# #
# trc_log = open( os.path.join(context['temp_directory'],'tmp_trace_4889.log'), 'w') # trc_log = open( os.path.join(context['temp_directory'],'tmp_trace_4889.log'), 'w')
# #
# # Execute a child program in a new process, redirecting STDERR to the same target as of STDOUT: # # Execute a child program in a new process, redirecting STDERR to the same target as of STDOUT:
# p_svcmgr = Popen( [context['fbsvcmgr_path'], "localhost:service_mgr", # p_svcmgr = Popen( [context['fbsvcmgr_path'], "localhost:service_mgr",
# "action_trace_start","trc_cfg", trc_cfg.name], # "action_trace_start","trc_cfg", trc_cfg.name],
# stdout=trc_log, stderr=subprocess.STDOUT) # stdout=trc_log, stderr=subprocess.STDOUT)
# #
# # Wait! Trace session is initialized not instantly! # # Wait! Trace session is initialized not instantly!
# time.sleep(2) # time.sleep(2)
# #
# ##################################################################### # #####################################################################
# #
# # Determine active trace session ID (for further stop): # # Determine active trace session ID (for further stop):
# #
# trc_lst = open( os.path.join(context['temp_directory'],'tmp_trace_4889.lst'), 'w') # trc_lst = open( os.path.join(context['temp_directory'],'tmp_trace_4889.lst'), 'w')
# subprocess.call([context['fbsvcmgr_path'], "localhost:service_mgr", # subprocess.call([context['fbsvcmgr_path'], "localhost:service_mgr",
# "action_trace_list"], # "action_trace_list"],
# stdout=trc_lst, stderr=subprocess.STDOUT # stdout=trc_lst, stderr=subprocess.STDOUT
# ) # )
# flush_and_close( trc_lst ) # flush_and_close( trc_lst )
# #
# # Session ID: 5 # # Session ID: 5
# # user: # # user:
# # date: 2015-08-27 15:24:14 # # date: 2015-08-27 15:24:14
# # flags: active, trace # # flags: active, trace
# #
# trcssn=0 # trcssn=0
# with open( trc_lst.name,'r') as f: # with open( trc_lst.name,'r') as f:
# for line in f: # for line in f:
@ -135,43 +137,43 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# trcssn=word # trcssn=word
# i=i+1 # i=i+1
# break # break
# #
# # Result: `trcssn` is ID of active trace session. # # Result: `trcssn` is ID of active trace session.
# # We have to terminate trace session that is running on server BEFORE we termitane process `p_svcmgr` # # We have to terminate trace session that is running on server BEFORE we termitane process `p_svcmgr`
# if trcssn==0: # if trcssn==0:
# print("Error parsing trace session ID.") # print("Error parsing trace session ID.")
# flush_and_close( trc_log ) # flush_and_close( trc_log )
# #
# else: # else:
# ##################################################################### # #####################################################################
# #
# # Preparing script for ISQL: # # Preparing script for ISQL:
# #
# sql_cmd=''' # sql_cmd='''
# set list on; # set list on;
# set count on; # set count on;
# select # select
# iif(a.mon$remote_protocol is null, 'internal', 'remote') as connection_protocol, # iif(a.mon$remote_protocol is null, 'internal', 'remote') as connection_protocol,
# iif(a.mon$remote_process is null, 'internal', 'remote') as connection_process, # iif(a.mon$remote_process is null, 'internal', 'remote') as connection_process,
# iif(a.mon$remote_pid is null, 'internal', 'remote') as connection_remote_pid, # iif(a.mon$remote_pid is null, 'internal', 'remote') as connection_remote_pid,
# a.mon$auth_method as auth_method -- should be: 'User name in DPB' # a.mon$auth_method as auth_method -- should be: 'User name in DPB'
# from rdb$database r # from rdb$database r
# left join mon$attachments a on a.mon$attachment_id = current_connection and a.mon$system_flag = 0; # left join mon$attachments a on a.mon$attachment_id = current_connection and a.mon$system_flag = 0;
# commit; # commit;
# ''' # '''
# #
# isql_cmd=open( os.path.join(context['temp_directory'],'tmp_isql_4889.sql'), 'w') # isql_cmd=open( os.path.join(context['temp_directory'],'tmp_isql_4889.sql'), 'w')
# isql_cmd.write(sql_cmd) # isql_cmd.write(sql_cmd)
# flush_and_close( isql_cmd ) # flush_and_close( isql_cmd )
# #
# ####################################################################### # #######################################################################
# #
# # Async. launch ISQL process with EMBEDDED connect. # # Async. launch ISQL process with EMBEDDED connect.
# # ::::: NB ::::: # # ::::: NB :::::
# # Confirmed that this action: # # Confirmed that this action:
# # works fine on WI-V3.0.0.31940, build 14-jul-2015 # # works fine on WI-V3.0.0.31940, build 14-jul-2015
# # **HANGS** on WI-V3.0.0.31948, build 16-jul-2015 # # **HANGS** on WI-V3.0.0.31948, build 16-jul-2015
# #
# isql_log=open( os.path.join(context['temp_directory'],'tmp_isql_4889.log'), 'w') # isql_log=open( os.path.join(context['temp_directory'],'tmp_isql_4889.log'), 'w')
# p_isql = Popen( [ context['isql_path'] , fdb_file, # p_isql = Popen( [ context['isql_path'] , fdb_file,
# "-user", "tmp$no$such$user$4889", # "-user", "tmp$no$such$user$4889",
@ -179,17 +181,17 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# stdout=isql_log, # stdout=isql_log,
# stderr=subprocess.STDOUT # stderr=subprocess.STDOUT
# ) # )
# #
# # do NOT remove this delay: # # do NOT remove this delay:
# time.sleep(5) # time.sleep(5)
# #
# p_isql.terminate() # p_isql.terminate()
# flush_and_close( isql_log ) # flush_and_close( isql_log )
# #
# ##################################################################### # #####################################################################
# #
# # Stop trace session: # # Stop trace session:
# #
# trc_lst=open(trc_lst.name, "a") # trc_lst=open(trc_lst.name, "a")
# trc_lst.seek(0,2) # trc_lst.seek(0,2)
# subprocess.call([ context['fbsvcmgr_path'], "localhost:service_mgr", # subprocess.call([ context['fbsvcmgr_path'], "localhost:service_mgr",
@ -197,17 +199,17 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# stdout=trc_lst, stderr=subprocess.STDOUT # stdout=trc_lst, stderr=subprocess.STDOUT
# ) # )
# flush_and_close( trc_lst ) # flush_and_close( trc_lst )
# #
# p_svcmgr.terminate() # p_svcmgr.terminate()
# flush_and_close( trc_log ) # flush_and_close( trc_log )
# #
# # do NOT remove this delay: # # do NOT remove this delay:
# time.sleep(2) # time.sleep(2)
# #
# ##################################################################### # #####################################################################
# #
# # Output logs: # # Output logs:
# #
# i=0 # i=0
# with open( trc_log.name,'r') as f: # with open( trc_log.name,'r') as f:
# for line in f: # for line in f:
@ -217,34 +219,35 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# i=2 # i=2
# print("OK: found text in trace related to EMBEDDED connect.") # print("OK: found text in trace related to EMBEDDED connect.")
# break # break
# #
# if not i==2: # if not i==2:
# print("FAILED to found text in trace related to EMBEDDED connect.") # print("FAILED to found text in trace related to EMBEDDED connect.")
# #
# if os.path.getsize(isql_log.name) == 0: # if os.path.getsize(isql_log.name) == 0:
# print("FAILED to print log from EMBEDDED connect: log is EMPTY.") # print("FAILED to print log from EMBEDDED connect: log is EMPTY.")
# else: # else:
# with open( isql_log.name,'r') as f: # with open( isql_log.name,'r') as f:
# print(f.read()) # print(f.read())
# f.close() # f.close()
# #
# #
# # do NOT remove this pause otherwise log of trace will not be enable for deletion and test will finish with # # do NOT remove this pause otherwise log of trace will not be enable for deletion and test will finish with
# # Exception raised while executing Python test script. exception: WindowsError: 32 # # Exception raised while executing Python test script. exception: WindowsError: 32
# #
# # On WI-V3.0.0.31948 final output was: # # On WI-V3.0.0.31948 final output was:
# # FAILED to found text in trace related to EMBEDDED connect. # # FAILED to found text in trace related to EMBEDDED connect.
# # FAILED to print log from EMBEDDED connect: log is EMPTY. # # FAILED to print log from EMBEDDED connect: log is EMPTY.
# #
# ##################################################################### # #####################################################################
# #
# # Cleanup: # # Cleanup:
# time.sleep(1) # time.sleep(1)
# cleanup( (trc_lst, trc_cfg, trc_log,isql_cmd, isql_log) ) # cleanup( (trc_lst, trc_cfg, trc_log,isql_cmd, isql_log) )
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
OK: found text in trace related to EMBEDDED connect. OK: found text in trace related to EMBEDDED connect.
@ -253,11 +256,66 @@ expected_stdout_1 = """
CONNECTION_REMOTE_PID internal CONNECTION_REMOTE_PID internal
AUTH_METHOD User name in DPB AUTH_METHOD User name in DPB
Records affected: 1 Records affected: 1
""" """
def trace_session(act: Action, b: Barrier):
cfg30 = ['# Trace config, format for 3.0. Generated auto, do not edit!',
f'database=%[\\\\/]{act.db.db_path.name}',
'{',
' enabled = true',
' time_threshold = 0',
' log_initfini = false',
' log_errors = true',
' log_statement_finish = true',
'}']
with act.connect_server() as srv:
srv.trace.start(config='\n'.join(cfg30))
b.wait()
for line in srv:
print(line)
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action, capsys):
def test_1(db_1): b = Barrier(2)
pytest.fail("Test not IMPLEMENTED") trace_thread = Thread(target=trace_session, args=[act_1, b])
trace_thread.start()
b.wait()
isq_script = """
set list on;
set count on;
select
iif(a.mon$remote_protocol is null, 'internal', 'remote') as connection_protocol,
iif(a.mon$remote_process is null, 'internal', 'remote') as connection_process,
iif(a.mon$remote_pid is null, 'internal', 'remote') as connection_remote_pid,
a.mon$auth_method as auth_method -- should be: 'User name in DPB'
from rdb$database r
left join mon$attachments a on a.mon$attachment_id = current_connection and a.mon$system_flag = 0;
commit;
"""
act_1.isql(switches=['-n', '-user', 'tmp$no$such$user$4889', str(act_1.db.db_path)],
connect_db=False, input=isq_script)
output = act_1.stdout
with act_1.connect_server() as srv:
for session in list(srv.trace.sessions.keys()):
srv.trace.stop(session_id=session)
trace_thread.join(1.0)
if trace_thread.is_alive():
pytest.fail('Trace thread still alive')
trace_log = capsys.readouterr().out
#
# Process logs
i = 0
for line in trace_log.splitlines():
if ') EXECUTE_STATEMENT_FINISH' in line:
i = 1
if i == 1 and '1 records fetched' in line:
i = 2
print("OK: found text in trace related to EMBEDDED connect.")
break
if not i == 2:
print("FAILED to found text in trace related to EMBEDDED connect.")
print(output if output else "FAILED to print log from EMBEDDED connect: log is EMPTY.")
# Check
act_1.expected_stdout = expected_stdout_1
act_1.stdout = capsys.readouterr().out
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,15 +2,15 @@
# #
# id: bugs.core_4899 # id: bugs.core_4899
# title: GFIX -online: message "IProvider::attachDatabase failed when loading mapping cache" appears in Classic (only) if access uses remote protocol # title: GFIX -online: message "IProvider::attachDatabase failed when loading mapping cache" appears in Classic (only) if access uses remote protocol
# decription: # decription:
# #
# tracker_id: CORE-4899 # tracker_id: CORE-4899
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, python_act, Action
# version: 3.0 # version: 3.0
# resources: None # resources: None
@ -23,83 +23,117 @@ db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# db_conn.close() # db_conn.close()
# #
# fdb='$(DATABASE_LOCATION)bugs.core_4899.fdb' # fdb='$(DATABASE_LOCATION)bugs.core_4899.fdb'
# fdx=os.path.join(context['temp_directory'],'tmp_copy_4899.fdb') # fdx=os.path.join(context['temp_directory'],'tmp_copy_4899.fdb')
# #
# if os.path.isfile(fdx): # if os.path.isfile(fdx):
# os.remove(fdx) # os.remove(fdx)
# #
# script="create database 'localhost:%s';" % fdx # script="create database 'localhost:%s';" % fdx
# runProgram('isql',['-q'],script) # runProgram('isql',['-q'],script)
# # --------------------- I ---------------- # # --------------------- I ----------------
# #
# #shutil.copy2( fdb, fdx ) # #shutil.copy2( fdb, fdx )
# #
# # Trying to move database to OFFLINE: # # Trying to move database to OFFLINE:
# #
# runProgram('gfix',['-shut', 'full', '-force', '0', fdx]) # runProgram('gfix',['-shut', 'full', '-force', '0', fdx])
# #
# runProgram('gstat',['-h',fdx]) # runProgram('gstat',['-h',fdx])
# #
# # Trying to move database online using LOCAL protocol: # # Trying to move database online using LOCAL protocol:
# runProgram('gfix',['-online',fdx]) # runProgram('gfix',['-online',fdx])
# #
# # gfix attachment via local protocol reflects with following lines in trace: # # gfix attachment via local protocol reflects with following lines in trace:
# # 2015-08-24T18:30:03.2580 (2516:012417E0) ATTACH_DATABASE # # 2015-08-24T18:30:03.2580 (2516:012417E0) ATTACH_DATABASE
# # C:\\MIX\\FIREBIRD\\QA\\FBT-REPO\\TMP\\CORE4899-TMP.FDB (ATT_9, SYSDBA:NONE, NONE, <internal>) # # C:\\MIX\\FIREBIRD\\QA\\FBT-REPO\\TMP\\CORE4899-TMP.FDB (ATT_9, SYSDBA:NONE, NONE, <internal>)
# #
# runProgram('gstat',['-h',fdx]) # runProgram('gstat',['-h',fdx])
# #
# if os.path.isfile(fdx): # if os.path.isfile(fdx):
# os.remove(fdx) # os.remove(fdx)
# #
# # --------------------- II --------------- # # --------------------- II ---------------
# #
# #shutil.copy2( fdb, fdx ) # #shutil.copy2( fdb, fdx )
# runProgram('isql',['-q'],script) # runProgram('isql',['-q'],script)
# #
# runProgram('gfix',['-shut', 'full', '-force', '0', fdx]) # runProgram('gfix',['-shut', 'full', '-force', '0', fdx])
# runProgram('gstat',['-h',fdx]) # runProgram('gstat',['-h',fdx])
# #
# # Trying to move database online using REMOTE protocol: # # Trying to move database online using REMOTE protocol:
# runProgram('gfix',['-online','localhost:'+fdx]) # runProgram('gfix',['-online','localhost:'+fdx])
# #
# # Note: gfix attachment via remote protocol refects with following lines in trace: # # Note: gfix attachment via remote protocol refects with following lines in trace:
# # 2015-08-24T18:30:03.8520 (3256:01B526A8) ATTACH_DATABASE # # 2015-08-24T18:30:03.8520 (3256:01B526A8) ATTACH_DATABASE
# # C:\\MIX\\FIREBIRD\\QA\\FBT-REPO\\TMP\\CORE4899-TMP.FDB (ATT_9, SYSDBA:NONE, NONE, TCPv4:127.0.0.1) # # C:\\MIX\\FIREBIRD\\QA\\FBT-REPO\\TMP\\CORE4899-TMP.FDB (ATT_9, SYSDBA:NONE, NONE, TCPv4:127.0.0.1)
# # C:\\MIX # # C:\\MIX\\firebird\\db30\\gfix.exe:1448
# irebird #
# b30\\gfix.exe:1448
#
# runProgram('gstat',['-h',fdx]) # runProgram('gstat',['-h',fdx])
# #
# if os.path.isfile(fdx): # if os.path.isfile(fdx):
# os.remove(fdx) # os.remove(fdx)
# #
# #, 'substitutions':[('^((?!Attributes).)*$',''),('[\\s]+',' ')] # #, 'substitutions':[('^((?!Attributes).)*$',''),('[\\s]+',' ')]
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
Attributes force write, full shutdown Attributes force write, full shutdown
Attributes force write Attributes force write
Attributes force write, full shutdown Attributes force write, full shutdown
Attributes force write Attributes force write
""" """
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action, capsys):
def test_1(db_1): # Trying to move database to OFFLINE:
pytest.fail("Test not IMPLEMENTED") act_1.gfix(switches=['-shut', 'full', '-force', '0', str(act_1.db.db_path)])
print(act_1.stdout)
act_1.reset()
act_1.gstat(switches=['-h', str(act_1.db.db_path)], connect_db=False)
print(act_1.stdout)
# Trying to move database online using LOCAL protocol:
act_1.reset()
act_1.gfix(switches=['-online', str(act_1.db.db_path)])
print(act_1.stdout)
# gfix attachment via local protocol reflects with following lines in trace:
# 2015-08-24T18:30:03.2580 (2516:012417E0) ATTACH_DATABASE
# C:\\MIX\\FIREBIRD\\QA\\FBT-REPO\\TMP\\CORE4899-TMP.FDB (ATT_9, SYSDBA:NONE, NONE, <internal>)
act_1.reset()
act_1.gstat(switches=['-h', str(act_1.db.db_path)], connect_db=False)
print(act_1.stdout)
# --------------------- II ---------------
act_1.reset()
act_1.gfix(switches=['-shut', 'full', '-force', '0', str(act_1.db.db_path)])
print(act_1.stdout)
act_1.reset()
act_1.gstat(switches=['-h', str(act_1.db.db_path)], connect_db=False)
print(act_1.stdout)
# Trying to move database online using REMOTE protocol:
act_1.reset()
act_1.gfix(switches=['-online', f'localhost:{act_1.db.db_path}'])
print(act_1.stdout)
# Note: gfix attachment via remote protocol refects with following lines in trace:
# 2015-08-24T18:30:03.8520 (3256:01B526A8) ATTACH_DATABASE
# C:\\MIX\\FIREBIRD\\QA\\FBT-REPO\\TMP\\CORE4899-TMP.FDB (ATT_9, SYSDBA:NONE, NONE, TCPv4:127.0.0.1)
# C:\\MIX\\firebird\\db30\\gfix.exe:1448
act_1.reset()
act_1.gstat(switches=['-h', str(act_1.db.db_path)], connect_db=False)
print(act_1.stdout)
# Check
act_1.reset()
act_1.expected_stdout = expected_stdout_1
act_1.stdout = capsys.readouterr().out
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,29 +2,33 @@
# #
# id: bugs.core_4904 # id: bugs.core_4904
# title: Index corruption when add data in long-key-indexed field # title: Index corruption when add data in long-key-indexed field
# decription: # decription:
# In order to check ticket issues this test does following: # In order to check ticket issues this test does following:
# 1. Change on test database FW to OFF - this will increase DML performance. # 1. Change on test database FW to OFF - this will increase DML performance.
# 2. Create table with indexed field of length = maximum that is allowed by # 2. Create table with indexed field of length = maximum that is allowed by
# current FB implementation (page_size / 4 - 9 bytes). # current FB implementation (page_size / 4 - 9 bytes).
# 3. Try to insert enough number of records in this table - this should cause # 3. Try to insert enough number of records in this table - this should cause
# runtime exception SQLSTATE = 54000, "Maximum index level reached" # runtime exception SQLSTATE = 54000, "Maximum index level reached"
# 4. Start validation of database: index should NOT be corrupted in its report. # 4. Start validation of database: index should NOT be corrupted in its report.
# #
# Checked on WI-V3.0.0.32140 (CS, SC); WI-V3.0.0.32157 - official RC1 (SS) # Checked on WI-V3.0.0.32140 (CS, SC); WI-V3.0.0.32157 - official RC1 (SS)
# #
# tracker_id: CORE-4904 # tracker_id: CORE-4904
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, python_act, Action
from firebird.driver import DbWriteMode
# version: 3.0 # version: 3.0
# resources: None # resources: None
substitutions_1 = [('[0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9]', ''), ('Maximum index .* reached', 'Maximum index reached'), ('Relation [0-9]{3,4}', 'Relation'), ('After line .*', ''), ('-At block line: [\\d]+, col: [\\d]+', '-At block line')] substitutions_1 = [('[0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9]', ''),
('Maximum index .* reached', 'Maximum index reached'),
('Relation [0-9]{3,4}', 'Relation'), ('After line .*', ''),
('-At block line: [\\d]+, col: [\\d]+', '-At block line')]
init_script_1 = """""" init_script_1 = """"""
@ -32,34 +36,34 @@ db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# import subprocess # import subprocess
# import time # import time
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# db_name = db_conn.database_name # db_name = db_conn.database_name
# db_conn.close() # db_conn.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close(file_handle): # def flush_and_close(file_handle):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb'): # if file_handle.mode not in ('r', 'rb'):
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -70,16 +74,16 @@ db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1)
# else: # else:
# print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.') # print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.')
# del_name = None # del_name = None
# #
# if del_name and os.path.isfile( del_name ): # if del_name and os.path.isfile( del_name ):
# os.remove( del_name ) # os.remove( del_name )
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# #
# # Move database to FW = OFF in order to increase speed of insertions and output its header info: # # Move database to FW = OFF in order to increase speed of insertions and output its header info:
# ##################################################################### # #####################################################################
# #
# f_change_fw_log=open( os.path.join(context['temp_directory'],'tmp_fw_off_4904.log'), 'w') # f_change_fw_log=open( os.path.join(context['temp_directory'],'tmp_fw_off_4904.log'), 'w')
# subprocess.call([ context['fbsvcmgr_path'], "localhost:service_mgr", # subprocess.call([ context['fbsvcmgr_path'], "localhost:service_mgr",
# "action_properties", # "action_properties",
@ -87,10 +91,10 @@ db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1)
# "dbname", db_name ], # "dbname", db_name ],
# stdout=f_change_fw_log, stderr=subprocess.STDOUT) # stdout=f_change_fw_log, stderr=subprocess.STDOUT)
# flush_and_close( f_change_fw_log ) # flush_and_close( f_change_fw_log )
# #
# ##################################################################### # #####################################################################
# # Preparing script for ISQL that will do inserts with long keys: # # Preparing script for ISQL that will do inserts with long keys:
# #
# sql_cmd=''' recreate table test(s varchar(1015)); -- with THIS length of field following EB will get exception very fast. # sql_cmd=''' recreate table test(s varchar(1015)); -- with THIS length of field following EB will get exception very fast.
# create index test_s on test(s); # create index test_s on test(s);
# commit; # commit;
@ -106,50 +110,51 @@ db_1 = db_factory(page_size=4096, sql_dialect=3, init=init_script_1)
# set term ;^ # set term ;^
# commit; # commit;
# ''' # '''
# #
# f_long_keys_cmd=open( os.path.join(context['temp_directory'],'tmp_isql_4904.sql'), 'w') # f_long_keys_cmd=open( os.path.join(context['temp_directory'],'tmp_isql_4904.sql'), 'w')
# f_long_keys_cmd.write(sql_cmd) # f_long_keys_cmd.write(sql_cmd)
# flush_and_close( f_long_keys_cmd ) # flush_and_close( f_long_keys_cmd )
# #
# ##################################################################### # #####################################################################
# # Starting ISQL # # Starting ISQL
# #
# f_long_keys_log=open( os.path.join(context['temp_directory'],'tmp_isql_4904.log'), 'w') # f_long_keys_log=open( os.path.join(context['temp_directory'],'tmp_isql_4904.log'), 'w')
# subprocess.call([ context['isql_path'] , dsn, "-i", f_long_keys_cmd.name], # subprocess.call([ context['isql_path'] , dsn, "-i", f_long_keys_cmd.name],
# stdout=f_long_keys_log, stderr=subprocess.STDOUT) # stdout=f_long_keys_log, stderr=subprocess.STDOUT)
# flush_and_close( f_long_keys_log ) # flush_and_close( f_long_keys_log )
# #
# ##################################################################### # #####################################################################
# # Run validation after ISQL will finish (with runtime exception due to implementation limit exceeding): # # Run validation after ISQL will finish (with runtime exception due to implementation limit exceeding):
# #
# f_validation_log=open( os.path.join(context['temp_directory'],'tmp_onval_4904.log'), 'w') # f_validation_log=open( os.path.join(context['temp_directory'],'tmp_onval_4904.log'), 'w')
# #
# subprocess.call([context['fbsvcmgr_path'], "localhost:service_mgr", # subprocess.call([context['fbsvcmgr_path'], "localhost:service_mgr",
# "action_validate","val_lock_timeout","1", # "action_validate","val_lock_timeout","1",
# "dbname","$(DATABASE_LOCATION)bugs.core_4904.fdb"], # "dbname","$(DATABASE_LOCATION)bugs.core_4904.fdb"],
# stdout=f_validation_log, stderr=subprocess.STDOUT) # stdout=f_validation_log, stderr=subprocess.STDOUT)
# #
# flush_and_close( f_validation_log ) # flush_and_close( f_validation_log )
# #
# ##################################################################### # #####################################################################
# # Output result of ISQL and online validation: # # Output result of ISQL and online validation:
# with open( f_long_keys_log.name,'r') as f: # with open( f_long_keys_log.name,'r') as f:
# print(f.read()) # print(f.read())
# #
# #
# with open( f_validation_log.name,'r') as f: # with open( f_validation_log.name,'r') as f:
# print(f.read()) # print(f.read())
# #
# #
# #
# # Cleanup: # # Cleanup:
# ########## # ##########
# time.sleep(1) # time.sleep(1)
# cleanup( (f_change_fw_log, f_validation_log, f_long_keys_cmd, f_long_keys_log) ) # cleanup( (f_change_fw_log, f_validation_log, f_long_keys_cmd, f_long_keys_log) )
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
Statement failed, SQLSTATE = 54000 Statement failed, SQLSTATE = 54000
@ -163,11 +168,37 @@ expected_stdout_1 = """
Index 1 (TEST_S) Index 1 (TEST_S)
Relation (TEST) is ok Relation (TEST) is ok
Validation finished Validation finished
""" """
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action, capsys):
def test_1(db_1): # Move database to FW = OFF in order to increase speed of insertions and output its header info:
pytest.fail("Test not IMPLEMENTED") with act_1.connect_server() as srv:
srv.database.set_write_mode(database=str(act_1.db.db_path), mode=DbWriteMode.ASYNC)
# Preparing script for ISQL that will do inserts with long keys:
long_keys_cmd = """
recreate table test(s varchar(1015)); -- with THIS length of field following EB will get exception very fast.
create index test_s on test(s);
commit;
set term ^;
execute block as
begin
insert into test(s)
select rpad('', 1015, uuid_to_char(gen_uuid()) )
from rdb$types, rdb$types
rows 50000; -- this is extra-huge reserve; exception should raise when about 120-130 rows will be inserted.
end
^
set term ;^
commit;
"""
act_1.expected_stderr = "We expect errors"
act_1.isql(switches=[], input=long_keys_cmd)
print(act_1.stdout)
print(act_1.stderr)
# Run validation after ISQL will finish (with runtime exception due to implementation limit exceeding):
srv.database.validate(database=str(act_1.db.db_path), lock_timeout=1, callback=print)
# Check
act_1.expected_stdout = expected_stdout_1
act_1.stdout = capsys.readouterr().out
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,17 +2,17 @@
# #
# id: bugs.core_4927 # id: bugs.core_4927
# title: IIF function prevents the condition from being pushed into the union for better optimization # title: IIF function prevents the condition from being pushed into the union for better optimization
# decription: # decription:
# 1. Obtain engine_version from built-in context variable. # 1. Obtain engine_version from built-in context variable.
# 2. Make config for trace in proper format according to FB engine version, # 2. Make config for trace in proper format according to FB engine version,
# with adding invalid element 'foo' instead on boolean ('true' or 'false') # with adding invalid element 'foo' instead on boolean ('true' or 'false')
# 3. Launch trace session in separate child process using 'FBSVCMGR action_trace_start' # 3. Launch trace session in separate child process using 'FBSVCMGR action_trace_start'
# 4. Run ISQL with calling test SP. # 4. Run ISQL with calling test SP.
# 5. Stop trace session. Output its log with filtering only messages related to ticket notes. # 5. Stop trace session. Output its log with filtering only messages related to ticket notes.
# #
# Trace log for FB 2.5 builds before rev. 62200 ( http://sourceforge.net/p/firebird/code/62200 ) # Trace log for FB 2.5 builds before rev. 62200 ( http://sourceforge.net/p/firebird/code/62200 )
# contained tables which does NOT contain data which we are looked for (marked as "<<<"): # contained tables which does NOT contain data which we are looked for (marked as "<<<"):
# #
# Table Natural Index # Table Natural Index
# **************************************************** # ****************************************************
# HEADER_2100 1 # HEADER_2100 1
@ -21,25 +21,29 @@
# DETAIL_2000 1 <<< # DETAIL_2000 1 <<<
# DETAIL_2100 1 # DETAIL_2100 1
# DETAIL_3300 1 <<< # DETAIL_3300 1 <<<
# #
# Here we check that trace log will contain only TWO tables: HEADER_2100 and DETAIL_2100. # Here we check that trace log will contain only TWO tables: HEADER_2100 and DETAIL_2100.
# Bug affected only 2.5.x. Test checked on: WI-V2.5.5.26928, built at: 2015-09-08 00:13:06 UTC (rev 62201) # Bug affected only 2.5.x. Test checked on: WI-V2.5.5.26928, built at: 2015-09-08 00:13:06 UTC (rev 62201)
# #
# ::: NB ::: # ::: NB :::
# Several delays (time.sleep) added in main thread because of OS buffering. Couldn't switch this buffering off. # Several delays (time.sleep) added in main thread because of OS buffering. Couldn't switch this buffering off.
# #
# tracker_id: CORE-4927 # tracker_id: CORE-4927
# min_versions: ['2.5.5'] # min_versions: ['2.5.5']
# versions: 2.5.5 # versions: 2.5.5
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action import time
from threading import Thread, Barrier
from firebird.qa import db_factory, python_act, Action
# version: 2.5.5 # version: 2.5.5
# resources: None # resources: None
substitutions_1 = [('^((?!HEADER_|DETAIL_).)*$', ''), ('HEADER_2100.*', 'HEADER_2100'), ('DETAIL_2100.*', 'DETAIL_2100')] substitutions_1 = [('^((?!HEADER_|DETAIL_).)*$', ''),
('HEADER_2100.*', 'HEADER_2100'),
('DETAIL_2100.*', 'DETAIL_2100')]
init_script_1 = """ init_script_1 = """
create or alter procedure sp_test as begin end; create or alter procedure sp_test as begin end;
@ -69,7 +73,7 @@ init_script_1 = """
commit; commit;
set term ^; set term ^;
create or alter procedure sp_test returns(result int) as create or alter procedure sp_test returns(result int) as
begin begin
for for
select count(*) select count(*)
@ -86,30 +90,30 @@ init_script_1 = """
and qd.rcv_optype_id is not distinct from 3300 and qd.rcv_optype_id is not distinct from 3300
and qd.snd_id = d.dd_id and qd.snd_id = d.dd_id
into result into result
do do
suspend; suspend;
end end
^ ^
set term ;^ set term ;^
commit; commit;
insert into header_2100(dd_id, ware_id, snd_optype_id) values(1, 11, 2100); insert into header_2100(dd_id, ware_id, snd_optype_id) values(1, 11, 2100);
commit; commit;
insert into detail_1000 (ware_id,snd_optype_id,rcv_optype_id,snd_id) values( 11, 1000, 1200, 1); insert into detail_1000 (ware_id,snd_optype_id,rcv_optype_id,snd_id) values( 11, 1000, 1200, 1);
insert into detail_1200 (ware_id,snd_optype_id,rcv_optype_id,snd_id) values( 11, 1200, 2000, 1); insert into detail_1200 (ware_id,snd_optype_id,rcv_optype_id,snd_id) values( 11, 1200, 2000, 1);
insert into detail_2000 (ware_id,snd_optype_id,rcv_optype_id,snd_id) values( 11, 2000, 2100, 1); insert into detail_2000 (ware_id,snd_optype_id,rcv_optype_id,snd_id) values( 11, 2000, 2100, 1);
insert into detail_2100 (ware_id,snd_optype_id,rcv_optype_id,snd_id) values( 11, 2100, 3300, 1); insert into detail_2100 (ware_id,snd_optype_id,rcv_optype_id,snd_id) values( 11, 2100, 3300, 1);
insert into detail_3300 (ware_id,snd_optype_id,rcv_optype_id,snd_id) values( 11, 3300, null, 1); insert into detail_3300 (ware_id,snd_optype_id,rcv_optype_id,snd_id) values( 11, 3300, null, 1);
commit; commit;
create index d1000_wsrs on detail_1000 (ware_id,snd_optype_id,rcv_optype_id,snd_id); create index d1000_wsrs on detail_1000 (ware_id,snd_optype_id,rcv_optype_id,snd_id);
create index d1200_wsrs on detail_1200 (ware_id,snd_optype_id,rcv_optype_id,snd_id); create index d1200_wsrs on detail_1200 (ware_id,snd_optype_id,rcv_optype_id,snd_id);
create index d2000_wsrs on detail_2000 (ware_id,snd_optype_id,rcv_optype_id,snd_id); create index d2000_wsrs on detail_2000 (ware_id,snd_optype_id,rcv_optype_id,snd_id);
create index d2100_wsrs on detail_2100 (ware_id,snd_optype_id,rcv_optype_id,snd_id); create index d2100_wsrs on detail_2100 (ware_id,snd_optype_id,rcv_optype_id,snd_id);
create index d3300_wsrs on detail_3300 (ware_id,snd_optype_id,rcv_optype_id,snd_id); create index d3300_wsrs on detail_3300 (ware_id,snd_optype_id,rcv_optype_id,snd_id);
commit; commit;
""" """
db_1 = db_factory(sql_dialect=3, init=init_script_1) db_1 = db_factory(sql_dialect=3, init=init_script_1)
@ -119,31 +123,31 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# import subprocess # import subprocess
# from subprocess import Popen # from subprocess import Popen
# import time # import time
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# # Obtain engine version: # # Obtain engine version:
# engine = str(db_conn.engine_version) # convert to text because 'float' object has no attribute 'startswith' # engine = str(db_conn.engine_version) # convert to text because 'float' object has no attribute 'startswith'
# db_conn.close() # db_conn.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close(file_handle): # def flush_and_close(file_handle):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb'): # if file_handle.mode not in ('r', 'rb'):
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -154,29 +158,29 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# else: # else:
# print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.') # print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.')
# del_name = None # del_name = None
# #
# if del_name and os.path.isfile( del_name ): # if del_name and os.path.isfile( del_name ):
# os.remove( del_name ) # os.remove( del_name )
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# #
# ##################################################### # #####################################################
# #
# txt25 = '''# Trace config, format for 2.5. Generated auto, do not edit! # txt25 = '''# Trace config, format for 2.5. Generated auto, do not edit!
# <database %[\\\\\\\\/]bugs.core_4927.fdb> # <database %[\\\\\\\\/]bugs.core_4927.fdb>
# enabled true # enabled true
# time_threshold 0 # time_threshold 0
# log_statement_finish true # log_statement_finish true
# print_perf true # print_perf true
# </database> # </database>
# ''' # '''
# #
# # NOTES ABOUT TRACE CONFIG FOR 3.0: # # NOTES ABOUT TRACE CONFIG FOR 3.0:
# # 1) Header contains `database` clause in different format vs FB 2.5: its data must be enclosed with '{' '}' # # 1) Header contains `database` clause in different format vs FB 2.5: its data must be enclosed with '{' '}'
# # 2) Name and value must be separated by EQUALITY sign ('=') in FB-3 trace.conf, otherwise we get runtime error: # # 2) Name and value must be separated by EQUALITY sign ('=') in FB-3 trace.conf, otherwise we get runtime error:
# # element "<. . .>" have no attribute value set # # element "<. . .>" have no attribute value set
# #
# txt30 = '''# Trace config, format for 3.0. Generated auto, do not edit! # txt30 = '''# Trace config, format for 3.0. Generated auto, do not edit!
# database=%[\\\\\\\\/]bugs.core_4927.fdb # database=%[\\\\\\\\/]bugs.core_4927.fdb
# { # {
@ -186,41 +190,41 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# print_perf = true # print_perf = true
# } # }
# ''' # '''
# #
# f_trccfg=open( os.path.join(context['temp_directory'],'tmp_trace_4927.cfg'), 'w') # f_trccfg=open( os.path.join(context['temp_directory'],'tmp_trace_4927.cfg'), 'w')
# if engine.startswith('2.5'): # if engine.startswith('2.5'):
# f_trccfg.write(txt25) # f_trccfg.write(txt25)
# else: # else:
# f_trccfg.write(txt30) # f_trccfg.write(txt30)
# #
# flush_and_close( f_trccfg ) # flush_and_close( f_trccfg )
# #
# ##################################################### # #####################################################
# # Starting trace session in new child process (async.): # # Starting trace session in new child process (async.):
# #
# f_trclog=open( os.path.join(context['temp_directory'],'tmp_trace_4927.log'), 'w') # f_trclog=open( os.path.join(context['temp_directory'],'tmp_trace_4927.log'), 'w')
# # Execute a child program in a new process, redirecting STDERR to the same target as of STDOUT: # # 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", # p_trace=Popen([context['fbsvcmgr_path'], "localhost:service_mgr",
# "action_trace_start", # "action_trace_start",
# "trc_cfg", f_trccfg.name], # "trc_cfg", f_trccfg.name],
# stdout=f_trclog, stderr=subprocess.STDOUT) # stdout=f_trclog, stderr=subprocess.STDOUT)
# #
# # Wait! Trace session is initialized not instantly! # # Wait! Trace session is initialized not instantly!
# time.sleep(1) # time.sleep(1)
# #
# sqltxt=''' # sqltxt='''
# set list on; # set list on;
# select result from sp_test; # select result from sp_test;
# ''' # '''
# #
# runProgram('isql',[dsn],sqltxt) # runProgram('isql',[dsn],sqltxt)
# #
# # do NOT remove this otherwise trace log can contain only message about its start before being closed! # # do NOT remove this otherwise trace log can contain only message about its start before being closed!
# time.sleep(3) # time.sleep(3)
# #
# # Getting ID of launched trace session and STOP it: # # Getting ID of launched trace session and STOP it:
# ################################################### # ###################################################
# #
# # Save active trace session info into file for further parsing it and obtain session_id back (for stop): # # Save active trace session info into file for further parsing it and obtain session_id back (for stop):
# f_trclst=open( os.path.join(context['temp_directory'],'tmp_trace_4927.lst'), 'w') # f_trclst=open( os.path.join(context['temp_directory'],'tmp_trace_4927.lst'), 'w')
# subprocess.call([context['fbsvcmgr_path'], "localhost:service_mgr", # subprocess.call([context['fbsvcmgr_path'], "localhost:service_mgr",
@ -228,7 +232,7 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# stdout=f_trclst, stderr=subprocess.STDOUT # stdout=f_trclst, stderr=subprocess.STDOUT
# ) # )
# flush_and_close( f_trclst ) # flush_and_close( f_trclst )
# #
# trcssn=0 # trcssn=0
# with open( f_trclst.name,'r') as f: # with open( f_trclst.name,'r') as f:
# for line in f: # for line in f:
@ -239,7 +243,7 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# trcssn=word # trcssn=word
# i=i+1 # i=i+1
# break # break
# #
# # Result: `trcssn` is ID of active trace session. Now we have to terminate it: # # Result: `trcssn` is ID of active trace session. Now we have to terminate it:
# f_trclst=open(f_trclst.name,'a') # f_trclst=open(f_trclst.name,'a')
# f_trclst.seek(0,2) # f_trclst.seek(0,2)
@ -249,35 +253,66 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# stdout=f_trclst, stderr=subprocess.STDOUT # stdout=f_trclst, stderr=subprocess.STDOUT
# ) # )
# flush_and_close( f_trclst ) # flush_and_close( f_trclst )
# #
# # Terminate child process of launched trace session (though it should already be killed): # # Terminate child process of launched trace session (though it should already be killed):
# p_trace.terminate() # p_trace.terminate()
# flush_and_close( f_trclog ) # flush_and_close( f_trclog )
# #
# with open( f_trclog.name,'r') as f: # with open( f_trclog.name,'r') as f:
# for line in f: # for line in f:
# print(line) # print(line)
# #
# # do NOT remove this delay otherwise get access error 'Windows 32' # # do NOT remove this delay otherwise get access error 'Windows 32'
# # (The process cannot access the file because it is being used by another process): # # (The process cannot access the file because it is being used by another process):
# time.sleep(1) # time.sleep(1)
# #
# # CLEANUP # # CLEANUP
# ######### # #########
# cleanup( (f_trccfg, f_trclst, f_trclog) ) # cleanup( (f_trccfg, f_trclst, f_trclog) )
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
HEADER_2100 HEADER_2100
DETAIL_2100 DETAIL_2100
""" """
@pytest.mark.version('>=2.5.5') def trace_session(act: Action, b: Barrier):
@pytest.mark.xfail cfg30 = ['# Trace config, format for 3.0. Generated auto, do not edit!',
def test_1(db_1): f'database=%[\\\\/]{act.db.db_path.name}',
pytest.fail("Test not IMPLEMENTED") '{',
' enabled = true',
' time_threshold = 0',
' log_initfini = false',
' log_statement_finish = true',
' print_perf = true',
'}']
with act.connect_server() as srv:
srv.trace.start(config='\n'.join(cfg30))
b.wait()
for line in srv:
print(line)
@pytest.mark.version('>=3.0')
def test_1(act_1: Action, capsys):
b = Barrier(2)
trace_thread = Thread(target=trace_session, args=[act_1, b])
trace_thread.start()
b.wait()
act_1.isql(switches=[], input='set list on; select result from sp_test;')
time.sleep(2)
with act_1.connect_server() as srv:
for session in list(srv.trace.sessions.keys()):
srv.trace.stop(session_id=session)
trace_thread.join(1.0)
if trace_thread.is_alive():
pytest.fail('Trace thread still alive')
# Check
act_1.expected_stdout = expected_stdout_1
act_1.stdout = capsys.readouterr().out
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,14 +2,15 @@
# #
# id: bugs.core_4928 # id: bugs.core_4928
# title: It is not possible to save the connection information in the ON CONNECT trigger, if the connection is created by the gbak # title: It is not possible to save the connection information in the ON CONNECT trigger, if the connection is created by the gbak
# decription: # decription:
# tracker_id: CORE-4928 # tracker_id: CORE-4928
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from pathlib import Path
from firebird.qa import db_factory, python_act, Action, temp_file
# version: 3.0 # version: 3.0
# resources: None # resources: None
@ -26,29 +27,29 @@ init_script_1 = """
att_auth varchar(255), att_auth varchar(255),
att_dts timestamp default 'now' att_dts timestamp default 'now'
); );
commit; commit;
set term ^; set term ^;
create or alter trigger trg_connect active on connect as create or alter trigger trg_connect active on connect as
begin begin
in autonomous transaction do in autonomous transaction do
insert into att_log(att_id, att_name, att_user, att_addr, att_prot, att_auth) insert into att_log(att_id, att_name, att_user, att_addr, att_prot, att_auth)
select select
mon$attachment_id mon$attachment_id,
,mon$attachment_name mon$attachment_name,
,mon$user mon$user,
,mon$remote_address mon$remote_address,
,mon$remote_protocol mon$remote_protocol,
,mon$auth_method mon$auth_method
from mon$attachments from mon$attachments
where mon$remote_protocol starting with upper('TCP') and mon$user = upper('SYSDBA') where mon$remote_protocol starting with upper('TCP') and mon$user = upper('SYSDBA')
; ;
end end
^ ^
set term ;^ set term ;^
commit; commit;
""" """
db_1 = db_factory(sql_dialect=3, init=init_script_1) db_1 = db_factory(sql_dialect=3, init=init_script_1)
@ -59,10 +60,10 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# fbk = os.path.join(context['temp_directory'],'tmp.core_4928.fbk') # fbk = os.path.join(context['temp_directory'],'tmp.core_4928.fbk')
# runProgram('gbak',['-b','-user',user_name,'-password',user_password,dsn,fbk]) # runProgram('gbak',['-b','-user',user_name,'-password',user_password,dsn,fbk])
# runProgram('gbak',['-rep','-user',user_name,'-password',user_password,fbk,dsn]) # runProgram('gbak',['-rep','-user',user_name,'-password',user_password,fbk,dsn])
# #
# sql=''' # sql='''
# set list on; # set list on;
# select # select
# iif( att_id > 0, 1, 0) is_att_id_ok # iif( att_id > 0, 1, 0) is_att_id_ok
# ,iif( att_name containing 'core_4928.fdb', 1, 0) is_att_name_ok # ,iif( att_name containing 'core_4928.fdb', 1, 0) is_att_name_ok
# ,iif( att_user = upper('SYSDBA'), 1, 0) is_att_user_ok # ,iif( att_user = upper('SYSDBA'), 1, 0) is_att_user_ok
@ -70,16 +71,17 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# ,iif( upper(att_prot) starting with upper('TCP'), 1, 0) is_att_prot_ok # ,iif( upper(att_prot) starting with upper('TCP'), 1, 0) is_att_prot_ok
# ,iif( att_auth is not null, 1, 0) is_att_auth_ok # ,iif( att_auth is not null, 1, 0) is_att_auth_ok
# ,iif( att_dts is not null, 1, 0) is_att_dts_ok # ,iif( att_dts is not null, 1, 0) is_att_dts_ok
# from att_log # from att_log
# where att_id <> current_connection; # where att_id <> current_connection;
# ''' # '''
# runProgram('isql',[dsn,'-user',user_name,'-pas',user_password],sql) # runProgram('isql',[dsn,'-user',user_name,'-pas',user_password],sql)
# #
# if os.path.isfile(fbk): # if os.path.isfile(fbk):
# os.remove(fbk) # os.remove(fbk)
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
IS_ATT_ID_OK 1 IS_ATT_ID_OK 1
@ -89,11 +91,33 @@ expected_stdout_1 = """
IS_ATT_PROT_OK 1 IS_ATT_PROT_OK 1
IS_ATT_AUTH_OK 1 IS_ATT_AUTH_OK 1
IS_ATT_DTS_OK 1 IS_ATT_DTS_OK 1
""" """
fbk_file_1 = temp_file('tmp_core_4928.fbk')
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action, fbk_file_1: Path):
def test_1(db_1): act_1.gbak(switches=['-b', act_1.db.dsn, str(fbk_file_1)])
pytest.fail("Test not IMPLEMENTED") act_1.reset()
# This was in original test, but it makes no sense as it overwites att_log content
# from backup that does not contain any data on v4.0.0.2496
# It's not important to test the issue anyway
#act_1.gbak(switches=['-rep', str(fbk_file_1), act_1.db.dsn])
#act_1.reset()
# Check
act_1.expected_stdout = expected_stdout_1
act_1.script = """
set list on;
select
iif(att_id > 0, 1, 0) is_att_id_ok,
iif(att_name containing 'test.fdb', 1, 0) is_att_name_ok,
iif(att_user = upper('SYSDBA'), 1, 0) is_att_user_ok,
iif(att_addr is not null, 1, 0) is_att_addr_ok,
iif(upper(att_prot) starting with upper('TCP'), 1, 0) is_att_prot_ok,
iif(att_auth is not null, 1, 0) is_att_auth_ok,
iif(att_dts is not null, 1, 0) is_att_dts_ok
from att_log
where att_id <> current_connection;
"""
act_1.execute()
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,31 +2,32 @@
# #
# id: bugs.core_4933 # id: bugs.core_4933
# title: Add better transaction control to isql # title: Add better transaction control to isql
# decription: # decription:
# Test creates two .sql script and run them using ISQL utility. # Test creates two .sql script and run them using ISQL utility.
# In the 1st script we create view for check current transaction parameters. # In the 1st script we create view for check current transaction parameters.
# View output following values for transaction: # View output following values for transaction:
# TIL, lock resolution (wait/no_wait/lock_timeout), read_only/read_write and [no]auto_undo # TIL, lock resolution (wait/no_wait/lock_timeout), read_only/read_write and [no]auto_undo
# #
# Then we TURN ON keeping of Tx parameters (SET KEEP_TRAN ON) and do some manipulations in this # Then we TURN ON keeping of Tx parameters (SET KEEP_TRAN ON) and do some manipulations in this
# ('main') script, including invocation of auxiliary ('addi') script using IN <...> command. # ('main') script, including invocation of auxiliary ('addi') script using IN <...> command.
# #
# Second script creates another database and the same view in it, then does soma actions there # Second script creates another database and the same view in it, then does some actions there
# and also check output of this view. # and also check output of this view.
# After this (second) script finish, we return to 1st one and resume there final actions. # After this (second) script finish, we return to 1st one and resume there final actions.
# #
# IN ALL STEPS WE HAVE TO SEE THE SAME PARAMS - NO MATTER HOW MUCH TIMES # IN ALL STEPS WE HAVE TO SEE THE SAME PARAMS - NO MATTER HOW MUCH TIMES
# WE DID COMMIT/ROLLBACK/RECONNECT AND EVEN WORK IN OTHER DB. # WE DID COMMIT/ROLLBACK/RECONNECT AND EVEN WORK IN OTHER DB.
# #
# Checked on: 3.0.6.33249; 4.0.0.1777 # Checked on: 3.0.6.33249; 4.0.0.1777
# #
# tracker_id: CORE-4933 # tracker_id: CORE-4933
# min_versions: ['3.0.6'] # min_versions: ['3.0.6']
# versions: 3.0.6 # versions: 3.0.6
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from pathlib import Path
from firebird.qa import db_factory, python_act, Action, temp_file
# version: 3.0.6 # version: 3.0.6
# resources: None # resources: None
@ -41,23 +42,23 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
#--- #---
# import sys # import sys
# import os # import os
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# db_conn.close() # db_conn.close()
# #
# tmp_addi_fdb = os.path.join(context['temp_directory'],'tmp_addi_4933.fdb') # tmp_addi_fdb = os.path.join(context['temp_directory'],'tmp_addi_4933.fdb')
# #
# if os.path.isfile(tmp_addi_fdb): # if os.path.isfile(tmp_addi_fdb):
# os.remove( tmp_addi_fdb ) # os.remove( tmp_addi_fdb )
# #
# #------------------------------------------- # #-------------------------------------------
# #
# sql_addi_script=''' # sql_addi_script='''
# create database 'localhost:%(tmp_addi_fdb)s' user %(user_name)s password '%(user_password)s'; # create database 'localhost:%(tmp_addi_fdb)s' user %(user_name)s password '%(user_password)s';
# #
# recreate view v_check as # recreate view v_check as
# select # select
# decode(t.mon$isolation_mode, 0,'consistency', 1,'snapshot', 2,'rc rec_vers', 3,'rc no_recv', 4,'rc read_cons', 'UNKNOWN') as tx_til_mon_trans # decode(t.mon$isolation_mode, 0,'consistency', 1,'snapshot', 2,'rc rec_vers', 3,'rc no_recv', 4,'rc read_cons', 'UNKNOWN') as tx_til_mon_trans
# ,rdb$get_context('SYSTEM', 'ISOLATION_LEVEL') as tx_til_rdb_get_context # ,rdb$get_context('SYSTEM', 'ISOLATION_LEVEL') as tx_til_rdb_get_context
# ,decode(t.mon$lock_timeout, -1, 'wait', 0, 'no_wait', 'timeout ' || t.mon$lock_timeout) as tx_lock_timeout_mon_trans # ,decode(t.mon$lock_timeout, -1, 'wait', 0, 'no_wait', 'timeout ' || t.mon$lock_timeout) as tx_lock_timeout_mon_trans
@ -69,28 +70,28 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# from mon$transactions t # from mon$transactions t
# where t.mon$transaction_id = current_transaction; # where t.mon$transaction_id = current_transaction;
# commit; # commit;
# #
# select 'addi_script: create_new_db' as msg, v.* from v_check v; # select 'addi_script: create_new_db' as msg, v.* from v_check v;
# rollback; # rollback;
# #
# connect 'localhost:%(tmp_addi_fdb)s' user %(user_name)s password '%(user_password)s'; # connect 'localhost:%(tmp_addi_fdb)s' user %(user_name)s password '%(user_password)s';
# select 'addi_script: reconnect' as msg, v.* from v_check v; # select 'addi_script: reconnect' as msg, v.* from v_check v;
# rollback; # rollback;
# #
# drop database; # drop database;
# ''' # '''
# #
# f_addi_sql = open( os.path.join(context['temp_directory'],'tmp_core_4731_addi.sql'), 'w', buffering = 0) # f_addi_sql = open( os.path.join(context['temp_directory'],'tmp_core_4731_addi.sql'), 'w', buffering = 0)
# f_addi_sql.write( sql_addi_script % dict(globals(), **locals()) ) # f_addi_sql.write( sql_addi_script % dict(globals(), **locals()) )
# f_addi_sql.close() # f_addi_sql.close()
# f_addi_sql_name = f_addi_sql.name # f_addi_sql_name = f_addi_sql.name
# #------------------------------------------- # #-------------------------------------------
# #
# sql_main_script=''' # sql_main_script='''
# set list on; # set list on;
# connect '%(dsn)s' user %(user_name)s password '%(user_password)s'; # connect '%(dsn)s' user %(user_name)s password '%(user_password)s';
# recreate view v_check as # recreate view v_check as
# select # select
# decode(t.mon$isolation_mode, 0,'consistency', 1,'snapshot', 2,'rc rec_vers', 3,'rc no_recv', 4,'rc read_cons', 'UNKNOWN') as tx_til_mon_trans # decode(t.mon$isolation_mode, 0,'consistency', 1,'snapshot', 2,'rc rec_vers', 3,'rc no_recv', 4,'rc read_cons', 'UNKNOWN') as tx_til_mon_trans
# ,rdb$get_context('SYSTEM', 'ISOLATION_LEVEL') as tx_til_rdb_get_context # ,rdb$get_context('SYSTEM', 'ISOLATION_LEVEL') as tx_til_rdb_get_context
# ,decode(t.mon$lock_timeout, -1, 'wait', 0, 'no_wait', 'timeout ' || t.mon$lock_timeout) as tx_lock_timeout_mon_trans # ,decode(t.mon$lock_timeout, -1, 'wait', 0, 'no_wait', 'timeout ' || t.mon$lock_timeout) as tx_lock_timeout_mon_trans
@ -102,64 +103,65 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# from mon$transactions t # from mon$transactions t
# where t.mon$transaction_id = current_transaction; # where t.mon$transaction_id = current_transaction;
# commit; # commit;
# #
# select 'main_script: initial' as msg, v.* from v_check v; # select 'main_script: initial' as msg, v.* from v_check v;
# commit; # commit;
# #
# set keep_tran on; # set keep_tran on;
# commit; # commit;
# #
# set transaction read only read committed record_version lock timeout 5 no auto undo; -- only in 4.x: auto commit; # set transaction read only read committed record_version lock timeout 5 no auto undo; -- only in 4.x: auto commit;
# #
# select 'main_script: started Tx' as msg, v.* from v_check v; # select 'main_script: started Tx' as msg, v.* from v_check v;
# #
# commit; -------------------------------------------------------------------------------------- [ 1 ] # commit; -------------------------------------------------------------------------------------- [ 1 ]
# #
# select 'main_script: after_commit' as msg, v.* from v_check v; # select 'main_script: after_commit' as msg, v.* from v_check v;
# #
# rollback; ------------------------------------------------------------------------------------ [ 2 ] # rollback; ------------------------------------------------------------------------------------ [ 2 ]
# #
# select 'main_script: after_rollback' as msg, v.* from v_check v; # select 'main_script: after_rollback' as msg, v.* from v_check v;
# #
# rollback; # rollback;
# #
# connect '%(dsn)s' user %(user_name)s password '%(user_password)s'; --------------------------- [ 3 ] # connect '%(dsn)s' user %(user_name)s password '%(user_password)s'; --------------------------- [ 3 ]
# #
# select 'main_script: after_reconnect' as msg, v.* from v_check v; # select 'main_script: after_reconnect' as msg, v.* from v_check v;
# rollback; # rollback;
# #
# --################### # --###################
# in %(f_addi_sql_name)s; # in %(f_addi_sql_name)s;
# --################### # --###################
# #
# connect '%(dsn)s' user %(user_name)s password '%(user_password)s'; --------------------------- [ 5 ] # connect '%(dsn)s' user %(user_name)s password '%(user_password)s'; --------------------------- [ 5 ]
# #
# select 'main_script: resume' as msg, v.* from v_check v; # select 'main_script: resume' as msg, v.* from v_check v;
# rollback; # rollback;
# #
# set keep_tran off; # set keep_tran off;
# commit; # commit;
# #
# select 'keep_tran: turned_off' as msg, v.* from v_check v; # select 'keep_tran: turned_off' as msg, v.* from v_check v;
# commit; # commit;
# ''' # '''
# #
# f_main_sql = open( os.path.join(context['temp_directory'],'tmp_core_4731_main.sql'), 'w', buffering = 0) # f_main_sql = open( os.path.join(context['temp_directory'],'tmp_core_4731_main.sql'), 'w', buffering = 0)
# f_main_sql.write( sql_main_script % dict(globals(), **locals()) ) # f_main_sql.write( sql_main_script % dict(globals(), **locals()) )
# f_main_sql.close() # f_main_sql.close()
# #
# runProgram( 'isql',['-q', '-i', f_main_sql.name] ) # runProgram( 'isql',['-q', '-i', f_main_sql.name] )
# #
# os.remove( f_main_sql.name ) # os.remove( f_main_sql.name )
# os.remove( f_addi_sql.name ) # os.remove( f_addi_sql.name )
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
MSG main_script: initial MSG main_script: initial
TX_TIL_MON_TRANS snapshot TX_TIL_MON_TRANS snapshot
TX_TIL_RDB_GET_CONTEXT SNAPSHOT TX_TIL_RDB_GET_CONTEXT SNAPSHOT
TX_LOCK_TIMEOUT_MON_TRANS wait TX_LOCK_TIMEOUT_MON_TRANS wait
TX_LOCK_TIMEOUT_RDB_GET_CONTEXT -1 TX_LOCK_TIMEOUT_RDB_GET_CONTEXT -1
@ -169,88 +171,176 @@ expected_stdout_1 = """
MSG main_script: started Tx MSG main_script: started Tx
TX_TIL_MON_TRANS rc rec_vers TX_TIL_MON_TRANS rc rec_vers
TX_TIL_RDB_GET_CONTEXT READ COMMITTED TX_TIL_RDB_GET_CONTEXT READ COMMITTED
TX_LOCK_TIMEOUT_MON_TRANS timeout 5 TX_LOCK_TIMEOUT_MON_TRANS timeout 5
TX_LOCK_TIMEOUT_RDB_GET_CONTEXT 5 TX_LOCK_TIMEOUT_RDB_GET_CONTEXT 5
TX_READ_ONLY_MON_TRANS read_only TX_READ_ONLY_MON_TRANS read_only
TX_READ_ONLY_RDB_GET_CONTEXT TRUE TX_READ_ONLY_RDB_GET_CONTEXT TRUE
TX_AUTOUNDO_MON_TRANS 0 TX_AUTOUNDO_MON_TRANS 0
MSG main_script: after_commit MSG main_script: after_commit
TX_TIL_MON_TRANS rc rec_vers TX_TIL_MON_TRANS rc rec_vers
TX_TIL_RDB_GET_CONTEXT READ COMMITTED TX_TIL_RDB_GET_CONTEXT READ COMMITTED
TX_LOCK_TIMEOUT_MON_TRANS timeout 5 TX_LOCK_TIMEOUT_MON_TRANS timeout 5
TX_LOCK_TIMEOUT_RDB_GET_CONTEXT 5 TX_LOCK_TIMEOUT_RDB_GET_CONTEXT 5
TX_READ_ONLY_MON_TRANS read_only TX_READ_ONLY_MON_TRANS read_only
TX_READ_ONLY_RDB_GET_CONTEXT TRUE TX_READ_ONLY_RDB_GET_CONTEXT TRUE
TX_AUTOUNDO_MON_TRANS 0 TX_AUTOUNDO_MON_TRANS 0
MSG main_script: after_rollback MSG main_script: after_rollback
TX_TIL_MON_TRANS rc rec_vers TX_TIL_MON_TRANS rc rec_vers
TX_TIL_RDB_GET_CONTEXT READ COMMITTED TX_TIL_RDB_GET_CONTEXT READ COMMITTED
TX_LOCK_TIMEOUT_MON_TRANS timeout 5 TX_LOCK_TIMEOUT_MON_TRANS timeout 5
TX_LOCK_TIMEOUT_RDB_GET_CONTEXT 5 TX_LOCK_TIMEOUT_RDB_GET_CONTEXT 5
TX_READ_ONLY_MON_TRANS read_only TX_READ_ONLY_MON_TRANS read_only
TX_READ_ONLY_RDB_GET_CONTEXT TRUE TX_READ_ONLY_RDB_GET_CONTEXT TRUE
TX_AUTOUNDO_MON_TRANS 0 TX_AUTOUNDO_MON_TRANS 0
MSG main_script: after_reconnect MSG main_script: after_reconnect
TX_TIL_MON_TRANS rc rec_vers TX_TIL_MON_TRANS rc rec_vers
TX_TIL_RDB_GET_CONTEXT READ COMMITTED TX_TIL_RDB_GET_CONTEXT READ COMMITTED
TX_LOCK_TIMEOUT_MON_TRANS timeout 5 TX_LOCK_TIMEOUT_MON_TRANS timeout 5
TX_LOCK_TIMEOUT_RDB_GET_CONTEXT 5 TX_LOCK_TIMEOUT_RDB_GET_CONTEXT 5
TX_READ_ONLY_MON_TRANS read_only TX_READ_ONLY_MON_TRANS read_only
TX_READ_ONLY_RDB_GET_CONTEXT TRUE TX_READ_ONLY_RDB_GET_CONTEXT TRUE
TX_AUTOUNDO_MON_TRANS 0 TX_AUTOUNDO_MON_TRANS 0
MSG addi_script: create_new_db MSG addi_script: create_new_db
TX_TIL_MON_TRANS rc rec_vers TX_TIL_MON_TRANS rc rec_vers
TX_TIL_RDB_GET_CONTEXT READ COMMITTED TX_TIL_RDB_GET_CONTEXT READ COMMITTED
TX_LOCK_TIMEOUT_MON_TRANS timeout 5 TX_LOCK_TIMEOUT_MON_TRANS timeout 5
TX_LOCK_TIMEOUT_RDB_GET_CONTEXT 5 TX_LOCK_TIMEOUT_RDB_GET_CONTEXT 5
TX_READ_ONLY_MON_TRANS read_only TX_READ_ONLY_MON_TRANS read_only
TX_READ_ONLY_RDB_GET_CONTEXT TRUE TX_READ_ONLY_RDB_GET_CONTEXT TRUE
TX_AUTOUNDO_MON_TRANS 0 TX_AUTOUNDO_MON_TRANS 0
MSG addi_script: reconnect MSG addi_script: reconnect
TX_TIL_MON_TRANS rc rec_vers TX_TIL_MON_TRANS rc rec_vers
TX_TIL_RDB_GET_CONTEXT READ COMMITTED TX_TIL_RDB_GET_CONTEXT READ COMMITTED
TX_LOCK_TIMEOUT_MON_TRANS timeout 5 TX_LOCK_TIMEOUT_MON_TRANS timeout 5
TX_LOCK_TIMEOUT_RDB_GET_CONTEXT 5 TX_LOCK_TIMEOUT_RDB_GET_CONTEXT 5
TX_READ_ONLY_MON_TRANS read_only TX_READ_ONLY_MON_TRANS read_only
TX_READ_ONLY_RDB_GET_CONTEXT TRUE TX_READ_ONLY_RDB_GET_CONTEXT TRUE
TX_AUTOUNDO_MON_TRANS 0 TX_AUTOUNDO_MON_TRANS 0
MSG main_script: resume MSG main_script: resume
TX_TIL_MON_TRANS rc rec_vers TX_TIL_MON_TRANS rc rec_vers
TX_TIL_RDB_GET_CONTEXT READ COMMITTED TX_TIL_RDB_GET_CONTEXT READ COMMITTED
TX_LOCK_TIMEOUT_MON_TRANS timeout 5 TX_LOCK_TIMEOUT_MON_TRANS timeout 5
TX_LOCK_TIMEOUT_RDB_GET_CONTEXT 5 TX_LOCK_TIMEOUT_RDB_GET_CONTEXT 5
TX_READ_ONLY_MON_TRANS read_only TX_READ_ONLY_MON_TRANS read_only
TX_READ_ONLY_RDB_GET_CONTEXT TRUE TX_READ_ONLY_RDB_GET_CONTEXT TRUE
TX_AUTOUNDO_MON_TRANS 0 TX_AUTOUNDO_MON_TRANS 0
MSG keep_tran: turned_off MSG keep_tran: turned_off
TX_TIL_MON_TRANS snapshot TX_TIL_MON_TRANS snapshot
TX_TIL_RDB_GET_CONTEXT SNAPSHOT TX_TIL_RDB_GET_CONTEXT SNAPSHOT
TX_LOCK_TIMEOUT_MON_TRANS wait TX_LOCK_TIMEOUT_MON_TRANS wait
TX_LOCK_TIMEOUT_RDB_GET_CONTEXT -1 TX_LOCK_TIMEOUT_RDB_GET_CONTEXT -1
TX_READ_ONLY_MON_TRANS read_write TX_READ_ONLY_MON_TRANS read_write
TX_READ_ONLY_RDB_GET_CONTEXT FALSE TX_READ_ONLY_RDB_GET_CONTEXT FALSE
TX_AUTOUNDO_MON_TRANS 1 TX_AUTOUNDO_MON_TRANS 1
""" """
addi_script_1 = temp_file('addi_script.sql')
main_script_1 = temp_file('main_script.sql')
tmp_db_1 = temp_file('tmp_addi_4933.fdb')
@pytest.mark.version('>=3.0.6') @pytest.mark.version('>=3.0.6')
@pytest.mark.xfail def test_1(act_1: Action, addi_script_1: Path, main_script_1: Path):
def test_1(db_1): addi_script_1.write_text(f"""
pytest.fail("Test not IMPLEMENTED") create database 'localhost:{tmp_db_1}' user {act_1.db.user} password '{act_1.db.password}';
recreate view v_check as
select
decode(t.mon$isolation_mode, 0,'consistency', 1,'snapshot', 2,'rc rec_vers', 3,'rc no_recv', 4,'rc read_cons', 'UNKNOWN') as tx_til_mon_trans,
rdb$get_context('SYSTEM', 'ISOLATION_LEVEL') as tx_til_rdb_get_context,
decode(t.mon$lock_timeout, -1, 'wait', 0, 'no_wait', 'timeout ' || t.mon$lock_timeout) as tx_lock_timeout_mon_trans,
rdb$get_context('SYSTEM', 'LOCK_TIMEOUT') as tx_lock_timeout_rdb_get_context,
iif(t.mon$read_only=1,'read_only','read_write') as tx_read_only_mon_trans,
rdb$get_context('SYSTEM', 'READ_ONLY') as tx_read_only_rdb_get_context,
t.mon$auto_undo as tx_autoundo_mon_trans
-- only in FB 4.x+: ,t.mon$auto_commit as tx_autocommit_mon_trans
from mon$transactions t
where t.mon$transaction_id = current_transaction;
commit;
select 'addi_script: create_new_db' as msg, v.* from v_check v;
rollback;
connect 'localhost:{tmp_db_1}' user {act_1.db.user} password '{act_1.db.password}';
select 'addi_script: reconnect' as msg, v.* from v_check v;
rollback;
drop database;
""")
main_script_1.write_text(f"""
set list on;
connect '{act_1.db.dsn}' user {act_1.db.user} password '{act_1.db.password}';
recreate view v_check as
select
decode(t.mon$isolation_mode, 0,'consistency', 1,'snapshot', 2,'rc rec_vers', 3,'rc no_recv', 4,'rc read_cons', 'UNKNOWN') as tx_til_mon_trans,
rdb$get_context('SYSTEM', 'ISOLATION_LEVEL') as tx_til_rdb_get_context,
decode(t.mon$lock_timeout, -1, 'wait', 0, 'no_wait', 'timeout ' || t.mon$lock_timeout) as tx_lock_timeout_mon_trans,
rdb$get_context('SYSTEM', 'LOCK_TIMEOUT') as tx_lock_timeout_rdb_get_context,
iif(t.mon$read_only=1,'read_only','read_write') as tx_read_only_mon_trans,
rdb$get_context('SYSTEM', 'READ_ONLY') as tx_read_only_rdb_get_context,
t.mon$auto_undo as tx_autoundo_mon_trans
-- only 4.x: ,t.mon$auto_commit as tx_autocommit_mon_trans
from mon$transactions t
where t.mon$transaction_id = current_transaction;
commit;
select 'main_script: initial' as msg, v.* from v_check v;
commit;
set keep_tran on;
commit;
set transaction read only read committed record_version lock timeout 5 no auto undo; -- only in 4.x: auto commit;
select 'main_script: started Tx' as msg, v.* from v_check v;
commit; -------------------------------------------------------------------------------------- [ 1 ]
select 'main_script: after_commit' as msg, v.* from v_check v;
rollback; ------------------------------------------------------------------------------------ [ 2 ]
select 'main_script: after_rollback' as msg, v.* from v_check v;
rollback;
connect '{act_1.db.dsn}' user {act_1.db.user} password '{act_1.db.password}'; --------------------------- [ 3 ]
select 'main_script: after_reconnect' as msg, v.* from v_check v;
rollback;
--###################
in {addi_script_1};
--###################
connect '{act_1.db.dsn}' user {act_1.db.user} password '{act_1.db.password}'; --------------------------- [ 5 ]
select 'main_script: resume' as msg, v.* from v_check v;
rollback;
set keep_tran off;
commit;
select 'keep_tran: turned_off' as msg, v.* from v_check v;
commit;
""")
# Check
act_1.expected_stdout = expected_stdout_1
act_1.isql(switches=['-q'], input_file=main_script_1)
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,9 +2,9 @@
# #
# id: bugs.core_4964 # id: bugs.core_4964
# title: Real errors during connect to security database are hidden by Srp user manager. Errors should be logged no matter what AuthServer is used. # title: Real errors during connect to security database are hidden by Srp user manager. Errors should be logged no matter what AuthServer is used.
# decription: # decription:
# Test obtains full path to $fb_home via FBSVCMGR info_get_env. # Test obtains full path to $fb_home via FBSVCMGR info_get_env.
# Then it makes copy of file 'databases.conf' that is in $fb_home directory because # Then it makes copy of file 'databases.conf' that is in $fb_home directory because
# following lines will be added to that 'databases.conf': # following lines will be added to that 'databases.conf':
# === # ===
# tmp_alias_4964 = ... # tmp_alias_4964 = ...
@ -13,18 +13,18 @@
# } # }
# === # ===
# NB: we intentionally put reference to file that for sure does exist but is INVALID for usage as fdb: 'firebird.msg' # NB: we intentionally put reference to file that for sure does exist but is INVALID for usage as fdb: 'firebird.msg'
# #
# Then we: # Then we:
# 1) obtain content of server firebird.log # 1) obtain content of server firebird.log
# 2) try to make connect to alias 'tmp_alias_4964' and (as expected) get error. # 2) try to make connect to alias 'tmp_alias_4964' and (as expected) get error.
# 3) wait a little and obtain again content of server firebird.log # 3) wait a little and obtain again content of server firebird.log
# #
# Finally we restore original databases.conf and check that: # Finally we restore original databases.conf and check that:
# 1) Client error message contains phrase about need to check server firebird.log for details. # 1) Client error message contains phrase about need to check server firebird.log for details.
# 2) Difference of firebird.log contains messages that engine could not attach to password database # 2) Difference of firebird.log contains messages that engine could not attach to password database
# because it is invalid (we specify 'firebird.msg' as security_db in databases.conf for test database, # because it is invalid (we specify 'firebird.msg' as security_db in databases.conf for test database,
# and of course this is not valid database) # and of course this is not valid database)
# #
# Client always get message with gdscode = 335545106 and sqlcode=-902. # Client always get message with gdscode = 335545106 and sqlcode=-902.
# Error text in firebird.log depends on what plugin is used for authentification: # Error text in firebird.log depends on what plugin is used for authentification:
# 1) Legacy: # 1) Legacy:
@ -34,16 +34,16 @@
# file <...> is not a valid database # file <...> is not a valid database
# 2) Srp: # 2) Srp:
# Authentication error # Authentication error
# file C:\\FBSS\\FIREBIRD.MSG is not a valid database # file C:\\FB\\SS\\FIREBIRD.MSG is not a valid database
# #
# Checked for: # Checked for:
# FB30SS, build 3.0.4.32972: OK, 3.360s. # FB30SS, build 3.0.4.32972: OK, 3.360s.
# FB40SS, build 4.0.0.977: OK, 3.485s. # FB40SS, build 4.0.0.977: OK, 3.485s.
# #
# Refactored 05.01.2020 (firebird.conf now contains Srp as first plugin in UserManager parameter): # Refactored 05.01.2020 (firebird.conf now contains Srp as first plugin in UserManager parameter):
# 4.0.0.1714 SS: 2.922s; 4.0.0.1714 SC: 5.563s; 4.0.0.1714 CS: 9.172s. # 4.0.0.1714 SS: 2.922s; 4.0.0.1714 SC: 5.563s; 4.0.0.1714 CS: 9.172s.
# 3.0.5.33221 SS: 2.015s; 3.0.5.33221 SC: 3.469s; 3.0.5.33221 CS: 6.173s. # 3.0.5.33221 SS: 2.015s; 3.0.5.33221 SC: 3.469s; 3.0.5.33221 CS: 6.173s.
# #
# tracker_id: CORE-4964 # tracker_id: CORE-4964
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
@ -55,7 +55,8 @@ from firebird.qa import db_factory, isql_act, Action
# version: 3.0 # version: 3.0
# resources: None # resources: None
substitutions_1 = [('FILE.*FIREBIRD.MSG', 'FILE FIREBIRD.MSG'), ('CLIENT_MSG: 335545106L', 'CLIENT_MSG: 335545106')] substitutions_1 = [('FILE.*FIREBIRD.MSG', 'FILE FIREBIRD.MSG'),
('CLIENT_MSG: 335545106L', 'CLIENT_MSG: 335545106')]
init_script_1 = """""" init_script_1 = """"""
@ -63,7 +64,7 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# import subprocess # import subprocess
# from subprocess import Popen # from subprocess import Popen
@ -73,29 +74,29 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# import shutil # import shutil
# import re # import re
# from fdb import services # from fdb import services
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# db_conn.close() # db_conn.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close(file_handle): # def flush_and_close(file_handle):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb'): # if file_handle.mode not in ('r', 'rb'):
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -106,16 +107,16 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# else: # else:
# print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.') # print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.')
# del_name = None # del_name = None
# #
# if del_name and os.path.isfile( del_name ): # if del_name and os.path.isfile( del_name ):
# os.remove( del_name ) # os.remove( del_name )
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def svc_get_fb_log( f_fb_log ): # def svc_get_fb_log( f_fb_log ):
# #
# global subprocess # global subprocess
# #
# subprocess.call( [ context['fbsvcmgr_path'], # subprocess.call( [ context['fbsvcmgr_path'],
# "localhost:service_mgr", # "localhost:service_mgr",
# "action_get_fb_log" # "action_get_fb_log"
@ -123,26 +124,26 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# stdout=f_fb_log, stderr=subprocess.STDOUT # stdout=f_fb_log, stderr=subprocess.STDOUT
# ) # )
# return # return
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# svc = services.connect(host='localhost') # svc = services.connect(host='localhost')
# fb_home=svc.get_home_directory() # fb_home=svc.get_home_directory()
# svc.close() # svc.close()
# #
# dts = datetime.datetime.now().strftime("%y%m%d_%H%M%S") # dts = datetime.datetime.now().strftime("%y%m%d_%H%M%S")
# #
# dbconf = os.path.join(fb_home, 'databases.conf') # dbconf = os.path.join(fb_home, 'databases.conf')
# fbconf = os.path.join(fb_home, 'firebird.conf') # fbconf = os.path.join(fb_home, 'firebird.conf')
# #
# dbcbak = os.path.join(fb_home, 'databases_'+dts+'.bak') # dbcbak = os.path.join(fb_home, 'databases_'+dts+'.bak')
# fbcbak = os.path.join(fb_home, 'firebird_'+dts+'.bak') # fbcbak = os.path.join(fb_home, 'firebird_'+dts+'.bak')
# #
# shutil.copy2( dbconf, dbcbak ) # shutil.copy2( dbconf, dbcbak )
# shutil.copy2( fbconf, fbcbak ) # shutil.copy2( fbconf, fbcbak )
# #
# tmp_fdb=os.path.join(context['temp_directory'],'tmp_4964.fdb') # tmp_fdb=os.path.join(context['temp_directory'],'tmp_4964.fdb')
# #
# f_dbconf=open(dbconf,'a') # f_dbconf=open(dbconf,'a')
# f_dbconf.seek(0, 2) # f_dbconf.seek(0, 2)
# f_dbconf.write("\\n\\n# Temporarily added by fbtest, CORE-4964. Should be removed auto:") # f_dbconf.write("\\n\\n# Temporarily added by fbtest, CORE-4964. Should be removed auto:")
@ -151,29 +152,29 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# f_dbconf.write("\\n{\\n SecurityDatabase = $(dir_conf)/firebird.msg\\n}") # f_dbconf.write("\\n{\\n SecurityDatabase = $(dir_conf)/firebird.msg\\n}")
# f_dbconf.write("\\n#" + '='*60 ) # f_dbconf.write("\\n#" + '='*60 )
# f_dbconf.close() # f_dbconf.close()
# #
# f_fbconf=open(fbconf,'r') # f_fbconf=open(fbconf,'r')
# fbconf_content=f_fbconf.readlines() # fbconf_content=f_fbconf.readlines()
# f_fbconf.close() # f_fbconf.close()
# for i,s in enumerate( fbconf_content ): # for i,s in enumerate( fbconf_content ):
# if s.lower().lstrip().startswith( 'wirecrypt'.lower() ): # if s.lower().lstrip().startswith( 'wirecrypt'.lower() ):
# fbconf_content[i] = '# <temply commented> ' + s # fbconf_content[i] = '# <temply commented> ' + s
# #
# fbconf_content.append('\\n# Temporarily added by fbtest, CORE-4964. Should be removed auto:') # fbconf_content.append('\\n# Temporarily added by fbtest, CORE-4964. Should be removed auto:')
# fbconf_content.append("\\n#" + '='*30 ) # fbconf_content.append("\\n#" + '='*30 )
# fbconf_content.append('\\nWireCrypt = Disabled') # fbconf_content.append('\\nWireCrypt = Disabled')
# fbconf_content.append("\\n#" + '='*30 ) # fbconf_content.append("\\n#" + '='*30 )
# #
# f_fbconf=open(fbconf,'w') # f_fbconf=open(fbconf,'w')
# f_fbconf.writelines( fbconf_content ) # f_fbconf.writelines( fbconf_content )
# flush_and_close( f_fbconf ) # flush_and_close( f_fbconf )
# #
# f_fblog_before=open( os.path.join(context['temp_directory'],'tmp_4964_fblog_before.txt'), 'w') # f_fblog_before=open( os.path.join(context['temp_directory'],'tmp_4964_fblog_before.txt'), 'w')
# svc_get_fb_log( f_fblog_before ) # svc_get_fb_log( f_fblog_before )
# flush_and_close( f_fblog_before ) # flush_and_close( f_fblog_before )
# #
# f_connect_log=open( os.path.join(context['temp_directory'],'tmp_connect_4964.log'), 'w') # f_connect_log=open( os.path.join(context['temp_directory'],'tmp_connect_4964.log'), 'w')
# #
# try: # try:
# # Try to connect to 'firebird.msg' which is obviously not a database file: # # Try to connect to 'firebird.msg' which is obviously not a database file:
# ################################### # ###################################
@ -183,37 +184,37 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# except Exception,e: # except Exception,e:
# for x in e: # for x in e:
# f_connect_log.write( repr(x)+'\\n' ) # f_connect_log.write( repr(x)+'\\n' )
# #
# flush_and_close( f_connect_log ) # flush_and_close( f_connect_log )
# #
# f_fblog_after=open( os.path.join(context['temp_directory'],'tmp_4964_fblog_after.txt'), 'w') # f_fblog_after=open( os.path.join(context['temp_directory'],'tmp_4964_fblog_after.txt'), 'w')
# svc_get_fb_log( f_fblog_after ) # svc_get_fb_log( f_fblog_after )
# flush_and_close( f_fblog_after ) # flush_and_close( f_fblog_after )
# #
# #
# # RESTORE original config: # # RESTORE original config:
# ########################## # ##########################
# shutil.move( dbcbak, dbconf ) # shutil.move( dbcbak, dbconf )
# shutil.move( fbcbak, fbconf ) # shutil.move( fbcbak, fbconf )
# #
# # Compare firebird.log versions BEFORE and AFTER this test: # # Compare firebird.log versions BEFORE and AFTER this test:
# ###################### # ######################
# #
# oldfb=open(f_fblog_before.name, 'r') # oldfb=open(f_fblog_before.name, 'r')
# newfb=open(f_fblog_after.name, 'r') # newfb=open(f_fblog_after.name, 'r')
# #
# difftext = ''.join(difflib.unified_diff( # difftext = ''.join(difflib.unified_diff(
# oldfb.readlines(), # oldfb.readlines(),
# newfb.readlines() # newfb.readlines()
# )) # ))
# oldfb.close() # oldfb.close()
# newfb.close() # newfb.close()
# #
# f_diff_txt=open( os.path.join(context['temp_directory'],'tmp_4964_diff.txt'), 'w') # f_diff_txt=open( os.path.join(context['temp_directory'],'tmp_4964_diff.txt'), 'w')
# f_diff_txt.write(difftext) # f_diff_txt.write(difftext)
# flush_and_close( f_diff_txt ) # flush_and_close( f_diff_txt )
# #
# #
# allowed_patterns = ( # allowed_patterns = (
# re.compile('cannot\\s+attach\\s+to\\s+password+\\s+database\\.*', re.IGNORECASE) # re.compile('cannot\\s+attach\\s+to\\s+password+\\s+database\\.*', re.IGNORECASE)
# ,re.compile('error\\s+in\\s+isc_attach_database\\(\\)\\s+API\\.*', re.IGNORECASE) # ,re.compile('error\\s+in\\s+isc_attach_database\\(\\)\\s+API\\.*', re.IGNORECASE)
@ -222,30 +223,30 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# ,re.compile('335545106') # ,re.compile('335545106')
# ,re.compile('-902') # ,re.compile('-902')
# ) # )
# #
# with open( f_connect_log.name,'r') as f: # with open( f_connect_log.name,'r') as f:
# for line in f: # for line in f:
# match2some = filter( None, [ p.search(line) for p in allowed_patterns ] ) # match2some = filter( None, [ p.search(line) for p in allowed_patterns ] )
# if match2some: # if match2some:
# print( 'CLIENT_MSG: ' + line.upper() ) # print( 'CLIENT_MSG: ' + line.upper() )
# #
# with open( f_diff_txt.name,'r') as f: # with open( f_diff_txt.name,'r') as f:
# for line in f: # for line in f:
# if line.startswith('+'): # if line.startswith('+'):
# match2some = filter( None, [ p.search(line) for p in allowed_patterns ] ) # match2some = filter( None, [ p.search(line) for p in allowed_patterns ] )
# if match2some: # if match2some:
# print( 'FIREBIRD.LOG: ' + (' '.join(line.split()).upper()) ) # print( 'FIREBIRD.LOG: ' + (' '.join(line.split()).upper()) )
# #
# #
# #
# # Cleanup: # # Cleanup:
# ########## # ##########
# # do NOT remove this pause otherwise some of logs will not be enable for deletion and test will finish with # # do NOT remove this pause otherwise some of logs will not be enable for deletion and test will finish with
# # Exception raised while executing Python test script. exception: WindowsError: 32 # # Exception raised while executing Python test script. exception: WindowsError: 32
# time.sleep(1) # time.sleep(1)
# cleanup( (f_connect_log, f_diff_txt, f_fblog_before, f_fblog_after) ) # cleanup( (f_connect_log, f_diff_txt, f_fblog_before, f_fblog_after) )
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1) #act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
@ -255,11 +256,9 @@ expected_stdout_1 = """
CLIENT_MSG: 335545106L CLIENT_MSG: 335545106L
FIREBIRD.LOG: + AUTHENTICATION ERROR FIREBIRD.LOG: + AUTHENTICATION ERROR
FIREBIRD.LOG: + FILE FIREBIRD.MSG IS NOT A VALID DATABASE FIREBIRD.LOG: + FILE FIREBIRD.MSG IS NOT A VALID DATABASE
""" """
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail
def test_1(db_1): def test_1(db_1):
pytest.fail("Test not IMPLEMENTED") pytest.skip("Test requires manipulation with databases.conf")
#pytest.fail("Test not IMPLEMENTED")

View File

@ -2,17 +2,17 @@
# #
# id: bugs.core_4977 # id: bugs.core_4977
# title: Detach using Linux client takes much longer than from Windows # title: Detach using Linux client takes much longer than from Windows
# decription: # decription:
# # *** NOTE *** # # *** NOTE ***
# # We measure APPROXIMATE time that is required for detaching from database by evaluating number of seconds that passed # # We measure APPROXIMATE time that is required for detaching from database by evaluating number of seconds that passed
# # from UNIX standard epoch time inside ISQL and writing it to log. After returning control from ISQL we evaluate again # # from UNIX standard epoch time inside ISQL and writing it to log. After returning control from ISQL we evaluate again
# # that number by calling Python 'time.time()' - and it will return value upto current UTC time, i.e. it WILL take in # # that number by calling Python 'time.time()' - and it will return value upto current UTC time, i.e. it WILL take in
# # account local timezone from OS settings (this is so at least on Windows). Thus we have to add/substract time shift # # account local timezone from OS settings (this is so at least on Windows). Thus we have to add/substract time shift
# # between UTC and local time - this is done by 'time.timezone' summand. # # between UTC and local time - this is done by 'time.timezone' command.
# # On PC-host with CPU 3.0 GHz and 2Gb RAM) in almost all cases difference was less than 1000 ms, so it was decided # # On PC-host with CPU 3.0 GHz and 2Gb RAM) in almost all cases difference was less than 1000 ms, so it was decided
# # to set MAX_DETACH_TIME_THRESHOLD = 1200 ms. # # to set MAX_DETACH_TIME_THRESHOLD = 1200 ms.
# # Tested on WI-V3.0.0.32140 (SS/SC/CC). # # Tested on WI-V3.0.0.32140 (SS/SC/CC).
# #
# Results for 22.05.2017: # Results for 22.05.2017:
# fb30Cs, build 3.0.3.32725: OK, 1.796ss. # fb30Cs, build 3.0.3.32725: OK, 1.796ss.
# fb30SC, build 3.0.3.32725: OK, 1.047ss. # fb30SC, build 3.0.3.32725: OK, 1.047ss.
@ -20,18 +20,19 @@
# FB40CS, build 4.0.0.645: OK, 2.032ss. # FB40CS, build 4.0.0.645: OK, 2.032ss.
# FB40SC, build 4.0.0.645: OK, 1.188ss. # FB40SC, build 4.0.0.645: OK, 1.188ss.
# FB40SS, build 4.0.0.645: OK, 1.157ss. # FB40SS, build 4.0.0.645: OK, 1.157ss.
# #
# 13.04.2021. Adapted for run both on Windows and Linux. Checked on: # 13.04.2021. Adapted for run both on Windows and Linux. Checked on:
# Windows: 3.0.8.33445, 4.0.0.2416 # Windows: 3.0.8.33445, 4.0.0.2416
# Linux: 3.0.8.33426, 4.0.0.2416 # Linux: 3.0.8.33426, 4.0.0.2416
# #
# tracker_id: CORE-4977 # tracker_id: CORE-4977
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action import time
from firebird.qa import db_factory, python_act, Action
# version: 3.0 # version: 3.0
# resources: None # resources: None
@ -44,37 +45,37 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# import subprocess # import subprocess
# import time # import time
# db_conn.close() # db_conn.close()
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# ############################################ # ############################################
# ### d e f i n e t h r e s h o l d ### # ### d e f i n e t h r e s h o l d ###
# ############################################ # ############################################
# MAX_DETACH_TIME_THRESHOLD=1200 # MAX_DETACH_TIME_THRESHOLD=1200
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close( file_handle ): # def flush_and_close( file_handle ):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull: # if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull:
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -86,34 +87,34 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.') # print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.')
# print('type(f_names_list[i])=',type(f_names_list[i])) # print('type(f_names_list[i])=',type(f_names_list[i]))
# del_name = None # del_name = None
# #
# if del_name and os.path.isfile( del_name ): # if del_name and os.path.isfile( del_name ):
# os.remove( del_name ) # os.remove( del_name )
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# sqltxt=''' # sqltxt='''
# set list on; # set list on;
# select datediff(second from timestamp '01.01.1970 00:00:00.000' to current_timestamp) as " " # select datediff(second from timestamp '01.01.1970 00:00:00.000' to current_timestamp) as " "
# from rdb$types rows 1; # from rdb$types rows 1;
# ''' # '''
# #
# f_isql_cmd=open( os.path.join(context['temp_directory'],'tmp_4977.sql'), 'w') # f_isql_cmd=open( os.path.join(context['temp_directory'],'tmp_4977.sql'), 'w')
# f_isql_cmd.write(sqltxt) # f_isql_cmd.write(sqltxt)
# flush_and_close( f_isql_cmd ) # flush_and_close( f_isql_cmd )
# #
# ms_before_detach=0 # ms_before_detach=0
# #
# f_isql_log = open( os.path.join(context['temp_directory'],'tmp_4977.log'), 'w') # f_isql_log = open( os.path.join(context['temp_directory'],'tmp_4977.log'), 'w')
# f_isql_err = open( os.path.join(context['temp_directory'],'tmp_4977.err'), 'w') # f_isql_err = open( os.path.join(context['temp_directory'],'tmp_4977.err'), 'w')
# #
# subprocess.call( [context['isql_path'], dsn, "-i", f_isql_cmd.name ], # subprocess.call( [context['isql_path'], dsn, "-i", f_isql_cmd.name ],
# stdout = f_isql_log, # stdout = f_isql_log,
# stderr = f_isql_err # stderr = f_isql_err
# ) # )
# flush_and_close( f_isql_log ) # flush_and_close( f_isql_log )
# flush_and_close( f_isql_err ) # flush_and_close( f_isql_err )
# #
# with open( f_isql_log.name,'r') as f: # with open( f_isql_log.name,'r') as f:
# for line in f: # for line in f:
# # ::: NB ::: do NOT remove "and line.split()[0].isdigit()" if decide to replace subprocess.call() # # ::: NB ::: do NOT remove "and line.split()[0].isdigit()" if decide to replace subprocess.call()
@ -121,28 +122,38 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# # String like: 'Database ....' does appear first in log instead of result! # # String like: 'Database ....' does appear first in log instead of result!
# if line.split() and line.split()[0].isdigit(): # if line.split() and line.split()[0].isdigit():
# ms_before_detach=int( line.split()[0] ) # ms_before_detach=int( line.split()[0] )
# #
# detach_during_ms = int( (time.time() - ms_before_detach - time.timezone) * 1000 ) # detach_during_ms = int( (time.time() - ms_before_detach - time.timezone) * 1000 )
# #
# if detach_during_ms < MAX_DETACH_TIME_THRESHOLD: # if detach_during_ms < MAX_DETACH_TIME_THRESHOLD:
# print('Detach performed fast enough: less than threshold.') # print('Detach performed fast enough: less than threshold.')
# else: # else:
# print('Detach lasted too long time: %s ms, MAX_DETACH_TIME_THRESHOLD is %s ms' % (detach_during_ms, MAX_DETACH_TIME_THRESHOLD) ) # print('Detach lasted too long time: %s ms, MAX_DETACH_TIME_THRESHOLD is %s ms' % (detach_during_ms, MAX_DETACH_TIME_THRESHOLD) )
# #
# # cleanup: # # cleanup:
# time.sleep(1) # time.sleep(1)
# cleanup((f_isql_log, f_isql_err, f_isql_cmd)) # cleanup((f_isql_log, f_isql_err, f_isql_cmd))
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
expected_stdout_1 = """ act_1 = python_act('db_1', substitutions=substitutions_1)
Detach performed fast enough: less than threshold.
"""
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action):
def test_1(db_1): MAX_DETACH_TIME_THRESHOLD=1200
pytest.fail("Test not IMPLEMENTED") act_1.script = """
set list on;
select datediff(second from timestamp '01.01.1970 00:00:00.000' to current_timestamp) as " "
from rdb$types rows 1;
"""
act_1.execute()
ms_before_detach = 0
for line in act_1.stdout.splitlines():
# ::: NB ::: do NOT remove "and line.split()[0].isdigit()" if decide to replace subprocess.call()
# with pipe-way like: runProgram('isql',[dsn,'-q','-o',sqllog.name], sqltxt) !!
# String like: 'Database ....' does appear first in log instead of result!
splitted = line.split()
if splitted and splitted[0].isdigit():
ms_before_detach = int(splitted[0])
detach_during_ms = int((time.time() - ms_before_detach - time.timezone) * 1000)
assert detach_during_ms < MAX_DETACH_TIME_THRESHOLD

View File

@ -2,36 +2,36 @@
# #
# id: bugs.core_4998 # id: bugs.core_4998
# title: Both client and server could not close connection after failed authentification # title: Both client and server could not close connection after failed authentification
# decription: # decription:
# Reproduced on 3.0.0.32136 RC1 with firebird.conf: # Reproduced on 3.0.0.32136 RC1 with firebird.conf:
# AuthServer = Legacy_Auth,Srp # AuthServer = Legacy_Auth,Srp
# AuthClient = Srp,Legacy_Auth # AuthClient = Srp,Legacy_Auth
# ::: NB-1 ::: # ::: NB-1 :::
# In order to get this environment for client test temp-ly CHANGES firebird.conf # In order to get this environment for client test temp-ly CHANGES firebird.conf
# Test will restore original firebird.conf in the end. # Test will restore original firebird.conf in the end.
# #
# ::: NB-2 ::: # ::: NB-2 :::
# We have to prepare auxiliary Python script to be executed in SEPARATE (NEW!) execution context, # We have to prepare auxiliary Python script to be executed in SEPARATE (NEW!) execution context,
# otherwise firebird.log is filled with messages "errno = 10054" only after this test completely finished. # otherwise firebird.log is filled with messages "errno = 10054" only after this test completely finished.
# See variable 'f_python_separate_exec_context' - it points to this temp .py file. # See variable 'f_python_separate_exec_context' - it points to this temp .py file.
# This aux Python script is called like this: # This aux Python script is called like this:
# os.system( f_python_separate_exec_context ) # os.system( f_python_separate_exec_context )
# #
# It contains three attempts to make connection with invalid passwords. # It contains three attempts to make connection with invalid passwords.
# Exceptions ('Your user/password not defined...') are suppressed, we need only make these attempts to check # Exceptions ('Your user/password not defined...') are suppressed, we need only make these attempts to check
# that no new records withh be added to firebird.log (as it is confirmed to be in 3.0.0.32136 RC1). # that no new records withh be added to firebird.log (as it is confirmed to be in 3.0.0.32136 RC1).
# #
# File firebird.log is compared BEFORE and AFTER os.system( f_python_separate_exec_context ). # File firebird.log is compared BEFORE and AFTER os.system( f_python_separate_exec_context ).
# No new messages related to 10054 error should occur during this test in firebird.log. # No new messages related to 10054 error should occur during this test in firebird.log.
# #
# 3.0.0.32366 RC2 - works OK. # 3.0.0.32366 RC2 - works OK.
# #
# Also checked on: # Also checked on:
# 30Cs, build 3.0.4.32972: OK, 6.172s. # 30Cs, build 3.0.4.32972: OK, 6.172s.
# 30SS, build 3.0.4.32972: OK, 4.375s. # 30SS, build 3.0.4.32972: OK, 4.375s.
# 40CS, build 4.0.0.955: OK, 7.281s. # 40CS, build 4.0.0.955: OK, 7.281s.
# 40SS, build 4.0.0.977: OK, 4.704s. # 40SS, build 4.0.0.977: OK, 4.704s.
# #
# tracker_id: CORE-4998 # tracker_id: CORE-4998
# min_versions: ['3.0.0'] # min_versions: ['3.0.0']
# versions: 3.0 # versions: 3.0
@ -60,35 +60,35 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# import re # import re
# import shutil # import shutil
# from fdb import services # from fdb import services
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# THIS_DSN = dsn # THIS_DSN = dsn
# DBAUSR = user_name # DBAUSR = user_name
# db_conn.close() # db_conn.close()
# #
# svc = services.connect(host='localhost') # svc = services.connect(host='localhost')
# fb_home=svc.get_home_directory() # fb_home=svc.get_home_directory()
# svc.close() # svc.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close(file_handle): # def flush_and_close(file_handle):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb'): # if file_handle.mode not in ('r', 'rb'):
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -99,16 +99,16 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# else: # else:
# print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.') # print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.')
# del_name = None # del_name = None
# #
# if del_name and os.path.isfile( del_name ): # if del_name and os.path.isfile( del_name ):
# os.remove( del_name ) # os.remove( del_name )
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def svc_get_fb_log( f_fb_log ): # def svc_get_fb_log( f_fb_log ):
# #
# global subprocess # global subprocess
# #
# subprocess.call( [ context['fbsvcmgr_path'], # subprocess.call( [ context['fbsvcmgr_path'],
# "localhost:service_mgr", # "localhost:service_mgr",
# "action_get_fb_log" # "action_get_fb_log"
@ -116,43 +116,43 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# stdout=f_fb_log, stderr=subprocess.STDOUT # stdout=f_fb_log, stderr=subprocess.STDOUT
# ) # )
# return # return
# #
# ########################################################################################### # ###########################################################################################
# #
# dts = datetime.datetime.now().strftime("%y%m%d_%H%M%S") # dts = datetime.datetime.now().strftime("%y%m%d_%H%M%S")
# #
# fbconf = os.path.join( fb_home, 'firebird.conf') # fbconf = os.path.join( fb_home, 'firebird.conf')
# fbcbak = os.path.join( fb_home, 'firebird_'+dts+'.bak') # fbcbak = os.path.join( fb_home, 'firebird_'+dts+'.bak')
# #
# shutil.copy2( fbconf, fbcbak ) # shutil.copy2( fbconf, fbcbak )
# #
# f_fbconf = open(fbconf,'r') # f_fbconf = open(fbconf,'r')
# fbconf_content=f_fbconf.readlines() # fbconf_content=f_fbconf.readlines()
# f_fbconf.close() # f_fbconf.close()
# #
# for i,s in enumerate( fbconf_content ): # for i,s in enumerate( fbconf_content ):
# if s.lower().lstrip().startswith( 'wirecrypt'.lower() ): # if s.lower().lstrip().startswith( 'wirecrypt'.lower() ):
# fbconf_content[i] = '# <temply commented> ' + s # fbconf_content[i] = '# <temply commented> ' + s
# if s.lower().lstrip().startswith( 'AuthClient'.lower() ): # if s.lower().lstrip().startswith( 'AuthClient'.lower() ):
# fbconf_content[i] = '# <temply commented> ' + s # fbconf_content[i] = '# <temply commented> ' + s
# #
# fbconf_content.append('\\n# Temporarily added by fbtest, CORE-4998. Should be removed auto:') # fbconf_content.append('\\n# Temporarily added by fbtest, CORE-4998. Should be removed auto:')
# fbconf_content.append("\\n#" + '='*30 ) # fbconf_content.append("\\n#" + '='*30 )
# fbconf_content.append('\\nAuthClient = Srp,Legacy_Auth') # fbconf_content.append('\\nAuthClient = Srp,Legacy_Auth')
# fbconf_content.append("\\n#" + '='*30 ) # fbconf_content.append("\\n#" + '='*30 )
# #
# f_fbconf=open(fbconf,'w') # f_fbconf=open(fbconf,'w')
# f_fbconf.writelines( fbconf_content ) # f_fbconf.writelines( fbconf_content )
# flush_and_close( f_fbconf ) # flush_and_close( f_fbconf )
# #
# ########################################################################################### # ###########################################################################################
# #
# f_fblog_before=open( os.path.join(context['temp_directory'],'tmp_4998_fblog_before.txt'), 'w') # f_fblog_before=open( os.path.join(context['temp_directory'],'tmp_4998_fblog_before.txt'), 'w')
# svc_get_fb_log( f_fblog_before ) # svc_get_fb_log( f_fblog_before )
# flush_and_close( f_fblog_before ) # flush_and_close( f_fblog_before )
# #
# other_exec_context_python_text = '''import fdb # other_exec_context_python_text = '''import fdb
# #
# for i in range(0,3): # for i in range(0,3):
# con1 = None # con1 = None
# try: # try:
@ -164,76 +164,76 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# con1.close() # con1.close()
# exit(0) # exit(0)
# ''' % locals() # ''' % locals()
# #
# f_python_separate_exec_context = os.path.join(context['temp_directory'], 'tmp_core_4998_try_connect_with_invalid_passwords.py') # f_python_separate_exec_context = os.path.join(context['temp_directory'], 'tmp_core_4998_try_connect_with_invalid_passwords.py')
# #
# f = open( f_python_separate_exec_context, 'w') # f = open( f_python_separate_exec_context, 'w')
# f.write( other_exec_context_python_text ) # f.write( other_exec_context_python_text )
# flush_and_close( f ) # flush_and_close( f )
# #
# ######################################################################################################## # ########################################################################################################
# ### l a u n c h P y t h o n i n a n o t h e r e x e c u t i o n c o n t e x t ### # ### l a u n c h P y t h o n i n a n o t h e r e x e c u t i o n c o n t e x t ###
# ######################################################################################################## # ########################################################################################################
# #
# # 17.06.2018. We have to add full path and name of interpretep (e.g. 'C:\\Python27\\python.exe') # # 17.06.2018. We have to add full path and name of interpretep (e.g. 'C:\\Python27\\python.exe')
# # because it can appear that OS will not be able to recognize how to handle .py files! # # because it can appear that OS will not be able to recognize how to handle .py files!
# # sys.executable - returns full path to Python exe, # # sys.executable - returns full path to Python exe,
# #
# os.system( sys.executable + ' ' + f_python_separate_exec_context ) # os.system( sys.executable + ' ' + f_python_separate_exec_context )
# #
# time.sleep(1) # time.sleep(1)
# #
# f_fblog_after=open( os.path.join(context['temp_directory'],'tmp_4998_fblog_after.txt'), 'w') # f_fblog_after=open( os.path.join(context['temp_directory'],'tmp_4998_fblog_after.txt'), 'w')
# svc_get_fb_log( f_fblog_after ) # svc_get_fb_log( f_fblog_after )
# flush_and_close( f_fblog_after ) # flush_and_close( f_fblog_after )
# #
# # RESTORE original config: # # RESTORE original config:
# ########################## # ##########################
# shutil.move( fbcbak, fbconf) # shutil.move( fbcbak, fbconf)
# #
# # Compare firebird.log versions BEFORE and AFTER this test: # # Compare firebird.log versions BEFORE and AFTER this test:
# ###################### # ######################
# #
# oldfb=open(f_fblog_before.name, 'r') # oldfb=open(f_fblog_before.name, 'r')
# newfb=open(f_fblog_after.name, 'r') # newfb=open(f_fblog_after.name, 'r')
# #
# difftext = ''.join(difflib.unified_diff( # difftext = ''.join(difflib.unified_diff(
# oldfb.readlines(), # oldfb.readlines(),
# newfb.readlines() # newfb.readlines()
# )) # ))
# oldfb.close() # oldfb.close()
# newfb.close() # newfb.close()
# #
# f_diff_txt=open( os.path.join(context['temp_directory'],'tmp_4998_diff.txt'), 'w') # f_diff_txt=open( os.path.join(context['temp_directory'],'tmp_4998_diff.txt'), 'w')
# f_diff_txt.write(difftext) # f_diff_txt.write(difftext)
# flush_and_close( f_diff_txt ) # flush_and_close( f_diff_txt )
# #
# # INET/inet_error: read errno = 10054 # # INET/inet_error: read errno = 10054
# #
# allowed_patterns = ( # allowed_patterns = (
# re.compile('\\.*inet_error\\:{0,1}\\s{0,}read\\s+errno\\s{0,}\\={0,}\\s{0,}10054\\.*', re.IGNORECASE), # re.compile('\\.*inet_error\\:{0,1}\\s{0,}read\\s+errno\\s{0,}\\={0,}\\s{0,}10054\\.*', re.IGNORECASE),
# ) # )
# #
# with open( f_diff_txt.name,'r') as f: # with open( f_diff_txt.name,'r') as f:
# for line in f: # for line in f:
# if line.startswith('+'): # if line.startswith('+'):
# match2some = filter( None, [ p.search(line) for p in allowed_patterns ] ) # match2some = filter( None, [ p.search(line) for p in allowed_patterns ] )
# if match2some: # if match2some:
# print( 'UNEXPECTED TEXT IN FIREBIRD.LOG: ' + (' '.join(line.split()).upper()) ) # print( 'UNEXPECTED TEXT IN FIREBIRD.LOG: ' + (' '.join(line.split()).upper()) )
# #
# ##################################################################### # #####################################################################
# # Cleanup: # # Cleanup:
# time.sleep(1) # time.sleep(1)
# cleanup( (f_diff_txt,f_fblog_before,f_fblog_after, f_python_separate_exec_context) ) # cleanup( (f_diff_txt,f_fblog_before,f_fblog_after, f_python_separate_exec_context) )
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1) #act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail
def test_1(db_1): def test_1(db_1):
pytest.fail("Test not IMPLEMENTED") pytest.skip("Test requires manipulation with firebird.conf")
#pytest.fail("Test not IMPLEMENTED")

View File

@ -2,15 +2,15 @@
# #
# id: bugs.core_5028 # id: bugs.core_5028
# title: Report the remote port number in MON$ATTACHMENTS # title: Report the remote port number in MON$ATTACHMENTS
# decription: # decription:
# #
# tracker_id: CORE-5028 # tracker_id: CORE-5028
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, python_act, Action
# version: 3.0 # version: 3.0
# resources: None # resources: None
@ -30,29 +30,43 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# select iif(port > 0, 'OK', 'BAD') as port_value # select iif(port > 0, 'OK', 'BAD') as port_value
# from ( # from (
# select cast(substring(mon$remote_address from 1 + position('/' in mon$remote_address)) as int) as port # select cast(substring(mon$remote_address from 1 + position('/' in mon$remote_address)) as int) as port
# from mon$attachments # from mon$attachments
# where mon$attachment_id = current_connection # where mon$attachment_id = current_connection
# ) # )
# ''' # '''
# #
# # On previous FB versions <sqlcmd> will raise exception: # # On previous FB versions <sqlcmd> will raise exception:
# # Statement failed, SQLSTATE = 22018 # # Statement failed, SQLSTATE = 22018
# # conversion error from string "192.168.43.154" # # conversion error from string "192.168.43.154"
# #
# cur.execute(sqlcmd) # cur.execute(sqlcmd)
# for r in cur: # for r in cur:
# print(r[0]) # print(r[0])
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
OK OK
""" """
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action, capsys):
def test_1(db_1): with act_1.db.connect() as con:
pytest.fail("Test not IMPLEMENTED") c = con.cursor()
cmd = """
select iif(port > 0, 'OK', 'BAD') as port_value
from (
select cast(substring(mon$remote_address from 1 + position('/' in mon$remote_address)) as int) as port
from mon$attachments
where mon$attachment_id = current_connection)
"""
for row in c.execute(cmd):
print(row[0])
# Check
act_1.expected_stdout = expected_stdout_1
act_1.stdout = capsys.readouterr().out
assert act_1.clean_stdout == act_1.clean_expected_stdout

View File

@ -2,9 +2,9 @@
# #
# id: bugs.core_5034 # id: bugs.core_5034
# title: At least 5 seconds delay on disconnect could happen if disconnect happens close after Event Manager initialization # title: At least 5 seconds delay on disconnect could happen if disconnect happens close after Event Manager initialization
# decription: # decription:
# This test uses Python multiprocessing package in order to spawn multiple processes with trivial job: attach/detach from DB. # This test uses Python multiprocessing package in order to spawn multiple processes with trivial job: attach/detach from DB.
# Number processes ('planned_attachments') and of iterations for each of them ('reconnect_count') depends on FB architecture # Number processes ('planned_attachments') and of iterations for each of them ('reconnect_count') depends on FB architecture
# which is obtained by call of SP sys_get_fb_arch. # which is obtained by call of SP sys_get_fb_arch.
# For ClassicServer weird effect was detected: when number of processes is more then ~34-35 some of Python processes may not # For ClassicServer weird effect was detected: when number of processes is more then ~34-35 some of Python processes may not
# finish (but CS processes are gone and thus database has no attachments). # finish (but CS processes are gone and thus database has no attachments).
@ -26,29 +26,32 @@
# In order to properly detect records that correspond to post-apply scripts (with INSERT statements) special integer field 'SEQ' # In order to properly detect records that correspond to post-apply scripts (with INSERT statements) special integer field 'SEQ'
# is used: it will have even values for event when we are 'just before detach' (i.e. "inside" database connection) and odd values # is used: it will have even values for event when we are 'just before detach' (i.e. "inside" database connection) and odd values
# when we are 'just after' (i.e. when connection has been just closed). The view 'v_top10_slow' is used to display connects which # when we are 'just after' (i.e. when connection has been just closed). The view 'v_top10_slow' is used to display connects which
# were closed too slow. # were closed too slow.
# Normally, this view should return ONE record with text 'Detaches were fast'. # Normally, this view should return ONE record with text 'Detaches were fast'.
# Otherwise it will return concrete values of time that was spent on detach process, in milliseconds. # Otherwise it will return concrete values of time that was spent on detach process, in milliseconds.
# ####################### # #######################
# ### A C H T U N G ### # ### A C H T U N G ###
# ####################### # #######################
# Following parameters: 'planned_attachments' and 'reconnect_count' affects on test result **VERY** strong, especially on Classic. # Following parameters: 'planned_attachments' and 'reconnect_count' affects on test result **VERY** strong, especially on Classic.
# You may need to change them if test results are unstable. Do NOT make value of 'planned_attachments' more than 33-34 on Classic! # You may need to change them if test results are unstable. Do NOT make value of 'planned_attachments' more than 33-34 on Classic!
# #
# Successfully checked on build 32276, SS/SC/CS. # Successfully checked on build 32276, SS/SC/CS.
# Confirmed error on WI-V3.0.0.32134, Cs: "MonitoringData: Cannot initialize the shared memory region / sh_mem_length_mapped is 0" # Confirmed error on WI-V3.0.0.32134, Cs: "MonitoringData: Cannot initialize the shared memory region / sh_mem_length_mapped is 0"
# #
# 23.11.2016 # 23.11.2016
# Increased threshold to 6000 ms instead of old 5000 ms -- see # Increased threshold to 6000 ms instead of old 5000 ms -- see
# http://web.firebirdsql.org/download/prerelease/results/archive/3.0.2.32630/bugs.core_5034.html # http://web.firebirdsql.org/download/prerelease/results/archive/3.0.2.32630/bugs.core_5034.html
# #
# [pcisar] 26.11.2021
# New implementation should be done using multiprocessing Python module
# Postponed for later due to complexity
# tracker_id: CORE-5034 # tracker_id: CORE-5034
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: # qmid:
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, python_act, Action
# version: 3.0 # version: 3.0
# resources: None # resources: None
@ -83,7 +86,7 @@ init_script_1 = """
declare v_fetches_beg bigint; declare v_fetches_beg bigint;
declare v_fetches_end bigint; declare v_fetches_end bigint;
begin begin
-- Aux SP for detect FB architecture. -- Aux SP for detect FB architecture.
select a.mon$server_pid, a.mon$remote_protocol select a.mon$server_pid, a.mon$remote_protocol
@ -94,11 +97,11 @@ init_script_1 = """
if ( att_protocol is null ) then if ( att_protocol is null ) then
fb_arch = 'Embedded'; fb_arch = 'Embedded';
else if ( upper(current_user) = upper('SYSDBA') else if ( upper(current_user) = upper('SYSDBA')
and rdb$get_context('SYSTEM','ENGINE_VERSION') NOT starting with '2.5' and rdb$get_context('SYSTEM','ENGINE_VERSION') NOT starting with '2.5'
and exists(select * from mon$attachments a and exists(select * from mon$attachments a
where a.mon$remote_protocol is null where a.mon$remote_protocol is null
and upper(a.mon$user) in ( upper('Cache Writer'), upper('Garbage Collector')) and upper(a.mon$user) in ( upper('Cache Writer'), upper('Garbage Collector'))
) )
) then ) then
fb_arch = 'SuperServer'; fb_arch = 'SuperServer';
else else
@ -112,7 +115,7 @@ init_script_1 = """
from mon$io_stats i from mon$io_stats i
where i.mon$stat_group = 0 -- db_level where i.mon$stat_group = 0 -- db_level
into v_fetches_beg; into v_fetches_beg;
execute statement v_test_sttm execute statement v_test_sttm
on external on external
'localhost:' || rdb$get_context('SYSTEM', 'DB_NAME') 'localhost:' || rdb$get_context('SYSTEM', 'DB_NAME')
@ -121,19 +124,19 @@ init_script_1 = """
password a_connect_with_pwd password a_connect_with_pwd
role left('R' || replace(uuid_to_char(gen_uuid()),'-',''),31) role left('R' || replace(uuid_to_char(gen_uuid()),'-',''),31)
into ext_server_pid; into ext_server_pid;
in autonomous transaction do in autonomous transaction do
select i.mon$page_fetches select i.mon$page_fetches
from mon$io_stats i from mon$io_stats i
where i.mon$stat_group = 0 -- db_level where i.mon$stat_group = 0 -- db_level
into v_fetches_end; into v_fetches_end;
fb_arch = iif( cur_server_pid is distinct from ext_server_pid, fb_arch = iif( cur_server_pid is distinct from ext_server_pid,
'Classic', 'Classic',
iif( v_fetches_beg is not distinct from v_fetches_end, iif( v_fetches_beg is not distinct from v_fetches_end,
'SuperClassic', 'SuperClassic',
'SuperServer' 'SuperServer'
) )
); );
end end
@ -141,7 +144,7 @@ init_script_1 = """
suspend; suspend;
end end
^ -- sys_get_fb_arch ^ -- sys_get_fb_arch
set term ;^ set term ;^
@ -168,9 +171,9 @@ init_script_1 = """
while ( i < n) do while ( i < n) do
begin begin
v_stt = 'drop role R_' || lpad(i, 3, '0'); v_stt = 'drop role R_' || lpad(i, 3, '0');
begin begin
execute statement v_stt; execute statement v_stt;
when any do begin end when any do begin end
end end
v_stt = 'create role R_' || lpad(i, 3, '0'); v_stt = 'create role R_' || lpad(i, 3, '0');
@ -187,12 +190,12 @@ init_script_1 = """
create or alter trigger trg_disc active on disconnect position 0 as create or alter trigger trg_disc active on disconnect position 0 as
begin begin
POST_EVENT 'FOO'; POST_EVENT 'FOO';
if ( current_user = 'TMP$C5034' and rdb$get_context('USER_SESSION','INITIAL_DDL') is null ) if ( current_user = 'TMP$C5034' and rdb$get_context('USER_SESSION','INITIAL_DDL') is null )
then then
in autonomous transaction do in autonomous transaction do
insert into log4detach default values; insert into log4detach default values;
end end
^ ^
@ -200,15 +203,15 @@ init_script_1 = """
commit; commit;
create or alter view v_top10_slow as create or alter view v_top10_slow as
select distinct msg select distinct msg
from from
( (
select iif( detach_ms > max_detach_ms, select iif( detach_ms > max_detach_ms,
'Slow detaches > '|| max_detach_ms ||' ms detected: ' || detach_ms || ' ms, from ' || dts_before_detach || ' to '||dts_after_detach, 'Slow detaches > '|| max_detach_ms ||' ms detected: ' || detach_ms || ' ms, from ' || dts_before_detach || ' to '||dts_after_detach,
'All detaches not exceeded threshold' 'All detaches not exceeded threshold'
) as msg ) as msg
from ( from (
select select
rno rno
,datediff(millisecond from min(t0) to min(t1)) as detach_ms ,datediff(millisecond from min(t0) to min(t1)) as detach_ms
,min(t0) as dts_before_detach ,min(t0) as dts_before_detach
@ -240,40 +243,38 @@ init_script_1 = """
rows 10 rows 10
); );
commit; commit;
""" """
db_1 = db_factory(sql_dialect=3, init=init_script_1) db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# import fdb # import fdb
# import time # import time
# import subprocess # import subprocess
# from multiprocessing import Process # from multiprocessing import Process
# from fdb import services # from fdb import services
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# # Get FB architecture: # # Get FB architecture:
# xcur=db_conn.cursor() # xcur=db_conn.cursor()
# xcur.execute("select fb_arch from sys_get_fb_arch;") # xcur.execute("select fb_arch from sys_get_fb_arch;")
# #
# for r in xcur: # for r in xcur:
# fb_arch = r[0].split()[0] # fb_arch = r[0].split()[0]
# #
# dbfile=db_conn.database_name # dbfile=db_conn.database_name
# db_conn.close() # db_conn.close()
# #
# ISQL_BINARY = context['isql_path'] # ISQL_BINARY = context['isql_path']
# svc = services.connect(host='localhost') # svc = services.connect(host='localhost')
# FB_HOME = os.path.normpath( svc.get_home_directory() ) # 'c: # FB_HOME = os.path.normpath( svc.get_home_directory() ) # 'c:\firebird\' --> 'c:\firebird' (i.e. remove trailing backslash if needed)
# irebird' --> 'c:
# irebird' (i.e. remove trailing backslash if needed)
# svc.close() # svc.close()
# #
# if os.name == 'nt': # if os.name == 'nt':
# # For Windows we assume that client library is always in FB_HOME dir: # # For Windows we assume that client library is always in FB_HOME dir:
# FB_CLNT=os.path.join(FB_HOME, 'fbclient.dll') # FB_CLNT=os.path.join(FB_HOME, 'fbclient.dll')
@ -281,26 +282,26 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# # For Linux client library will be searched in 'lib' subdirectory of FB_HOME: # # For Linux client library will be searched in 'lib' subdirectory of FB_HOME:
# # con=fdb.connect( dsn='localhost:employee', user='SYSDBA', password='masterkey', fb_library_name='/var/tmp/fb40tmp/lib/libfbclient.so') # # con=fdb.connect( dsn='localhost:employee', user='SYSDBA', password='masterkey', fb_library_name='/var/tmp/fb40tmp/lib/libfbclient.so')
# FB_CLNT=os.path.join(FB_HOME, 'lib', 'libfbclient.so' ) # FB_CLNT=os.path.join(FB_HOME, 'lib', 'libfbclient.so' )
# #
# FBT_TEMP_DIR = os.path.normpath(context['temp_directory']) # FBT_TEMP_DIR = os.path.normpath(context['temp_directory'])
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close(file_handle): # def flush_and_close(file_handle):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb'): # if file_handle.mode not in ('r', 'rb'):
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -311,12 +312,12 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# else: # else:
# print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.') # print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.')
# del_name = None # del_name = None
# #
# if del_name and os.path.isfile( del_name ): # if del_name and os.path.isfile( del_name ):
# os.remove( del_name ) # os.remove( del_name )
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# if fb_arch=='Classic': # if fb_arch=='Classic':
# # {20,20}=72"; {32,10}=52" - but can hang!; {30,8)=46"; {30,15}=93"; {33,10}=91"; {35,5}=no-return-from-python! # # {20,20}=72"; {32,10}=52" - but can hang!; {30,8)=46"; {30,15}=93"; {33,10}=91"; {35,5}=no-return-from-python!
# planned_attachments=30 # planned_attachments=30
@ -329,16 +330,16 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# # SS: {30,10}=35"; {50,20}=54"; {40,30}=57"; {75,30}=70"; {80,10}=42" # # SS: {30,10}=35"; {50,20}=54"; {40,30}=57"; {75,30}=70"; {80,10}=42"
# planned_attachments=80 # planned_attachments=80
# reconnect_count=10 # reconnect_count=10
# #
# #
# subprocess.check_output([ context['fbsvcmgr_path'], "localhost:service_mgr", # subprocess.check_output([ context['fbsvcmgr_path'], "localhost:service_mgr",
# "action_properties", # "action_properties",
# "prp_write_mode", "prp_wm_async", # "prp_write_mode", "prp_wm_async",
# "dbname", dbfile ], stderr=subprocess.STDOUT) # "dbname", dbfile ], stderr=subprocess.STDOUT)
# #
# # GENERATE CODE FOR EXECUTING IN SEPARATE EXECUTABLE PYTHON CONTEXT # # GENERATE CODE FOR EXECUTING IN SEPARATE EXECUTABLE PYTHON CONTEXT
# ################################################################### # ###################################################################
# #
# f_parallel_txt='''import os # f_parallel_txt='''import os
# import fdb # import fdb
# import time # import time
@ -347,120 +348,121 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# import subprocess # import subprocess
# from subprocess import Popen # from subprocess import Popen
# from multiprocessing import Process # from multiprocessing import Process
# #
# def attach_detach(a_dsn, a_fb_client, v_temp_dir, reconnect_count, process_seq): # def attach_detach(a_dsn, a_fb_client, v_temp_dir, reconnect_count, process_seq):
# f_detach_info_sql = open( os.path.join(v_temp_dir, 'tmp_5034_after_detach_' + str(process_seq).zfill(3) + '.log'), 'w') # f_detach_info_sql = open( os.path.join(v_temp_dir, 'tmp_5034_after_detach_' + str(process_seq).zfill(3) + '.log'), 'w')
# v_role = 'R_'+str(process_seq).zfill(3) # v_role = 'R_'+str(process_seq).zfill(3)
# v_sqllst = [] # v_sqllst = []
# #
# for i in range(0,reconnect_count): # for i in range(0,reconnect_count):
# v_seq = 100000 + (1+process_seq) * 1000 + 2*i # v_seq = 100000 + (1+process_seq) * 1000 + 2*i
# #
# att = fdb.connect( dsn = a_dsn, fb_library_name = a_fb_client, user='TMP$C5034', password='123',role = v_role ) # att = fdb.connect( dsn = a_dsn, fb_library_name = a_fb_client, user='TMP$C5034', password='123',role = v_role )
# #
# # Trigger 'trg_disc' will add row to LOG4DETACH table. # # Trigger 'trg_disc' will add row to LOG4DETACH table.
# # Column RNO will have value = 'R_nnnn' (for worker #0 --> 'R_000', #1 --> 'R_001' etc), # # Column RNO will have value = 'R_nnnn' (for worker #0 --> 'R_000', #1 --> 'R_001' etc),
# # i.e. it will be NOT null for the timestamp when we are DISCONNECTED to database: # # i.e. it will be NOT null for the timestamp when we are DISCONNECTED to database:
# att.close() # att.close()
# #
# v_seq = v_seq + 1 # v_seq = v_seq + 1
# #
# # Catch current timestamp (we just retuirned from DB connect) and store it in the list: # # Catch current timestamp (we just retuirned from DB connect) and store it in the list:
# v_sqllst.append( "insert into log4detach(dts, rno, seq) values( '%%s', '%%s', %%s ); " %% ( datetime.strftime(datetime.now(), '%%Y-%%m-%%d %%H:%%M:%%S.%%f')[:23], v_role, v_seq ) ) # v_sqllst.append( "insert into log4detach(dts, rno, seq) values( '%%s', '%%s', %%s ); " %% ( datetime.strftime(datetime.now(), '%%Y-%%m-%%d %%H:%%M:%%S.%%f')[:23], v_role, v_seq ) )
# #
# # Current worker COMPLETED <reconnect_count> iterations of connect/disconnect, # # Current worker COMPLETED <reconnect_count> iterations of connect/disconnect,
# # now we can save timestamps that was stores in the list just after each detach # # now we can save timestamps that was stores in the list just after each detach
# # to the text file for further executing it by ISQL: # # to the text file for further executing it by ISQL:
# f_detach_info_sql.write("\\\\n".join(v_sqllst)) # f_detach_info_sql.write("\\\\n".join(v_sqllst))
# f_detach_info_sql.write( '\\\\n' ) # f_detach_info_sql.write( '\\\\n' )
# #
# merge_sql="merge into log4detach t using ( select id, %%s + 2 * (row_number()over(order by id)-1) seq from log4detach d where rno='%%s' and seq is null ) s on (s.id = t.id) when matched then update set t.seq = s.seq;" %% ( 100000 + (1+process_seq) * 1000, v_role ) # merge_sql="merge into log4detach t using ( select id, %%s + 2 * (row_number()over(order by id)-1) seq from log4detach d where rno='%%s' and seq is null ) s on (s.id = t.id) when matched then update set t.seq = s.seq;" %% ( 100000 + (1+process_seq) * 1000, v_role )
# #
# f_detach_info_sql.write( ' '.join(merge_sql.split()).lstrip() ) # f_detach_info_sql.write( ' '.join(merge_sql.split()).lstrip() )
# f_detach_info_sql.write( '\\\\n' ) # f_detach_info_sql.write( '\\\\n' )
# f_detach_info_sql.write( 'commit;' ) # f_detach_info_sql.write( 'commit;' )
# f_detach_info_sql.close() # f_detach_info_sql.close()
# #
# planned_attachments = %(planned_attachments)s # planned_attachments = %(planned_attachments)s
# #
# if __name__ == '__main__': # if __name__ == '__main__':
# p_list=[] # p_list=[]
# v_fb_home = r'%(FB_HOME)s' # v_fb_home = r'%(FB_HOME)s'
# v_temp_dir = r'%(FBT_TEMP_DIR)s' # v_temp_dir = r'%(FBT_TEMP_DIR)s'
# v_dsn = r'%(dsn)s' # v_dsn = r'%(dsn)s'
# v_fb_client = r'%(FB_CLNT)s' # v_fb_client = r'%(FB_CLNT)s'
# #
# for i in range(0, planned_attachments): # for i in range(0, planned_attachments):
# # Python multi-processing feature: # # Python multi-processing feature:
# ################################## # ##################################
# p_i = Process(target=attach_detach, args=( v_dsn, v_fb_client, v_temp_dir, %(reconnect_count)s, i, )) # p_i = Process(target=attach_detach, args=( v_dsn, v_fb_client, v_temp_dir, %(reconnect_count)s, i, ))
# p_i.start() # p_i.start()
# p_list.append(p_i) # p_list.append(p_i)
# #
# for i in range(len(p_list)): # for i in range(len(p_list)):
# p_list[i].join() # p_list[i].join()
# #
# # All completed # # All completed
# #
# f_detach_info_sql = open( os.path.join(v_temp_dir, 'tmp_5034_after_detach_all.sql'), 'w') # f_detach_info_sql = open( os.path.join(v_temp_dir, 'tmp_5034_after_detach_all.sql'), 'w')
# for i in range(len(p_list)): # for i in range(len(p_list)):
# f_detach_log_i = os.path.join(v_temp_dir, 'tmp_5034_after_detach_' + str(i).zfill(3) + '.log') # f_detach_log_i = os.path.join(v_temp_dir, 'tmp_5034_after_detach_' + str(i).zfill(3) + '.log')
# with open(f_detach_log_i, 'r') as s: # with open(f_detach_log_i, 'r') as s:
# f_detach_info_sql.write(s.read()+'\\\\n\\\\n\\\\n') # f_detach_info_sql.write(s.read()+'\\\\n\\\\n\\\\n')
# os.remove(f_detach_log_i) # os.remove(f_detach_log_i)
# #
# f_detach_info_sql.flush() # f_detach_info_sql.flush()
# os.fsync(f_detach_info_sql.fileno()) # os.fsync(f_detach_info_sql.fileno())
# f_detach_info_sql.close() # f_detach_info_sql.close()
# #
# # subprocess.call( [ os.path.join( v_fb_home, 'isql'), v_dsn, '-user', '%(user_name)s', '-password', '%(user_password)s', '-nod', '-n', '-i', f_detach_info_sql.name] ) # # subprocess.call( [ os.path.join( v_fb_home, 'isql'), v_dsn, '-user', '%(user_name)s', '-password', '%(user_password)s', '-nod', '-n', '-i', f_detach_info_sql.name] )
# subprocess.call( [ r'%(ISQL_BINARY)s', v_dsn, '-user', '%(user_name)s', '-password', '%(user_password)s', '-nod', '-n', '-i', f_detach_info_sql.name] ) # subprocess.call( [ r'%(ISQL_BINARY)s', v_dsn, '-user', '%(user_name)s', '-password', '%(user_password)s', '-nod', '-n', '-i', f_detach_info_sql.name] )
# #
# os.remove(f_detach_info_sql.name) # os.remove(f_detach_info_sql.name)
# ''' % dict(globals(), **locals()) # ''' % dict(globals(), **locals())
# # (context['temp_directory'].replace('\\\\','\\\\\\\\'), planned_attachments, dsn, reconnect_count, dsn) # # (context['temp_directory'].replace('\\\\','\\\\\\\\'), planned_attachments, dsn, reconnect_count, dsn)
# #
# f_parallel_py=open( os.path.join(context['temp_directory'],'tmp_5034_after_detach.py'), 'w') # f_parallel_py=open( os.path.join(context['temp_directory'],'tmp_5034_after_detach.py'), 'w')
# f_parallel_py.write(f_parallel_txt) # f_parallel_py.write(f_parallel_txt)
# flush_and_close( f_parallel_py ) # flush_and_close( f_parallel_py )
# #
# ######################################################################################################## # ########################################################################################################
# ### l a u n c h P y t h o n i n a n o t h e r e x e c u t i o n c o n t e x t ### # ### l a u n c h P y t h o n i n a n o t h e r e x e c u t i o n c o n t e x t ###
# ######################################################################################################## # ########################################################################################################
# runProgram( sys.executable, [f_parallel_py.name] ) # runProgram( sys.executable, [f_parallel_py.name] )
# #
# time.sleep(2) # time.sleep(2)
# #
# f_top_slow_sql=open( os.path.join(context['temp_directory'], 'tmp_5034_slowest_detaches.sql'), 'w') # f_top_slow_sql=open( os.path.join(context['temp_directory'], 'tmp_5034_slowest_detaches.sql'), 'w')
# f_top_slow_sql.write('drop user tmp$c5034; commit; set count on; set heading off; select * from v_top10_slow; commit;') # f_top_slow_sql.write('drop user tmp$c5034; commit; set count on; set heading off; select * from v_top10_slow; commit;')
# flush_and_close( f_top_slow_sql ) # flush_and_close( f_top_slow_sql )
# #
# f_top_slow_log=open( os.path.join(context['temp_directory'], 'tmp_5034_slowest_detaches.log'), 'w') # f_top_slow_log=open( os.path.join(context['temp_directory'], 'tmp_5034_slowest_detaches.log'), 'w')
# subprocess.call( [ context['isql_path'], dsn, "-nod", "-n", "-i", f_top_slow_sql.name], stdout=f_top_slow_log, stderr=subprocess.STDOUT) # subprocess.call( [ context['isql_path'], dsn, "-nod", "-n", "-i", f_top_slow_sql.name], stdout=f_top_slow_log, stderr=subprocess.STDOUT)
# flush_and_close( f_top_slow_log ) # flush_and_close( f_top_slow_log )
# #
# time.sleep(1) # time.sleep(1)
# #
# with open(f_top_slow_log.name) as f: # with open(f_top_slow_log.name) as f:
# print(f.read()) # print(f.read())
# #
# # Cleanup. # # Cleanup.
# ######### # #########
# time.sleep(1) # time.sleep(1)
# cleanup( (f_top_slow_sql,f_top_slow_log,f_parallel_py,f_parallel_py.name+'c') ) # cleanup( (f_top_slow_sql,f_top_slow_log,f_parallel_py,f_parallel_py.name+'c') )
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
All detaches not exceeded threshold All detaches not exceeded threshold
Records affected: 1 Records affected: 1
""" """
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail @pytest.mark.xfail
def test_1(db_1): def test_1(act_1: Action):
pytest.fail("Test not IMPLEMENTED") pytest.fail("Test not IMPLEMENTED")

View File

@ -2,20 +2,22 @@
# #
# id: bugs.core_5039 # id: bugs.core_5039
# title: Connecting to service with invalid servicename yields incorrect error message # title: Connecting to service with invalid servicename yields incorrect error message
# decription: # decription:
# 28.01.2019. # 28.01.2019.
# Name of service manager is ignored in FB 4.0, see http://tracker.firebirdsql.org/browse/CORE-5883 # Name of service manager is ignored in FB 4.0, see http://tracker.firebirdsql.org/browse/CORE-5883
# ("service_mgr" to be cleaned out from connection string completely...") # ("service_mgr" to be cleaned out from connection string completely...")
# Disabled this test to be run on FB 4.0: added record to '%FBT_REPO% ests\\qa4x-exclude-list.txt'. # Disabled this test to be run on FB 4.0: added record to '%FBT_REPO% ests\\qa4x-exclude-list.txt'.
# Added EMPTY section for FB version 4.0 in this .fbt as one more way to protect from running. # Added EMPTY section for FB version 4.0 in this .fbt as one more way to protect from running.
# # [pcisar] 26.112021
# "Empty" 4.0 version was removed completelly, as it's not needed with pytest
#
# tracker_id: CORE-5039 # tracker_id: CORE-5039
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0, 4.0 # versions: 3.0, 4.0
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, python_act, Action
# version: 3.0 # version: 3.0
# resources: None # resources: None
@ -28,45 +30,23 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# db_conn.close() # db_conn.close()
# runProgram('fbsvcmgr',['localhost:qwe_mnb_zxc_9','user','SYSDBA','password','masterkey','info_server_version']) # runProgram('fbsvcmgr',['localhost:qwe_mnb_zxc_9','user','SYSDBA','password','masterkey','info_server_version'])
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stderr_1 = """ expected_stderr_1 = """
Cannot attach to services manager Cannot attach to services manager
-service qwe_mnb_zxc_9 is not defined -service qwe_mnb_zxc_9 is not defined
""" """
@pytest.mark.version('>=3.0')
@pytest.mark.xfail
def test_1(db_1):
pytest.fail("Test not IMPLEMENTED")
# version: 4.0
# resources: None
substitutions_2 = []
init_script_2 = """"""
db_2 = db_factory(sql_dialect=3, init=init_script_2)
test_script_2 = """
-- This section was intentionally left empty.
-- No message should be in expected_* sections.
-- It is STRONGLY RECOMMENDED to add this ticket
-- in the 'excluded-list file:
-- %FBT_REPO% ests\\qa4x-exclude-list.txt
"""
act_2 = isql_act('db_2', test_script_2, substitutions=substitutions_2)
@pytest.mark.version('>=4.0')
def test_2(act_2: Action):
act_2.execute()
@pytest.mark.version('>=3.0,<4')
def test_1(act_1: Action):
act_1.expected_stderr = expected_stderr_1
act_1.svcmgr(switches=['localhost:qwe_mnb_zxc_9', 'user', 'SYSDBA',
'password', 'masterkey', 'info_server_version'],
connect_mngr=False)
assert act_1.clean_stderr == act_1.clean_expected_stderr

View File

@ -2,18 +2,18 @@
# #
# id: bugs.core_5061 # id: bugs.core_5061
# title: ISQL plan output is unexpectedly truncated after a query is simplified to become shorter # title: ISQL plan output is unexpectedly truncated after a query is simplified to become shorter
# decription: # decription:
# Start of discussion: letter to dimitr, 30-dec-2015 13:57; its subject refers to core-4708. # Start of discussion: letter to dimitr, 30-dec-2015 13:57; its subject refers to core-4708.
# It was found that explained plan produced by ISQL is unexpectedly ends on WI-V3.0.0.32256. # It was found that explained plan produced by ISQL is unexpectedly ends on WI-V3.0.0.32256.
# This testuses that query, but instead of verifying plan text itself (which can be changed in the future) # This testuses that query, but instead of verifying plan text itself (which can be changed in the future)
# it is sufficient to check only that plan does NOT contain lines with ellipsis or 'truncated' or 'error'. # it is sufficient to check only that plan does NOT contain lines with ellipsis or 'truncated' or 'error'.
# This mean that 'expected_stdout' section must be EMPTY. Otherwise expected_stdout will contain info # This mean that 'expected_stdout' section must be EMPTY. Otherwise expected_stdout will contain info
# about error or invalid plan. # about error or invalid plan.
# #
# tracker_id: CORE-5061 # tracker_id: CORE-5061
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: # qmid:
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, isql_act, Action
@ -29,11 +29,11 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# import subprocess # import subprocess
# import time # import time
# #
# sql_text=''' set list on; # sql_text=''' set list on;
# set explain on; # set explain on;
# set planonly; # set planonly;
@ -45,54 +45,54 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# select r.i+1 from r1 r where r.i < 2 # select r.i+1 from r1 r where r.i < 2
# ) # )
# --select count(*) from r1; # --select count(*) from r1;
# #
# ,r2 as ( # ,r2 as (
# select first 1 row_number() over() i # select first 1 row_number() over() i
# from r1 ra # from r1 ra
# full join r1 rb on rb.i=ra.i # full join r1 rb on rb.i=ra.i
# group by ra.i # group by ra.i
# having count(*)>0 # having count(*)>0
# #
# union all # union all
# #
# select rx.i+1 from r2 rx # select rx.i+1 from r2 rx
# where rx.i+1 <= 2 # where rx.i+1 <= 2
# ) # )
# --select count(*) from r2 # --select count(*) from r2
# ,r3 as ( # ,r3 as (
# select first 1 row_number() over() i # select first 1 row_number() over() i
# from r2 ra # from r2 ra
# full join r2 rb on rb.i=ra.i # full join r2 rb on rb.i=ra.i
# group by ra.i # group by ra.i
# having count(*)>0 # having count(*)>0
# #
# union all # union all
# #
# select rx.i+1 from r3 rx # select rx.i+1 from r3 rx
# where rx.i+1 <= 2 # where rx.i+1 <= 2
# ) # )
# --select count(*) from r3 # --select count(*) from r3
# ,r4 as ( # ,r4 as (
# select first 1 row_number() over() i # select first 1 row_number() over() i
# from r3 ra # from r3 ra
# full join r3 rb on rb.i=ra.i # full join r3 rb on rb.i=ra.i
# group by ra.i # group by ra.i
# having count(*)>0 # having count(*)>0
# #
# union all # union all
# #
# select rx.i+1 from r4 rx # select rx.i+1 from r4 rx
# where rx.i+1 <= 2 # where rx.i+1 <= 2
# ) # )
# ,rn as ( # ,rn as (
# select row_number() over() i # select row_number() over() i
# from rdb$database r full join rdb$database r2 on r2.rdb$relation_id=r.rdb$relation_id # from rdb$database r full join rdb$database r2 on r2.rdb$relation_id=r.rdb$relation_id
# group by r.rdb$relation_id # group by r.rdb$relation_id
# having count(*)>0 # having count(*)>0
# order by r.rdb$relation_id # order by r.rdb$relation_id
# rows 1 to 1 # rows 1 to 1
# ) # )
# select # select
# char_length(mon$explained_plan) # char_length(mon$explained_plan)
# ,(select count(*) from r4) # ,(select count(*) from r4)
# ,(select count(*) from rn) # ,(select count(*) from rn)
@ -100,20 +100,20 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# from mon$statements # from mon$statements
# ; # ;
# ''' # '''
# #
# sqltxt=open( os.path.join(context['temp_directory'],'tmp_sql_5061.sql'), 'w') # sqltxt=open( os.path.join(context['temp_directory'],'tmp_sql_5061.sql'), 'w')
# sqltxt.write(sql_text) # sqltxt.write(sql_text)
# sqltxt.close() # sqltxt.close()
# #
# sqllog=open( os.path.join(context['temp_directory'],'tmp_sql_5061.log'), 'w') # sqllog=open( os.path.join(context['temp_directory'],'tmp_sql_5061.log'), 'w')
# subprocess.call( [ context['isql_path'], dsn,'-user',user_name,'-pas',user_password,'-q', '-i', sqltxt.name], # subprocess.call( [ context['isql_path'], dsn,'-user',user_name,'-pas',user_password,'-q', '-i', sqltxt.name],
# stdout=sqllog, # stdout=sqllog,
# stderr=subprocess.STDOUT # stderr=subprocess.STDOUT
# ) # )
# sqllog.close() # sqllog.close()
# #
# # Check content of files: 1st shuld contain name of temply created user, 2nd should be with error during get FB log: # # Check content of files: 1st shuld contain name of temply created user, 2nd should be with error during get FB log:
# #
# i=0 # i=0
# with open( sqllog.name,'r') as f: # with open( sqllog.name,'r') as f:
# for line in f: # for line in f:
@ -121,23 +121,94 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# if '...' in line or 'truncated' in line or 'error' in line: # if '...' in line or 'truncated' in line or 'error' in line:
# print("Plan is truncated or empty. Found at line "+str(i)) # print("Plan is truncated or empty. Found at line "+str(i))
# break # break
# #
# # Do not remove this pause: on Windows closing of handles can take some (small) time. # # Do not remove this pause: on Windows closing of handles can take some (small) time.
# # Otherwise Windows(32) access error can raise here. # # Otherwise Windows(32) access error can raise here.
# time.sleep(1) # time.sleep(1)
# #
# if os.path.isfile(sqltxt.name): # if os.path.isfile(sqltxt.name):
# os.remove(sqltxt.name) # os.remove(sqltxt.name)
# if os.path.isfile(sqllog.name): # if os.path.isfile(sqllog.name):
# os.remove(sqllog.name) # os.remove(sqllog.name)
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
test_script_1 = """
set list on;
set explain on;
set planonly;
set blob all;
with recursive
r1 as (
select 1 as i from rdb$database
union all
select r.i+1 from r1 r where r.i < 2
)
--select count(*) from r1;
,r2 as (
select first 1 row_number() over() i
from r1 ra
full join r1 rb on rb.i=ra.i
group by ra.i
having count(*)>0
union all
select rx.i+1 from r2 rx
where rx.i+1 <= 2
)
--select count(*) from r2
,r3 as (
select first 1 row_number() over() i
from r2 ra
full join r2 rb on rb.i=ra.i
group by ra.i
having count(*)>0
union all
select rx.i+1 from r3 rx
where rx.i+1 <= 2
)
--select count(*) from r3
,r4 as (
select first 1 row_number() over() i
from r3 ra
full join r3 rb on rb.i=ra.i
group by ra.i
having count(*)>0
union all
select rx.i+1 from r4 rx
where rx.i+1 <= 2
)
,rn as (
select row_number() over() i
from rdb$database r full join rdb$database r2 on r2.rdb$relation_id=r.rdb$relation_id
group by r.rdb$relation_id
having count(*)>0
order by r.rdb$relation_id
rows 1 to 1
)
select
char_length(mon$explained_plan)
,(select count(*) from r4)
,(select count(*) from rn)
--,(select count(*) from rn)
from mon$statements
;
"""
act_1 = isql_act('db_1', test_script_1, substitutions=substitutions_1)
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action):
def test_1(db_1): act_1.execute()
pytest.fail("Test not IMPLEMENTED") i = 0
for line in act_1.stdout.splitlines():
i += 1
if '...' in line or 'truncated' in line or 'error' in line:
pytest.fail(f"Plan is truncated or empty. Found at line {i}")

View File

@ -2,36 +2,46 @@
# #
# id: bugs.core_5062 # id: bugs.core_5062
# title: CHAR_TO_UUID on column with index throws expression evaluation not supported Human readable UUID argument for CHAR_TO_UUID must be of exact length 36 # title: CHAR_TO_UUID on column with index throws expression evaluation not supported Human readable UUID argument for CHAR_TO_UUID must be of exact length 36
# decription: # decription:
# tracker_id: CORE-5062 # tracker_id: CORE-5062
# min_versions: ['2.5.6'] # min_versions: ['2.5.6']
# versions: 2.5.6 # versions: 2.5.6
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, python_act, Action
# version: 2.5.6 # version: 2.5.6
# resources: None # resources: None
substitutions_1 = [] substitutions_1 = []
init_script_1 = """""" init_script_1 = """
recreate table test_uuid(
datavalue int,
uuid char(16) character set octets,
constraint test_uuid_unq unique(uuid)
);
commit;
insert into test_uuid(datavalue, uuid) values( 1, char_to_uuid('57F2B8C7-E1D8-4B61-9086-C66D1794F2D9') );
--insert into test_uuid(datavalue, uuid) values( 2, char_to_uuid('37F2B8C3-E1D8-4B31-9083-C33D1794F2D3') );
commit;
"""
db_1 = db_factory(sql_dialect=3, init=init_script_1) db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# # ::: NB ::: Could not reproduce ticket issue on x86. # # ::: NB ::: Could not reproduce ticket issue on x86.
# # Checked on: WI-V2.5.4.26856, WI-V3.0.0.31948 (Python = 2.7 x86, fdb = 1.5). # # Checked on: WI-V2.5.4.26856, WI-V3.0.0.31948 (Python = 2.7 x86, fdb = 1.5).
# #
# import fdb # import fdb
# db_conn.close() # db_conn.close()
# #
# sql_ddl='''recreate table test_uuid( # sql_ddl='''recreate table test_uuid(
# datavalue int, # datavalue int,
# uuid char(16) character set octets, # uuid char(16) character set octets,
# constraint test_uuid_unq unique(uuid) # constraint test_uuid_unq unique(uuid)
# ); # );
# commit; # commit;
@ -39,32 +49,37 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# --insert into test_uuid(datavalue, uuid) values( 2, char_to_uuid('37F2B8C3-E1D8-4B31-9083-C33D1794F2D3') ); # --insert into test_uuid(datavalue, uuid) values( 2, char_to_uuid('37F2B8C3-E1D8-4B31-9083-C33D1794F2D3') );
# commit; # commit;
# ''' # '''
# #
# runProgram('isql',['-user',user_name, '-pas',user_password, dsn],sql_ddl) # runProgram('isql',['-user',user_name, '-pas',user_password, dsn],sql_ddl)
# #
# con2 = fdb.connect(dsn=dsn, user=user_name, password=user_password, charset='utf8') # con2 = fdb.connect(dsn=dsn, user=user_name, password=user_password, charset='utf8')
# #
# xcur2 = con2.cursor() # xcur2 = con2.cursor()
# psSel = xcur2.prep("select datavalue from test_uuid where uuid = char_to_uuid(?)") # psSel = xcur2.stmt("select datavalue from test_uuid where uuid = char_to_uuid(?)")
# #
# print ( psSel.plan ) # print ( psSel.plan )
# xcur2.execute(psSel, [('57F2B8C7-E1D8-4B61-9086-C66D1794F2D9')]) # xcur2.execute(psSel, [('57F2B8C7-E1D8-4B61-9086-C66D1794F2D9')])
# for row in xcur2: # for row in xcur2:
# print( row[0] ) # print( row[0] )
# #
# con2.close() # con2.close()
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
expected_stdout_1 = """ act_1 = python_act('db_1', substitutions=substitutions_1)
PLAN (TEST_UUID INDEX (TEST_UUID_UNQ))
1
"""
@pytest.mark.version('>=2.5.6') # Not needed for new implementation
@pytest.mark.xfail #expected_stdout_1 = """
def test_1(db_1): #PLAN (TEST_UUID INDEX (TEST_UUID_UNQ))
pytest.fail("Test not IMPLEMENTED") #1
#"""
@pytest.mark.version('>=3.0')
def test_1(act_1: Action):
with act_1.db.connect() as con:
c = con.cursor()
stmt = c.prepare("select datavalue from test_uuid where uuid = char_to_uuid(?)")
assert stmt.plan == 'PLAN (TEST_UUID INDEX (TEST_UUID_UNQ))'
result = c.execute(stmt, ['57F2B8C7-E1D8-4B61-9086-C66D1794F2D9']).fetchall()
assert result == [(1, )]

View File

@ -1,47 +0,0 @@
#coding:utf-8
#
# id: bugs.core_5068
# title: gbak with invalid parameter crashes FB
# decription:
# Confirmed crash on 2.5.5.26952, but only when use 'gbak' utility (with services call).
# As of fbsvcmgr, it works correct and reports error: Unknown switch "res_user_all_space".
# Output when use gbak is:
# gbak:unknown switch "USER_ALL_SPACE"
# gbak: ERROR:Unable to complete network request to host "localhost".
# gbak: ERROR: Error reading data from the connection.
# gbak:Exiting before completion due to errors
#
# Checked on WI-V2.5.6.26962 - works OK.
# No test needed for 3.0 thus only stub code present here in 'firebird_version': '3.0' section.
#
# tracker_id: CORE-5068
# min_versions: ['2.5.5']
# versions: 3.0
# qmid:
import pytest
from firebird.qa import db_factory, isql_act, Action
# version: 3.0
# resources: None
substitutions_1 = []
init_script_1 = """"""
db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1
#---
#
#
#---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
@pytest.mark.version('>=3.0')
@pytest.mark.xfail
def test_1(db_1):
pytest.fail("Test not IMPLEMENTED")

View File

@ -2,7 +2,7 @@
# #
# id: bugs.core_5075 # id: bugs.core_5075
# title: Regression. Triger on DISCONNECT with dynamic SQL (ES 'insert into ...'): 1) does not work in 3.0; 2) leads FB to crash when it is recreated # title: Regression. Triger on DISCONNECT with dynamic SQL (ES 'insert into ...'): 1) does not work in 3.0; 2) leads FB to crash when it is recreated
# decription: # decription:
# Test does following: # Test does following:
# * obtains firebird.log as it was _before_ actions; # * obtains firebird.log as it was _before_ actions;
# * stores initial script for creation DB objects in file <f_sql_init> for futher applying it twice (see ticket); # * stores initial script for creation DB objects in file <f_sql_init> for futher applying it twice (see ticket);
@ -12,17 +12,18 @@
# * print variable 'sqlres'; # * print variable 'sqlres';
# * obtains firebird.log as it is _after_ actions; # * obtains firebird.log as it is _after_ actions;
# * compare two firebird.log versions - diff must be empty. # * compare two firebird.log versions - diff must be empty.
# #
# Checked on 3.0.0.32281, SS/SC/CS. # Checked on 3.0.0.32281, SS/SC/CS.
# #
# #
# tracker_id: CORE-5075 # tracker_id: CORE-5075
# min_versions: ['3.0'] # min_versions: ['3.0']
# versions: 3.0 # versions: 3.0
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from difflib import unified_diff
from firebird.qa import db_factory, python_act, Action
# version: 3.0 # version: 3.0
# resources: None # resources: None
@ -40,28 +41,28 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# from subprocess import Popen, PIPE, STDOUT # from subprocess import Popen, PIPE, STDOUT
# import time # import time
# import difflib # import difflib
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# db_conn.close() # db_conn.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close(file_handle): # def flush_and_close(file_handle):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb'): # if file_handle.mode not in ('r', 'rb'):
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -72,17 +73,17 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# else: # else:
# print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.') # print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.')
# del_name = None # del_name = None
# #
# if del_name and os.path.isfile( del_name ): # if del_name and os.path.isfile( del_name ):
# os.remove( del_name ) # os.remove( del_name )
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# #
# def svc_get_fb_log( f_fb_log ): # def svc_get_fb_log( f_fb_log ):
# #
# import subprocess # import subprocess
# #
# subprocess.call([ context['fbsvcmgr_path'], # subprocess.call([ context['fbsvcmgr_path'],
# "localhost:service_mgr", # "localhost:service_mgr",
# "action_get_fb_log" # "action_get_fb_log"
@ -90,48 +91,48 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# stdout=f_fb_log, stderr=subprocess.STDOUT # stdout=f_fb_log, stderr=subprocess.STDOUT
# ) # )
# return # return
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# f_fblog_before=open( os.path.join(context['temp_directory'],'tmp_5075_fblog_before.txt'), 'w') # f_fblog_before=open( os.path.join(context['temp_directory'],'tmp_5075_fblog_before.txt'), 'w')
# svc_get_fb_log( f_fblog_before ) # svc_get_fb_log( f_fblog_before )
# flush_and_close( f_fblog_before ) # flush_and_close( f_fblog_before )
# #
# sqltxt='''set term ^; # sqltxt='''set term ^;
# create or alter trigger trg_connect active on connect position 0 as # create or alter trigger trg_connect active on connect position 0 as
# begin # begin
# end # end
# ^ # ^
# #
# create or alter trigger trg_disc active on disconnect position 0 as # create or alter trigger trg_disc active on disconnect position 0 as
# begin # begin
# end # end
# ^ # ^
# set term ;^ # set term ;^
# commit; # commit;
# #
# recreate sequence g; # recreate sequence g;
# recreate table log( # recreate table log(
# event_id int generated by default as identity constraint pk_log primary key, # event_id int generated by default as identity constraint pk_log primary key,
# event_name varchar(20), # event_name varchar(20),
# when_it_was timestamp default 'now' # when_it_was timestamp default 'now'
# ); # );
# commit; # commit;
# #
# set term ^; # set term ^;
# execute block as # execute block as
# begin # begin
# rdb$set_context('USER_SESSION','INITIAL_DDL','1'); # rdb$set_context('USER_SESSION','INITIAL_DDL','1');
# end # end
# ^ # ^
# #
# create or alter trigger trg_connect active on connect position 0 as # create or alter trigger trg_connect active on connect position 0 as
# begin # begin
# execute statement 'insert into log(event_name) values(''connect'')' # execute statement 'insert into log(event_name) values(''connect'')'
# with autonomous transaction; # with autonomous transaction;
# end # end
# ^ # ^
# #
# create or alter trigger trg_disc active on disconnect position 0 as # create or alter trigger trg_disc active on disconnect position 0 as
# begin # begin
# if ( rdb$get_context('USER_SESSION','INITIAL_DDL') is null ) then # if ( rdb$get_context('USER_SESSION','INITIAL_DDL') is null ) then
@ -140,76 +141,77 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# end # end
# ^ # ^
# set term ;^ # set term ;^
# commit; # commit;
# ''' # '''
# #
# f_sql_init=open( os.path.join(context['temp_directory'],'tmp_5075_init.sql'), 'w') # f_sql_init=open( os.path.join(context['temp_directory'],'tmp_5075_init.sql'), 'w')
# f_sql_init.write(sqltxt) # f_sql_init.write(sqltxt)
# flush_and_close( f_sql_init ) # flush_and_close( f_sql_init )
# #
# sqlres=subprocess.check_output([context['isql_path'], dsn, "-nod", "-i", f_sql_init.name], stderr=subprocess.STDOUT) # sqlres=subprocess.check_output([context['isql_path'], dsn, "-nod", "-i", f_sql_init.name], stderr=subprocess.STDOUT)
# print(sqlres) # Must be empty (no errors) # print(sqlres) # Must be empty (no errors)
# #
# sqltxt='''set list on;set count on; select event_id, event_name from log; # sqltxt='''set list on;set count on; select event_id, event_name from log;
# ''' # '''
# #
# # http://stackoverflow.com/questions/8475290/how-do-i-write-to-a-python-subprocess-stdin # # http://stackoverflow.com/questions/8475290/how-do-i-write-to-a-python-subprocess-stdin
# #
# sqlres='' # sqlres=''
# #
# p = Popen([context['isql_path'], dsn], stdout=PIPE, stdin=PIPE, stderr=subprocess.STDOUT ) # p = Popen([context['isql_path'], dsn], stdout=PIPE, stdin=PIPE, stderr=subprocess.STDOUT )
# sqlres += p.communicate(input=sqltxt)[0] # sqlres += p.communicate(input=sqltxt)[0]
# #
# #
# p = Popen([context['isql_path'], dsn], stdout=PIPE, stdin=PIPE, stderr=subprocess.STDOUT ) # p = Popen([context['isql_path'], dsn], stdout=PIPE, stdin=PIPE, stderr=subprocess.STDOUT )
# sqlres += p.communicate(input=sqltxt)[0] # sqlres += p.communicate(input=sqltxt)[0]
# #
# p = Popen([context['isql_path'], dsn], stdout=PIPE, stdin=PIPE, stderr=subprocess.STDOUT ) # p = Popen([context['isql_path'], dsn], stdout=PIPE, stdin=PIPE, stderr=subprocess.STDOUT )
# sqlres += p.communicate(input=sqltxt)[0] # sqlres += p.communicate(input=sqltxt)[0]
# #
# print(sqlres) # print(sqlres)
# #
# sqlres=subprocess.check_output([context['isql_path'], dsn, "-nod", "-i", f_sql_init.name], stderr=subprocess.STDOUT) # sqlres=subprocess.check_output([context['isql_path'], dsn, "-nod", "-i", f_sql_init.name], stderr=subprocess.STDOUT)
# print(sqlres) # Must be empty (no errors) # print(sqlres) # Must be empty (no errors)
# #
# p = Popen([context['isql_path'], dsn], stdout=PIPE, stdin=PIPE, stderr=subprocess.STDOUT ) # p = Popen([context['isql_path'], dsn], stdout=PIPE, stdin=PIPE, stderr=subprocess.STDOUT )
# sqlres = p.communicate(input=sqltxt)[0] # sqlres = p.communicate(input=sqltxt)[0]
# print(sqlres) # print(sqlres)
# #
# #
# f_fblog_after=open( os.path.join(context['temp_directory'],'tmp_5075_fblog_after.txt'), 'w') # f_fblog_after=open( os.path.join(context['temp_directory'],'tmp_5075_fblog_after.txt'), 'w')
# svc_get_fb_log( f_fblog_after ) # svc_get_fb_log( f_fblog_after )
# flush_and_close( f_fblog_after ) # flush_and_close( f_fblog_after )
# #
# time.sleep(1) # time.sleep(1)
# #
# oldfb=open(f_fblog_before.name, 'r') # oldfb=open(f_fblog_before.name, 'r')
# newfb=open(f_fblog_after.name, 'r') # newfb=open(f_fblog_after.name, 'r')
# #
# difftext = ''.join(difflib.unified_diff( # difftext = ''.join(difflib.unified_diff(
# oldfb.readlines(), # oldfb.readlines(),
# newfb.readlines() # newfb.readlines()
# )) # ))
# oldfb.close() # oldfb.close()
# newfb.close() # newfb.close()
# #
# f_diff_txt=open( os.path.join(context['temp_directory'],'tmp_5075_diff.txt'), 'w') # f_diff_txt=open( os.path.join(context['temp_directory'],'tmp_5075_diff.txt'), 'w')
# f_diff_txt.write(difftext) # f_diff_txt.write(difftext)
# flush_and_close( f_diff_txt ) # flush_and_close( f_diff_txt )
# #
# with open( f_diff_txt.name,'r') as f: # with open( f_diff_txt.name,'r') as f:
# for line in f: # for line in f:
# print('new messages in firebird.log: '+line) # print('new messages in firebird.log: '+line)
# #
# # Cleanup. # # Cleanup.
# ########## # ##########
# # do NOT remove this pause otherwise some of logs will not be enable for deletion and test will finish with # # do NOT remove this pause otherwise some of logs will not be enable for deletion and test will finish with
# # Exception raised while executing Python test script. exception: WindowsError: 32 # # Exception raised while executing Python test script. exception: WindowsError: 32
# time.sleep(1) # time.sleep(1)
# cleanup((f_sql_init,f_fblog_before,f_fblog_after,f_diff_txt)) # cleanup((f_sql_init,f_fblog_before,f_fblog_after,f_diff_txt))
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
EVENT_ID 1 EVENT_ID 1
@ -236,11 +238,81 @@ expected_stdout_1 = """
EVENT_ID 1 EVENT_ID 1
EVENT_NAME connect EVENT_NAME connect
Records affected: 1 Records affected: 1
""" """
init_script = """
set term ^;
create or alter trigger trg_connect active on connect position 0 as
begin
end
^
create or alter trigger trg_disc active on disconnect position 0 as
begin
end
^
set term ;^
commit;
recreate sequence g;
recreate table log(
event_id int generated by default as identity constraint pk_log primary key,
event_name varchar(20),
when_it_was timestamp default 'now'
);
commit;
set term ^;
execute block as
begin
rdb$set_context('USER_SESSION','INITIAL_DDL','1');
end
^
create or alter trigger trg_connect active on connect position 0 as
begin
execute statement 'insert into log(event_name) values(''connect'')'
with autonomous transaction;
end
^
create or alter trigger trg_disc active on disconnect position 0 as
begin
if ( rdb$get_context('USER_SESSION','INITIAL_DDL') is null ) then
execute statement 'insert into log(event_name) values(''disconnect'')'
with autonomous transaction;
end
^
set term ;^
commit;
"""
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail def test_1(act_1: Action, capsys):
def test_1(db_1): with act_1.connect_server() as srv:
pytest.fail("Test not IMPLEMENTED") srv.info.get_log()
log_before = srv.readlines()
#
act_1.isql(switches=['-nod'], input=init_script)
# Tests 3x
test_cmd = 'set list on;set count on; select event_id, event_name from log;'
for step in range(3):
act_1.reset()
act_1.isql(switches=[], input=test_cmd)
print(act_1.stdout)
# Init again
act_1.reset()
act_1.isql(switches=['-nod'], input=init_script)
# Test again
act_1.reset()
act_1.isql(switches=[], input=test_cmd)
print(act_1.stdout)
# Get log again
with act_1.connect_server() as srv:
srv.info.get_log()
log_after = srv.readlines()
#
act_1.expected_stdout = expected_stdout_1
act_1.stdout = capsys.readouterr().out
assert act_1.clean_stdout == act_1.clean_expected_stdout
assert list(unified_diff(log_before, log_after)) == []

View File

@ -2,30 +2,30 @@
# #
# id: bugs.core_5077 # id: bugs.core_5077
# title: ISQL 'SHOW DATABASE' command does not show encryption status of database # title: ISQL 'SHOW DATABASE' command does not show encryption status of database
# decription: # decription:
# We create new database ('tmp_core_5077.fdb') and try to encrypt it usng IBSurgeon Demo Encryption package # We create new database ('tmp_core_5077.fdb') and try to encrypt it usng IBSurgeon Demo Encryption package
# ( https://ib-aid.com/download-demo-firebird-encryption-plugin/ ; https://ib-aid.com/download/crypt/CryptTest.zip ) # ( https://ib-aid.com/download-demo-firebird-encryption-plugin/ ; https://ib-aid.com/download/crypt/CryptTest.zip )
# License file plugins\\dbcrypt.conf with unlimited expiration was provided by IBSurgeon to Firebird Foundation (FF). # License file plugins\\dbcrypt.conf with unlimited expiration was provided by IBSurgeon to Firebird Foundation (FF).
# This file was preliminary stored in FF Test machine. # This file was preliminary stored in FF Test machine.
# Test assumes that this file and all neccessary libraries already were stored into FB_HOME and %FB_HOME%\\plugins. # Test assumes that this file and all neccessary libraries already were stored into FB_HOME and %FB_HOME%\\plugins.
# #
# After test database will be created, we try to encrypt it using 'alter database encrypt with <plugin_name> ...' command # After test database will be created, we try to encrypt it using 'alter database encrypt with <plugin_name> ...' command
# (where <plugin_name> = dbcrypt - name of .dll in FB_HOME\\plugins\\ folder that implements encryption). # (where <plugin_name> = dbcrypt - name of .dll in FB_HOME\\plugins\\ folder that implements encryption).
# Then we allow engine to complete this job - take delay about 1..2 seconds BEFORE detach from database. # Then we allow engine to complete this job - take delay about 1..2 seconds BEFORE detach from database.
# #
# After this we run ISQL with 'SHOW DATABASE' command. Its output has to contain string 'Database encrypted'. # After this we run ISQL with 'SHOW DATABASE' command. Its output has to contain string 'Database encrypted'.
# #
# Finally, we change this temp DB state to full shutdown in order to have 100% ability to drop this file. # Finally, we change this temp DB state to full shutdown in order to have 100% ability to drop this file.
# #
# Checked on: 4.0.0.1629: OK, 6.264s; 3.0.5.33179: OK, 4.586s. # Checked on: 4.0.0.1629: OK, 6.264s; 3.0.5.33179: OK, 4.586s.
# #
# 13.04.2021. Adapted for run both on Windows and Linux. Checked on: # 13.04.2021. Adapted for run both on Windows and Linux. Checked on:
# Windows: 4.0.0.2416 # Windows: 4.0.0.2416
# Linux: 4.0.0.2416 # Linux: 4.0.0.2416
# Note: different names for encryption plugin and keyholde rare used for Windows vs Linux: # Note: different names for encryption plugin and keyholde rare used for Windows vs Linux:
# PLUGIN_NAME = 'dbcrypt' if os.name == 'nt' else '"fbSampleDbCrypt"' # PLUGIN_NAME = 'dbcrypt' if os.name == 'nt' else '"fbSampleDbCrypt"'
# KHOLDER_NAME = 'KeyHolder' if os.name == 'nt' else "fbSampleKeyHolder" # KHOLDER_NAME = 'KeyHolder' if os.name == 'nt' else "fbSampleKeyHolder"
# #
# tracker_id: CORE-5077 # tracker_id: CORE-5077
# min_versions: ['3.0.0'] # min_versions: ['3.0.0']
# versions: 3.0 # versions: 3.0
@ -45,36 +45,36 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# import time # import time
# import subprocess # import subprocess
# import re # import re
# import fdb # import fdb
# from fdb import services # from fdb import services
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# engine = db_conn.engine_version # engine = db_conn.engine_version
# db_conn.close() # db_conn.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close( file_handle ): # def flush_and_close( file_handle ):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull: # if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull:
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -86,18 +86,18 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.') # print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.')
# print('type(f_names_list[i])=',type(f_names_list[i])) # print('type(f_names_list[i])=',type(f_names_list[i]))
# del_name = None # del_name = None
# #
# if del_name and os.path.isfile( del_name ): # if del_name and os.path.isfile( del_name ):
# os.remove( del_name ) # os.remove( del_name )
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# tmpfdb='$(DATABASE_LOCATION)'+'tmp_core_5077.fdb' # tmpfdb='$(DATABASE_LOCATION)'+'tmp_core_5077.fdb'
# #
# cleanup( (tmpfdb,) ) # cleanup( (tmpfdb,) )
# #
# con = fdb.create_database( dsn = 'localhost:'+tmpfdb ) # con = fdb.create_database( dsn = 'localhost:'+tmpfdb )
# #
# # 14.04.2021. # # 14.04.2021.
# # Name of encryption plugin depends on OS: # # Name of encryption plugin depends on OS:
# # * for Windows we (currently) use plugin by IBSurgeon, its name is 'dbcrypt'; # # * for Windows we (currently) use plugin by IBSurgeon, its name is 'dbcrypt';
@ -107,7 +107,7 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# # # #
# PLUGIN_NAME = 'dbcrypt' if os.name == 'nt' else ( '"fbSampleDbCrypt"' if engine >= 4.0 else '"DbCrypt_example"') # PLUGIN_NAME = 'dbcrypt' if os.name == 'nt' else ( '"fbSampleDbCrypt"' if engine >= 4.0 else '"DbCrypt_example"')
# KHOLDER_NAME = 'KeyHolder' if os.name == 'nt' else "fbSampleKeyHolder" # KHOLDER_NAME = 'KeyHolder' if os.name == 'nt' else "fbSampleKeyHolder"
# #
# cur = con.cursor() # cur = con.cursor()
# cur.execute('alter database encrypt with %(PLUGIN_NAME)s key Red' % locals()) # cur.execute('alter database encrypt with %(PLUGIN_NAME)s key Red' % locals())
# ### DOES NOT WORK ON LINUX! ISSUES 'TOKEN UNKNOWN' !! >>> con.execute_immediate('alter database encrypt with %(PLUGIN_NAME)s key Red' % locals()) // sent letter to Alex and dimitr, 14.04.2021 # ### DOES NOT WORK ON LINUX! ISSUES 'TOKEN UNKNOWN' !! >>> con.execute_immediate('alter database encrypt with %(PLUGIN_NAME)s key Red' % locals()) // sent letter to Alex and dimitr, 14.04.2021
@ -115,66 +115,64 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# time.sleep(2) # time.sleep(2)
# # ^ # # ^
# # +-------- !! ALLOW BACKGROUND ENCRYPTION PROCESS TO COMPLETE ITS JOB !! # # +-------- !! ALLOW BACKGROUND ENCRYPTION PROCESS TO COMPLETE ITS JOB !!
# #
# con.close() # con.close()
# #
# ######################################## # ########################################
# # run ISQL with 'SHOW DATABASE' command: # # run ISQL with 'SHOW DATABASE' command:
# ######################################## # ########################################
# f_isql_cmd=open( os.path.join(context['temp_directory'],'tmp_5077.sql'), 'w') # f_isql_cmd=open( os.path.join(context['temp_directory'],'tmp_5077.sql'), 'w')
# f_isql_cmd.write('show database;') # f_isql_cmd.write('show database;')
# flush_and_close( f_isql_cmd ) # flush_and_close( f_isql_cmd )
# #
# f_isql_log=open( os.path.join(context['temp_directory'], 'tmp_5077.log'), 'w') # f_isql_log=open( os.path.join(context['temp_directory'], 'tmp_5077.log'), 'w')
# f_isql_err=open( os.path.join(context['temp_directory'], 'tmp_5077.err'), 'w') # f_isql_err=open( os.path.join(context['temp_directory'], 'tmp_5077.err'), 'w')
# subprocess.call( [context['isql_path'], 'localhost:' + tmpfdb, '-q', '-n', '-i', f_isql_cmd.name ], stdout=f_isql_log, stderr = f_isql_err) # subprocess.call( [context['isql_path'], 'localhost:' + tmpfdb, '-q', '-n', '-i', f_isql_cmd.name ], stdout=f_isql_log, stderr = f_isql_err)
# flush_and_close( f_isql_log ) # flush_and_close( f_isql_log )
# flush_and_close( f_isql_err ) # flush_and_close( f_isql_err )
# #
# #---------------------------- shutdown temp DB -------------------- # #---------------------------- shutdown temp DB --------------------
# #
# f_dbshut_log = open( os.path.join(context['temp_directory'],'tmp_dbshut_5077.log'), 'w') # f_dbshut_log = open( os.path.join(context['temp_directory'],'tmp_dbshut_5077.log'), 'w')
# subprocess.call( [ context['gfix_path'], 'localhost:'+tmpfdb, "-shut", "full", "-force", "0" ], # subprocess.call( [ context['gfix_path'], 'localhost:'+tmpfdb, "-shut", "full", "-force", "0" ],
# stdout = f_dbshut_log, # stdout = f_dbshut_log,
# stderr = subprocess.STDOUT # stderr = subprocess.STDOUT
# ) # )
# flush_and_close( f_dbshut_log ) # flush_and_close( f_dbshut_log )
# #
# allowed_patterns = ( # allowed_patterns = (
# re.compile( 'Database(\\s+not){0,1}\\s+encrypted\\.*', re.IGNORECASE), # re.compile( 'Database(\\s+not){0,1}\\s+encrypted\\.*', re.IGNORECASE),
# ) # )
# #
# with open( f_isql_log.name,'r') as f: # with open( f_isql_log.name,'r') as f:
# for line in f: # for line in f:
# match2some = filter( None, [ p.search(line) for p in allowed_patterns ] ) # match2some = filter( None, [ p.search(line) for p in allowed_patterns ] )
# if match2some: # if match2some:
# print( (' '.join( line.split()).upper() ) ) # print( (' '.join( line.split()).upper() ) )
# #
# with open( f_isql_err.name,'r') as f: # with open( f_isql_err.name,'r') as f:
# for line in f: # for line in f:
# print("Unexpected error when doing 'SHOW DATABASE': "+line) # print("Unexpected error when doing 'SHOW DATABASE': "+line)
# #
# with open( f_dbshut_log.name,'r') as f: # with open( f_dbshut_log.name,'r') as f:
# for line in f: # for line in f:
# print("Unexpected error on SHUTDOWN temp database: "+line) # print("Unexpected error on SHUTDOWN temp database: "+line)
# #
# #
# # CLEANUP # # CLEANUP
# ######### # #########
# time.sleep(1) # time.sleep(1)
# #
# cleanup( ( f_isql_log, f_isql_err, f_isql_cmd, f_dbshut_log,tmpfdb ) ) # cleanup( ( f_isql_log, f_isql_err, f_isql_cmd, f_dbshut_log,tmpfdb ) )
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1) #act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
expected_stdout_1 = """ expected_stdout_1 = """
DATABASE ENCRYPTED DATABASE ENCRYPTED
""" """
@pytest.mark.version('>=3.0') @pytest.mark.version('>=3.0')
@pytest.mark.xfail
def test_1(db_1): def test_1(db_1):
pytest.fail("Test not IMPLEMENTED") pytest.skip("Test depends on 3rd party encryption plugin")
#pytest.fail("Test not IMPLEMENTED")

View File

@ -2,7 +2,7 @@
# #
# id: bugs.core_5078 # id: bugs.core_5078
# title: "Invalid BLOB ID" error # title: "Invalid BLOB ID" error
# decription: # decription:
# Confirmed, got exception during selecting data on Classic WI-V2.5.5.26952, x64. # Confirmed, got exception during selecting data on Classic WI-V2.5.5.26952, x64.
# STDERR: # STDERR:
# Statement failed, SQLSTATE = 42000 # Statement failed, SQLSTATE = 42000
@ -15,14 +15,16 @@
# SUBS 2806 # SUBS 2806
# MSGTYPE 2524 # MSGTYPE 2524
# NOTIFYPARAMS 1482 # NOTIFYPARAMS 1482
# #
# tracker_id: CORE-5078 # tracker_id: CORE-5078
# min_versions: ['2.5.6'] # min_versions: ['2.5.6']
# versions: 2.5.6 # versions: 2.5.6
# qmid: None # qmid: None
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from pathlib import Path
import zipfile
from firebird.qa import db_factory, python_act, Action, temp_file
# version: 2.5.6 # version: 2.5.6
# resources: None # resources: None
@ -35,36 +37,36 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# import sys # import sys
# import subprocess # import subprocess
# from subprocess import Popen # from subprocess import Popen
# import zipfile # import zipfile
# import time # import time
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# db_conn.close() # db_conn.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close(file_handle): # def flush_and_close(file_handle):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull: # if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull:
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -75,58 +77,66 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# else: # else:
# print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.') # print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.')
# del_name = None # del_name = None
# #
# if del_name and os.path.isfile( del_name ): # if del_name and os.path.isfile( del_name ):
# os.remove( del_name ) # os.remove( del_name )
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# zf = zipfile.ZipFile( os.path.join(context['files_location'],'core_5078.zip') ) # zf = zipfile.ZipFile( os.path.join(context['files_location'],'core_5078.zip') )
# zf.extractall( context['temp_directory'] ) # zf.extractall( context['temp_directory'] )
# zf.close() # zf.close()
# #
# # Result: file tmp_core_5078.fbk is extracted into context['temp_directory'] # # Result: file tmp_core_5078.fbk is extracted into context['temp_directory']
# #
# tmp_fbk=os.path.join(context['temp_directory'],'tmp_core_5078.fbk') # tmp_fbk=os.path.join(context['temp_directory'],'tmp_core_5078.fbk')
# tmp_fdb=os.path.join(context['temp_directory'],'tmp_core_5078.fdb') # tmp_fdb=os.path.join(context['temp_directory'],'tmp_core_5078.fdb')
# #
# cleanup( (tmp_fdb,) ) # cleanup( (tmp_fdb,) )
# #
# # Restoring from .fbk: # # Restoring from .fbk:
# runProgram( context['fbsvcmgr_path'],['localhost:service_mgr','action_restore','dbname',tmp_fdb,'bkp_file',tmp_fbk]) # runProgram( context['fbsvcmgr_path'],['localhost:service_mgr','action_restore','dbname',tmp_fdb,'bkp_file',tmp_fbk])
# #
# f_sql=open( os.path.join(context['temp_directory'],'tmp_isql_5078.sql'), 'w') # f_sql=open( os.path.join(context['temp_directory'],'tmp_isql_5078.sql'), 'w')
# f_sql.write('set list on; select * from do_changeTxStatus;') # f_sql.write('set list on; select * from do_changeTxStatus;')
# flush_and_close( f_sql ) # flush_and_close( f_sql )
# #
# f_log = open(os.devnull, 'w') # f_log = open(os.devnull, 'w')
# f_err = open( os.path.join(context['temp_directory'],'tmp_isql_5078.err'), 'w') # f_err = open( os.path.join(context['temp_directory'],'tmp_isql_5078.err'), 'w')
# #
# subprocess.call( [context['isql_path'], 'localhost:'+tmp_fdb, "-i", f_sql.name],stdout=f_log,stderr=f_err) # subprocess.call( [context['isql_path'], 'localhost:'+tmp_fdb, "-i", f_sql.name],stdout=f_log,stderr=f_err)
# #
# flush_and_close( f_log ) # flush_and_close( f_log )
# flush_and_close( f_err ) # flush_and_close( f_err )
# #
# time.sleep(1) # time.sleep(1)
# #
# # This file should be EMPTY: # # This file should be EMPTY:
# ########################### # ###########################
# with open(f_err.name) as f: # with open(f_err.name) as f:
# print( f.read() ) # print( f.read() )
# #
# # CLEANUP # # CLEANUP
# ########## # ##########
# time.sleep(1) # time.sleep(1)
# cleanup( (tmp_fbk, tmp_fdb, f_err, f_sql) ) # cleanup( (tmp_fbk, tmp_fdb, f_err, f_sql) )
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
act_1 = python_act('db_1', substitutions=substitutions_1)
fbk_file_1 = temp_file('tmp_core_5078.fbk')
fdb_file_1 = temp_file('tmp_core_5078.fdb')
@pytest.mark.version('>=2.5.6') @pytest.mark.version('>=2.5.6')
@pytest.mark.xfail def test_1(act_1: Action, fbk_file_1: Path, fdb_file_1: Path):
def test_1(db_1): script_file = zipfile.Path(act_1.vars['files'] / 'core_5078.zip',
pytest.fail("Test not IMPLEMENTED") at='tmp_core_5078.fbk')
fbk_file_1.write_bytes(script_file.read_bytes())
with act_1.connect_server() as srv:
srv.database.restore(database=str(fdb_file_1), backup=str(fbk_file_1))
srv.wait()
# This should execute without errors
act_1.isql(switches=[str(fdb_file_1)], input='set list on; select * from do_changeTxStatus;',
connect_db=False)

View File

@ -2,16 +2,16 @@
# #
# id: bugs.core_5085 # id: bugs.core_5085
# title: Allow to fixup (nbackup) database via Services API # title: Allow to fixup (nbackup) database via Services API
# decription: # decription:
# Checked on 4.0.0.2119: OK. # Checked on 4.0.0.2119: OK.
# #
# tracker_id: CORE-5085 # tracker_id: CORE-5085
# min_versions: ['4.0'] # min_versions: ['4.0']
# versions: 4.0 # versions: 4.0
# qmid: # qmid:
import pytest import pytest
from firebird.qa import db_factory, isql_act, Action from firebird.qa import db_factory, python_act, Action
# version: 4.0 # version: 4.0
# resources: None # resources: None
@ -24,38 +24,38 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1 # test_script_1
#--- #---
# #
# import os # import os
# import time # import time
# import subprocess # import subprocess
# #
# os.environ["ISC_USER"] = user_name # os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password # os.environ["ISC_PASSWORD"] = user_password
# #
# db_source = db_conn.database_name # db_source = db_conn.database_name
# db_delta = db_source +'.delta' # db_delta = db_source +'.delta'
# nbk_level_0 = os.path.splitext(db_source)[0] + '.nbk00' # nbk_level_0 = os.path.splitext(db_source)[0] + '.nbk00'
# #'$(DATABASE_LOCATION)tmp_core_5085.nbk_00' # #'$(DATABASE_LOCATION)tmp_core_5085.nbk_00'
# #
# db_conn.close() # db_conn.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def flush_and_close(file_handle): # def flush_and_close(file_handle):
# # https://docs.python.org/2/library/os.html#os.fsync # # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f, # # If you're starting with a Python file object f,
# # first do f.flush(), and # # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. # # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os # global os
# #
# file_handle.flush() # file_handle.flush()
# if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull: # if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull:
# # otherwise: "OSError: [Errno 9] Bad file descriptor"! # # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno()) # os.fsync(file_handle.fileno())
# file_handle.close() # file_handle.close()
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# def cleanup( f_names_list ): # def cleanup( f_names_list ):
# global os # global os
# for i in range(len( f_names_list )): # for i in range(len( f_names_list )):
@ -66,25 +66,25 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# else: # else:
# print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.') # print('Unrecognized type of element:', f_names_list[i], ' - can not be treated as file.')
# del_name = None # del_name = None
# #
# if del_name and os.path.isfile( del_name ): # if del_name and os.path.isfile( del_name ):
# os.remove( del_name ) # os.remove( del_name )
# #
# #-------------------------------------------- # #--------------------------------------------
# #
# cleanup( ( db_delta, nbk_level_0, ) ) # cleanup( ( db_delta, nbk_level_0, ) )
# #
# # 1. Create standby copy: make clone of source DB using nbackup -b 0: # # 1. Create standby copy: make clone of source DB using nbackup -b 0:
# ######################## # ########################
# f_nbk0_log=open( os.path.join(context['temp_directory'],'tmp_nbk0_5085.log'), 'w') # f_nbk0_log=open( os.path.join(context['temp_directory'],'tmp_nbk0_5085.log'), 'w')
# f_nbk0_err=open( os.path.join(context['temp_directory'],'tmp_nbk0_5085.err'), 'w') # f_nbk0_err=open( os.path.join(context['temp_directory'],'tmp_nbk0_5085.err'), 'w')
# #
# subprocess.call( [context['nbackup_path'], '-L', db_source], stdout=f_nbk0_log, stderr=f_nbk0_err ) # subprocess.call( [context['nbackup_path'], '-L', db_source], stdout=f_nbk0_log, stderr=f_nbk0_err )
# subprocess.call( [context['fbsvcmgr_path'], 'service_mgr', 'action_nfix', 'dbname', db_source], stdout=f_nbk0_log, stderr=f_nbk0_err ) # subprocess.call( [context['fbsvcmgr_path'], 'service_mgr', 'action_nfix', 'dbname', db_source], stdout=f_nbk0_log, stderr=f_nbk0_err )
# #
# flush_and_close( f_nbk0_log ) # flush_and_close( f_nbk0_log )
# flush_and_close( f_nbk0_err ) # flush_and_close( f_nbk0_err )
# #
# # test connect to ensure that all OK after fixup: # # test connect to ensure that all OK after fixup:
# ############## # ##############
# con=fdb.connect(dsn = dsn) # con=fdb.connect(dsn = dsn)
@ -94,7 +94,7 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# print(r[0]) # print(r[0])
# cur.close() # cur.close()
# con.close() # con.close()
# #
# # Check. All of these files must be empty: # # Check. All of these files must be empty:
# ################################### # ###################################
# f_list=(f_nbk0_log, f_nbk0_err) # f_list=(f_nbk0_log, f_nbk0_err)
@ -103,23 +103,32 @@ db_1 = db_factory(sql_dialect=3, init=init_script_1)
# for line in f: # for line in f:
# if line.split(): # if line.split():
# print( 'UNEXPECTED output in file '+f_list[i].name+': '+line.upper() ) # print( 'UNEXPECTED output in file '+f_list[i].name+': '+line.upper() )
# #
# # Cleanup. # # Cleanup.
# ########## # ##########
# time.sleep(1) # time.sleep(1)
# cleanup( (f_nbk0_log,f_nbk0_err,db_delta, nbk_level_0) ) # cleanup( (f_nbk0_log,f_nbk0_err,db_delta, nbk_level_0) )
# #
# #
#--- #---
#act_1 = python_act('db_1', test_script_1, substitutions=substitutions_1)
expected_stdout_1 = """ act_1 = python_act('db_1', substitutions=substitutions_1)
0
""" #expected_stdout_1 = """
#0
#"""
@pytest.mark.version('>=4.0') @pytest.mark.version('>=4.0')
@pytest.mark.xfail def test_1(act_1: Action):
def test_1(db_1): act_1.nbackup(switches=['-l', str(act_1.db.db_path)])
pytest.fail("Test not IMPLEMENTED") #with act_1.connect_server() as srv:
# This raises error in new FB OO API while calling spb.insert_string(SPBItem.DBNAME, database):
# "Internal error when using clumplet API: attempt to store data in dataless clumplet"
#srv.database.nfix_database(database=str(act_1.db.db_path))
# So we have to use svcmgr...
act_1.reset()
act_1.svcmgr(switches=['action_nfix', 'dbname', str(act_1.db.db_path)])
with act_1.db.connect() as con:
c = con.cursor()
result = c.execute('select mon$backup_state from mon$database').fetchall()
assert result == [(0, )]