diff --git a/tests/bugs/gh_6860_test.py b/tests/bugs/gh_6860_test.py index 818ef3a2..4c8ff55d 100644 --- a/tests/bugs/gh_6860_test.py +++ b/tests/bugs/gh_6860_test.py @@ -2,260 +2,107 @@ """ ID: issue-6860 -ISSUE: 6860 +ISSUE: https://github.com/FirebirdSQL/firebird/issues/6860 TITLE: Create user statement fails with SQLSTATE = HY000 when using DataTypeCompatibility DESCRIPTION: - Test makes back-copies of firebnird.conf + databases.conf and changes firebird.conf by adding DataTypeCompatibility = N.M; - also, databases.conf is changed by adding alias for new database which will be self-security and has alias = 'self_gh_6860'. - Such database (self-security) allows us to create new DBA (SYSDBA/masterkey) without any affect on common security.db data. - - Then we make file-level copy of security.db to database defined by alias 'self_gh_6860', do connect and apply SQL script that - creates and drops user. Repeat this for DataTypeCompatibility = 2.5 and 3.0. - No errors must raise during this work. - - Finally, test restored back-copies of .conf files. - - NOTE-1. - Perhaps, test can be significantly simplified (may be implemented without self-security database ?), but this will be done later. - - NOTE-2. - One need to change temporary DB state to 'full shutdown' before drop it otherwise we get 'Windows 32' error because DB file remains - opened by engine. In order to do this, we have to call fbsvcmgr and pass "expected_db" command switch to it with specifying name - of tempoprary DB which is self-security. Otherwise fbsvcmgr will try to connect to common security.db. - - Confirmed on 5.0.0.82, 4.0.1.2519: statement 'CREATE USER SYSDBA ...' fails with "SQLSTATE = HY000/.../-Incompatible data type" - Checked on 5.0.0.131 SS/CS, 4.0.1.2563 SS/CS -- all fine. + Test creates two self-security databases, each of them allows to create new SYSDBA. + Alias for first of these DB contains 'DataTypeCompatibility = 2.5', second - 'DataTypeCompatibility = 3.0' + (NB: parameter 'DataTypeCompatibility' is defined as per-database). + Creation of SYSDBA in each of these databases must no raise any error. FBTEST: bugs.gh_6860 +NOTES: + [18.08.2022] pzotov + 1. One need to be sure that firebird.conf does NOT contain DatabaseAccess = None. + 2. Test uses pre-created databases.conf which has TWO aliases ('tmp_gh_6860_fb25_alias', 'tmp_gh_6860_fb30_alias') + for each DataTypeCompatibility value (2.5 and 3.0). + Both these aliases must defined SecurityDatabase which points to that alias, thus making such DB be self-security. + Database file for that alias must NOT exist in the QA_root/files/qa/ subdirectory: it will be created here. + Content of databases.conf must be taken from $QA_ROOT/files/qa-databases.conf (one need to replace + it before every test session). + Discussed with pcisar, letters since 30-may-2022 13:48, subject: + "new qa, core_4964_test.py: strange outcome when use... shutil.copy() // comparing to shutil.copy2()" + 3. Each value of req_alias must be EXACTLY the same as alias specified in the pre-created databases.conf + (for LINUX this equality is case-sensitive, even when aliases are compared!) + + Confirmed again problem: 5.0.0.82, 4.0.1.2519 (got SQLSTATE = HY000 / add record error / -Incompatible data type). + Checked on 5.0.0.623, 4.0.1.2692 - both on Windows and Linux. """ +import re +import locale +from pathlib import Path + import pytest from firebird.qa import * +# Name of alias for self-security DB in the QA_root/files/qa-databases.conf. +# This file must be copied manually to each testing FB homw folder, with replacing +# databases.conf there: +# db = db_factory() -act_ = python_act('db_1') +act = python_act('db') -expected_stdout = """ - SEC$USER_NAME TMP$GH_6860_25 - SEC$USER_NAME TMP$GH_6860_25_X +@pytest.mark.version('>=4.0') +def test_1(act: Action, capsys): + + + for req_alias in ('tmp_gh_6860_fb25_alias', 'tmp_gh_6860_fb30_alias'): + + # Scan line-by-line through databases.conf, find line starting with REQUIRED_ALIAS and extract name of file that + # must be created in the $(dir_sampleDb)/qa/ folder. This name will be used further as target database (tmp_fdb). + # NOTE: we have to SKIP lines which are commented out, i.e. if they starts with '#': + p_required_alias_ptn = re.compile( '^(?!#)((^|\\s+)' + req_alias + ')\\s*=\\s*\\$\\(dir_sampleDb\\)/qa/', re.IGNORECASE ) + fname_in_dbconf = None + + with open(act.home_dir/'databases.conf', 'r') as f: + for line in f: + if p_required_alias_ptn.search(line): + # If databases.conf contains line like this: + # tmp_6147_alias = $(dir_sampleDb)/qa/tmp_core_6147.fdb + # - then we extract filename: 'tmp_core_6147.fdb' (see below): + fname_in_dbconf = Path(line.split('=')[1].strip()).name + break + + # if 'fname_in_dbconf' remains undefined here then propably REQUIRED_ALIAS not equals to specified in the databases.conf! + # + assert fname_in_dbconf + + # Full path + filename of database to which we will try to connect: + # + tmp_fdb = Path( act.vars['sample_dir'], 'qa', fname_in_dbconf ) + + tmp_dba_pswd = 'p@$$w6860' - SEC$USER_NAME TMP$GH_6860_30 - SEC$USER_NAME TMP$GH_6860_30_X -""" + init_sql = f''' + set bail on; + set list on; -@pytest.mark.skip('FIXME: databases.conf / firebird.conf') -@pytest.mark.version('>=4.0.1') -def test_1(act_: Action): - pytest.fail("Not IMPLEMENTED") + create database '{req_alias}' user {act.db.user}; -# test_script_1 -#--- -# import os -# import sys -# import subprocess -# from subprocess import Popen -# import datetime -# import time -# import shutil -# from fdb import services -# from datetime import datetime as dt -# from datetime import timedelta -# -# os.environ["ISC_USER"] = user_name -# os.environ["ISC_PASSWORD"] = user_password -# -# db_conn.close() -# -# #----------------------------------- -# def showtime(): -# global dt -# return ''.join( (dt.now().strftime("%H:%M:%S.%f")[:11],'.') ) -# -# #-------------------------------------------- -# -# def flush_and_close( file_handle ): -# # https://docs.python.org/2/library/os.html#os.fsync -# # If you're starting with a Python file object f, -# # first do f.flush(), and -# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. -# global os -# -# file_handle.flush() -# if file_handle.mode not in ('r', 'rb') and file_handle.name != os.devnull: -# # otherwise: "OSError: [Errno 9] Bad file descriptor"! -# os.fsync(file_handle.fileno()) -# file_handle.close() -# -# #-------------------------------------------- -# -# def cleanup( f_names_list ): -# global os -# for i in range(len( f_names_list )): -# if type(f_names_list[i]) == file: -# del_name = f_names_list[i].name -# elif type(f_names_list[i]) == str: -# del_name = f_names_list[i] -# else: -# 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])) -# del_name = None -# -# if del_name and os.path.isfile( del_name ): -# os.remove( del_name ) -# -# #-------------------------------------------- -# -# -# FB_HOME = services.connect(host='localhost', user= user_name, password= user_password).get_home_directory() -# # Resut: FB_HOME is full path to FB instance home (with trailing slash). -# -# if os.name == 'nt': -# # For Windows we assume that client library is always in FB_HOME dir: -# FB_CLNT=os.path.join(FB_HOME, 'fbclient.dll') -# else: -# # For Linux client library will be searched in 'lib' subdirectory of FB_HOME: -# FB_CLNT=os.path.join(FB_HOME, 'lib', 'libfbclient.so' ) -# -# -# dts = datetime.datetime.now().strftime("%y%m%d_%H%M%S") -# -# fbconf_cur = os.path.join( FB_HOME, 'firebird.conf') -# fbconf_bak = os.path.join( context['temp_directory'], 'firebird'+dts+'.bak') -# -# dbconf_cur = os.path.join(FB_HOME, 'databases.conf') -# dbconf_bak = os.path.join(context['temp_directory'], 'databases_'+dts+'.bak') -# -# shutil.copy2( fbconf_cur, fbconf_bak ) -# shutil.copy2( dbconf_cur, dbconf_bak ) -# -# sec_db = context['isc4_path'] -# tmp_alias = 'self_gh_6860' -# -# ############################################################## -# -# for iter in (1,2): -# -# DCOMPAT_VAL = '2.5' if iter == 1 else '3.0' -# DCOMPAT_SUF = DCOMPAT_VAL.replace('.', '') -# DBA_PSWD = 'pw6860_' + DCOMPAT_SUF -# DBA_PSWD = 'masterkey' -# -# tmp_fdb = os.path.join(context['temp_directory'],'tmp_gh_6860_'+DCOMPAT_SUF+'.fdb') -# alias_data= ''' -# # Temporary added for executing test gh_6860.fbt -# # -# %(tmp_alias)s = %(tmp_fdb)s { -# RemoteAccess = true -# SecurityDatabase = %(tmp_alias)s -# } -# ''' % locals() -# -# cleanup( (tmp_fdb,) ) -# shutil.copy2( sec_db, tmp_fdb ) -# -# -# # Restore original content of .conf files: -# ################## -# shutil.copy2( fbconf_bak, fbconf_cur ) -# shutil.copy2( dbconf_bak, dbconf_cur ) -# -# f_fbconf=open( fbconf_cur, 'r') -# fbconf_content=f_fbconf.readlines() -# f_fbconf.close() -# for i,s in enumerate( fbconf_content ): -# line = s.lower().lstrip() -# if line.startswith( 'DataTypeCompatibility'.lower() ): -# fbconf_content[i] = '# [temply commented by fbtest for gh_6860.fbt] ' + s -# -# text2app= ''' -# ### TEMPORARY CHANGED FOR gh-6860.fbt ### -# DataTypeCompatibility = %(DCOMPAT_VAL)s -# ######################################### -# ''' % locals() -# -# fbconf_content += [ os.linesep + x for x in text2app.split( os.linesep ) ] -# -# f_fbconf=open( fbconf_cur, 'w') -# f_fbconf.writelines( fbconf_content ) -# flush_and_close( f_fbconf ) -# -# -# f_dbconf=open( dbconf_cur, 'a') -# f_dbconf.seek(0, 2) -# f_dbconf.write( alias_data ) -# flush_and_close( f_dbconf ) -# -# -# sql_txt = ''' -# set bail on; -# set list on; -# -# create or alter user %(user_name)s password '%(DBA_PSWD)s'; -# create or alter user tmp$gh_6860_%(DCOMPAT_SUF)s password '123' grant admin role; -# commit; -# -# connect 'localhost:%(tmp_alias)s' user tmp$gh_6860_%(DCOMPAT_SUF)s password '123'; -# -# create or alter user tmp$gh_6860_%(DCOMPAT_SUF)s_x password '456'; -# commit; -# -# connect 'localhost:%(tmp_alias)s' user %(user_name)s password '%(DBA_PSWD)s'; -# -# select sec$user_name from sec$users where sec$user_name starting with upper('tmp$gh_6860_') order by sec$user_name; -# commit; -# -# drop user tmp$gh_6860_%(DCOMPAT_SUF)s; -# drop user tmp$gh_6860_%(DCOMPAT_SUF)s_x; -# commit; -# ''' % dict(globals(), **locals()) -# -# f_isql_cmd=open( os.path.join(context['temp_directory'],'tmp_6860.dtc_' + DCOMPAT_SUF + '.sql'), 'w') -# f_isql_cmd.write(sql_txt) -# flush_and_close( f_isql_cmd ) -# -# f_isql_log = open( os.path.splitext(f_isql_cmd.name)[0] + '.log', 'w' ) -# f_isql_err = open( os.path.splitext(f_isql_cmd.name)[0] + '.err', 'w' ) -# subprocess.call( [ context['isql_path'], 'localhost:' + tmp_alias, '-q', '-user', user_name, '-i', f_isql_cmd.name], stdout=f_isql_log, stderr=f_isql_err ) -# flush_and_close( f_isql_log ) -# flush_and_close( f_isql_err ) -# -# with open( f_isql_err.name, 'r' ) as f: -# for line in f: -# if line: -# print("Unexpected STDERR when create/drop users: "+line) -# -# # Output list of users created on this iteration of test: -# with open( f_isql_log.name, 'r' ) as f: -# for line in f: -# if line: -# print(line) -# -# f_shutdown_log=open( os.path.join(context['temp_directory'],'tmp_6860_shutdown_'+DCOMPAT_SUF+'.log'), 'w') -# subprocess.call( [context['fbsvcmgr_path'], -# "localhost:service_mgr", -# "user", user_name, "password", DBA_PSWD, -# "expected_db", tmp_fdb, -# "action_properties", "prp_shutdown_mode", "prp_sm_full", "prp_shutdown_db", "0", "dbname", tmp_fdb, -# ], -# stdout = f_shutdown_log, -# stderr = subprocess.STDOUT -# ) -# flush_and_close( f_shutdown_log ) -# -# with open( f_shutdown_log.name, 'r' ) as f: -# for line in f: -# if line: -# print("Unexpected STDERR when try to make full shutdown: "+line) -# -# -# cleanup( (tmp_fdb,) ) -# cleanup( (f_isql_err,f_isql_log,f_isql_cmd, f_shutdown_log) ) -# -# -# # Restore original content of firebird.conf: -# ################## -# shutil.move( fbconf_bak, fbconf_cur ) -# shutil.move( dbconf_bak, dbconf_cur ) -# -#--- + select '{req_alias}' as req_alias, 'DB creation completed OK.' as msg from rdb$database; + alter database set linger to 0; + create user {act.db.user} password '{tmp_dba_pswd}' using plugin Srp; + commit; + ''' + + try: + act.expected_stdout = f""" + REQ_ALIAS {req_alias} + MSG DB creation completed OK. + """ + + act.isql(switches = ['-q'], input = init_sql, connect_db=False, credentials = False, combine_output = True, io_enc = locale.getpreferredencoding()) + assert act.clean_stdout == act.clean_expected_stdout + act.reset() + + # Change DB state to full shutdown in order to have ability to drop database file. + # This is needed because when DB is self-security then it will be kept opened for 10s + # (as it always occurs for common security.db). Set linger to 0 does not help. + act.gfix(switches=['-shut', 'full', '-force', '0', f'localhost:{req_alias}', '-user', act.db.user, '-pas', tmp_dba_pswd], io_enc = locale.getpreferredencoding(), credentials = False, combine_output = True) + act.stdout = capsys.readouterr().out + assert act.clean_stdout == act.clean_expected_stdout + act.reset() + finally: + tmp_fdb.unlink()