#coding:utf-8 # # id: bugs.gh_6872 # title: Indexed STARTING WITH execution is very slow with UNICODE collation # decription: # https://github.com/FirebirdSQL/firebird/issues/6872 # # 21.11.2021. Totally re-implemented, package 'psutil' must be installed. # # We make two calls of psutil.Process(fb_pid).cpu_times() (before and after SQL code) and obtain CPU User Time # values from each result. # Difference between them can be considered as much more accurate performance estimation. # # Test creates two tables and two procedures for measuring performance of STARTING WITH operator when it is applied # to the table WITH or WITHOUNT unicode collation. # # On each calls of procedural code (see variable N_MEASURES) dozen execution of 'SELECT ... FROM WHERE starting with ...' # are performed, where names '' and '' points to apropriate table and column (with or without collation). # Number of iterations within procedures is defined by variable N_COUNT_PER_MEASURE. # # Each result (difference between cpu_times().user values when PSQL code finished) is added to the list. # Finally, we evaluate MEDIAN of ratio values between cpu user time which was received for both of procedures. # If this median is less then threshold (see var. ADDED_COLL_TIME_MAX_RATIO) then result can be considered as ACCEPTABLE. # # See also: # https://psutil.readthedocs.io/en/latest/#psutil.cpu_times # # Checked on Windows: # * builds before/without fix: # 3.0.8.33540: median = 16.30; # 4.0.1.2520: median = 47.65 # 5.0.0.85: median = 43.14 # * builds after fix: # 4.0.1.2668: median = 1.70 # 5.0.0.313: median = 1.80 # # tracker_id: # min_versions: ['4.0.1'] # versions: 4.0.1 # qmid: None import pytest from firebird.qa import db_factory, python_act, Action # version: 4.0.1 # resources: None substitutions_1 = [] init_script_1 = """ recreate table test_utf8_miss_coll (c1 varchar(10) character set utf8); create index test_utf8_miss_coll_idx on test_utf8_miss_coll (c1); recreate table test_utf8_with_coll(c1 varchar(10) character set utf8 collate unicode); create index test_utf8_with_coll_idx on test_utf8_with_coll(c1); """ db_1 = db_factory(sql_dialect=3, init=init_script_1) # test_script_1 #--- # # import os # import psutil # import time # # os.environ["ISC_USER"] = user_name # os.environ["ISC_PASSWORD"] = user_password # # #------------------ # def median(lst): # n = len(lst) # s = sorted(lst) # return (sum(s[n//2-1:n//2+1])/2.0, s[n//2])[n % 2] if n else None # #------------------ # # ########################### # ### S E T T I N G S ### # ########################### # # Number of PSQL calls: # N_MEASURES = 30 # # # How many iterations must be done in each of stored procedures when they work: # N_COUNT_PER_MEASURE = 100000 # # # Maximal value for MEDIAN of ratios between CPU user time when comparison was made. # # # ADDED_COLL_TIME_MAX_RATIO = 2.0 # ############################### # # # sp_make_proc_ddl=''' -- x_name = 'with_coll' | 'miss_col' # create or alter procedure sp_%(x_name)s ( n_count int = 1000000 ) as # declare v smallint; # begin # while (n_count > 0) do # begin # select 1 from test_utf8_%(x_name)s where c1 starting with 'x' into v; # n_count = n_count - 1; # end # end # ''' # # # name_suffixes = ( 'with_coll', 'miss_coll' ) # for x_name in name_suffixes: # db_conn.execute_immediate( sp_make_proc_ddl % locals() ) # # db_conn.commit() # # cur=db_conn.cursor() # cur.execute('select mon$server_pid as p from mon$attachments where mon$attachment_id = current_connection') # fb_pid = int(cur.fetchone()[0]) # # # -------------------------------------- # sp_time = {} # # for i in range(0, N_MEASURES): # for x_name in name_suffixes: # # fb_info_init = psutil.Process(fb_pid).cpu_times() # cur.callproc( 'sp_%(x_name)s' % locals(), (N_COUNT_PER_MEASURE,) ) # fb_info_curr = psutil.Process(fb_pid).cpu_times() # # sp_time[ x_name, i ] = max(fb_info_curr.user - fb_info_init.user, 0.000001) # # #--------------------------------------- # cur.close() # # ratio_lst = [] # for i in range(0, N_MEASURES): # ratio_lst.append( sp_time['with_coll',i] / sp_time['miss_coll',i] ) # # median_ratio = median(ratio_lst) # # # # print( 'Duration ratio: ' + ('acceptable' if median_ratio < UTF8_TO_PTBR_MAX_RATIO else 'POOR: %s, more than threshold: %s' % ( '{:9g}'.format(median_ratio), '{:9g}'.format(UTF8_TO_PTBR_MAX_RATIO) ) ) ) # # if median(ratio_lst) <= ADDED_COLL_TIME_MAX_RATIO: # print('Duration ratio: acceptable.') # else: # print('Duration ratio: POOR: %s, more than threshold: %s' % ( '{:9g}'.format(median_ratio), '{:9g}'.format(ADDED_COLL_TIME_MAX_RATIO) ) ) # # print("\\nCheck sp_time values:" ) # for k,v in sorted(sp_time.items()): # print('k=',k,'; v=',v) # # print('\\nCheck ratio values:') # for i,p in enumerate(ratio_lst): # print( "%d : %12.2f" % (i,p) ) # print('\\nMedian value: %12.2f' % median_ratio) # #--- act_1 = python_act('db_1', substitutions=substitutions_1) expected_stdout_1 = """ Duration ratio: acceptable. """ @pytest.mark.skip('FIXME: Not IMPLEMENTED') @pytest.mark.version('>=4.0.1') def test_1(act_1: Action): pytest.fail("Not IMPLEMENTED")