2021-04-26 20:07:00 +02:00
#coding:utf-8
#
# id: bugs.core_4126
# title: gbak -r fails in restoring all stored procedures/functions in packages
2021-11-18 20:15:37 +01:00
# decription:
2021-04-26 20:07:00 +02:00
# Test creates table and procedure + function - both standalone and packaged.
# Then we do (with saving result in logs):
# 1) checking query;
# 2) isql -x
# After this we try to backup and restore - STDERR should be empty.
2021-11-18 20:15:37 +01:00
# Finally, we try again to run checking query and extract metadata - and compare
# their result with previously stored one.
2021-04-26 20:07:00 +02:00
# Difference between them should be EMPTY with excluding name of databases.
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# tracker_id: CORE-4126
# min_versions: ['3.0']
# versions: 3.0
# qmid: None
import pytest
2021-11-18 20:15:37 +01:00
from io import BytesIO
from difflib import unified_diff
from firebird . qa import db_factory , python_act , Action
from firebird . driver import SrvRestoreFlag
2021-04-26 20:07:00 +02:00
# version: 3.0
# resources: None
2021-11-18 20:15:37 +01:00
substitutions_1 = [ ( ' CREATE DATABASE.* ' , ' CREATE DATABASE ' ) ,
( ' CREATE COLLATION .* ' , ' CREATE COLLATION ' ) ]
2021-04-26 20:07:00 +02:00
init_script_1 = """
create or alter procedure p01 as begin end ;
create or alter function f01 ( ) returns int as begin end ;
recreate package pg_01 as begin end ;
commit ;
recreate table test ( x smallint ) ;
commit ;
set term ^ ;
execute block as
begin
begin
execute statement ' drop domain dm_nums ' ;
when any do begin end
end
begin
execute statement ' drop collation nums_coll ' ;
when any do begin end
end
end
^
set term ; ^
commit ;
create collation nums_coll for utf8 from unicode case insensitive ' NUMERIC-SORT=1 ' ;
create domain dm_nums as varchar ( 10 ) character set utf8 collate nums_coll ;
commit ;
recreate table test ( id int , s dm_nums ) ;
commit ;
set term ^ ;
create or alter procedure p01 ( a_id int ) returns ( o_s type of dm_nums ) as
begin
for select reverse ( s ) from test where id = : a_id into o_s do suspend ;
end
^
create or alter function f01 ( a_id int ) returns dm_nums as
begin
return reverse ( ( select s from test where id = : a_id ) ) ;
end
^
2021-11-18 20:15:37 +01:00
recreate package pg_01 as
begin
2021-04-26 20:07:00 +02:00
procedure p01 ( a_id int ) returns ( o_s type of dm_nums ) ;
function f01 ( a_id int ) returns dm_nums ;
end
^
create package body pg_01 as
begin
procedure p01 ( a_id int ) returns ( o_s type of dm_nums ) as
begin
for select s from test where id = : a_id into o_s do suspend ;
end
function f01 ( a_id int ) returns dm_nums as
begin
return ( select s from test where id = : a_id ) ;
end
end
^
set term ; ^
commit ;
insert into test ( id , s ) values ( 1 , ' 1234 ' ) ;
insert into test ( id , s ) values ( 2 , ' 125 ' ) ;
insert into test ( id , s ) values ( 3 , ' 16 ' ) ;
commit ;
2021-11-18 20:15:37 +01:00
"""
2021-04-26 20:07:00 +02:00
db_1 = db_factory ( sql_dialect = 3 , init = init_script_1 )
# test_script_1
#---
# import os
# import time
# import subprocess
# import difflib
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# os.environ["ISC_USER"] = user_name
# os.environ["ISC_PASSWORD"] = user_password
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# db_file=db_conn.database_name
# db_conn.close()
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# #---------------------------------------------
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# def flush_and_close(file_handle):
# # https://docs.python.org/2/library/os.html#os.fsync
2021-11-18 20:15:37 +01:00
# # If you're starting with a Python file object f,
# # first do f.flush(), and
2021-04-26 20:07:00 +02:00
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# file_handle.flush()
# if file_handle.mode not in ('r', 'rb'):
# # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno())
# file_handle.close()
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# #--------------------------------------------
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# def cleanup( f_names_list ):
# global os
# for i in range(len( f_names_list )):
# if os.path.isfile( f_names_list[i]):
# os.remove( f_names_list[i] )
# if os.path.isfile( f_names_list[i]):
# print('ERROR: can not remove file ' + f_names_list[i])
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# #--------------------------------------------
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# tmpfbk1=os.path.join(context['temp_directory'],'tmp_4126.fbk')
# if os.path.isfile(tmpfbk1):
# os.remove(tmpfbk1)
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# tmpfdb1=os.path.join(context['temp_directory'],'tmp_4126.fdb')
# if os.path.isfile(tmpfdb1):
# os.remove(tmpfdb1)
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# dml_test = ''' set list on;
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# select t.id, p.o_s as standalone_proc_result
# from test t left join p01(t.id) p on 1=1
# order by 2;
2021-11-18 20:15:37 +01:00
#
# select t.id, f01(t.id) as standalone_func_result from test t
2021-04-26 20:07:00 +02:00
# order by 2;
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# select t.id, p.o_s as packaged_proc_result
# from test t left join pg_01.p01(t.id) p on 1=1
# order by 2;
2021-11-18 20:15:37 +01:00
#
# select t.id, pg_01.f01(t.id) as packaged_func_result from test t
2021-04-26 20:07:00 +02:00
# order by 2;
# '''
# fbb=''
2021-11-18 20:15:37 +01:00
#
#
2021-04-26 20:07:00 +02:00
# # FIRST RUN DML_TEST
# ####################
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# f_run_dml_sql = open( os.path.join(context['temp_directory'],'tmp_4126_dml.sql'), 'w')
# f_run_dml_sql.write(dml_test)
# flush_and_close( f_run_dml_sql )
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# f_run_dml_log_1 = open( os.path.join(context['temp_directory'],'tmp_4126_dml_1.log'), 'w')
# subprocess.call( [context['isql_path'], dsn, "-i", f_run_dml_sql.name, "-ch", "utf8" ],
# stdout = f_run_dml_log_1,
# stderr = subprocess.STDOUT
# )
# flush_and_close( f_run_dml_log_1 )
2021-11-18 20:15:37 +01:00
#
#
2021-04-26 20:07:00 +02:00
# # EXTRACT METADATA-1
# ####################
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# f_extract_meta1_sql = open( os.path.join(context['temp_directory'],'tmp_4126_meta1.log'), 'w')
# subprocess.call( [context['isql_path'], dsn, "-x"],
# stdout = f_extract_meta1_sql,
# stderr = subprocess.STDOUT
# )
# flush_and_close( f_extract_meta1_sql )
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# # TRY TO BACKUP AND RESTORE
# ###########################
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# f_backup_log=open( os.path.join(context['temp_directory'],'tmp_4126_backup.log'), "w")
# f_backup_err=open( os.path.join(context['temp_directory'],'tmp_4126_backup.err'), "w")
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# subprocess.call([context['fbsvcmgr_path'],"localhost:service_mgr",
# "action_backup", "verbose",
# "dbname", db_file,
# "bkp_file", tmpfbk1
# ],
# stdout=f_backup_log, stderr=f_backup_err)
# flush_and_close( f_backup_log )
# flush_and_close( f_backup_err )
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# f_restore_log=open( os.path.join(context['temp_directory'],'tmp_4126_restore.log'), "w")
# f_restore_err=open( os.path.join(context['temp_directory'],'tmp_4126_restore.err'), "w")
# subprocess.call([context['fbsvcmgr_path'],"localhost:service_mgr",
# "action_restore", "res_replace", "verbose",
2021-11-18 20:15:37 +01:00
# "bkp_file", tmpfbk1,
# "dbname", tmpfdb1
2021-04-26 20:07:00 +02:00
# ],
# stdout=f_restore_log, stderr=f_restore_err)
# flush_and_close( f_restore_log )
# flush_and_close( f_restore_err )
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# # EXTRACT METADATA-2
# ####################
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# f_extract_meta2_sql = open( os.path.join(context['temp_directory'],'tmp_4126_meta2.log'), 'w')
# subprocess.call( [context['isql_path'], 'localhost:'+tmpfdb1, "-x"],
# stdout = f_extract_meta2_sql,
# stderr = subprocess.STDOUT
# )
# flush_and_close( f_extract_meta2_sql )
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# # AGAIN RUN DML_TEST
# ####################
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# f_run_dml_log_2 = open( os.path.join(context['temp_directory'],'tmp_4126_dml_2.log'), 'w')
# subprocess.call( [context['isql_path'], 'localhost:'+tmpfdb1, "-i", f_run_dml_sql.name, "-ch", "utf8" ],
# stdout = f_run_dml_log_2,
# stderr = subprocess.STDOUT
# )
# flush_and_close( f_run_dml_log_2 )
2021-11-18 20:15:37 +01:00
#
#
2021-04-26 20:07:00 +02:00
# # 7. CHECKS
# ###########
# # 1) STDERR for backup and restore - they all must be EMPTY.
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# f_list=[f_backup_err, f_restore_err]
# for i in range(len(f_list)):
# f_name=f_list[i].name
# if os.path.getsize(f_name) > 0:
# with open( f_name,'r') as f:
# for line in f:
# print("Unexpected STDERR, file "+f_name+": "+line)
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# # 2) diff between dml_1.log and dml_2.log should be EMPTY.
# # 3) diff between meta1.log and meta2.log should be EMPTY.
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# f_diff_txt=open( os.path.join(context['temp_directory'],'tmp_4126_diff.txt'), 'w')
# f_old=[]
# f_new=[]
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# f_old.append(f_extract_meta1_sql) # DDL: what we have BEFORE database backup
# f_old.append(f_run_dml_log_1) # DML: result of querying tables before DB backup
# f_new.append(f_extract_meta2_sql) # DDL: what we have AFTER database restore
# f_new.append(f_run_dml_log_2) # DML: result of querying tables AFTER database restore
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# for i in range(len(f_old)):
# old_file=open(f_old[i].name,'r')
# new_file=open(f_new[i].name,'r')
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# f_diff_txt.write( ''.join( difflib.unified_diff( old_file.readlines(), new_file.readlines() ) ) )
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# old_file.close()
# new_file.close()
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# flush_and_close( f_diff_txt )
2021-11-18 20:15:37 +01:00
#
2021-04-26 20:07:00 +02:00
# # Should be EMPTY:
# ##################
# with open( f_diff_txt.name,'r') as f:
# for line in f:
# if line.split() and not('---' in line or '+++' in line or '@@' in line or 'SET SQL DIALECT' in line or 'CREATE DATABASE' in line or 'collation'.upper() in line.upper()):
# print( 'Unexpected diff: ' + line )
2021-11-18 20:15:37 +01:00
#
#
2021-04-26 20:07:00 +02:00
# # Cleanup:
# ##########
# cleanup( [i.name for i in (f_backup_log, f_backup_err, f_restore_log, f_restore_err, f_extract_meta1_sql, f_extract_meta2_sql, f_run_dml_sql, f_run_dml_log_1, f_run_dml_log_2, f_diff_txt) ] )
2021-11-18 20:15:37 +01:00
#
# os.remove(tmpfdb1)
2021-04-26 20:07:00 +02:00
# os.remove(tmpfbk1)
2021-11-18 20:15:37 +01:00
#
#
2021-04-26 20:07:00 +02:00
#---
2021-11-18 20:15:37 +01:00
act_1 = python_act ( ' db_1 ' , substitutions = substitutions_1 )
2021-04-26 20:07:00 +02:00
@pytest.mark.version ( ' >=3.0 ' )
2021-11-18 20:15:37 +01:00
def test_1 ( act_1 : Action ) :
dml_test = '''
set list on ;
select t . id , p . o_s as standalone_proc_result
from test t left join p01 ( t . id ) p on 1 = 1
order by 2 ;
select t . id , f01 ( t . id ) as standalone_func_result from test t
order by 2 ;
2021-04-26 20:07:00 +02:00
2021-11-18 20:15:37 +01:00
select t . id , p . o_s as packaged_proc_result
from test t left join pg_01 . p01 ( t . id ) p on 1 = 1
order by 2 ;
2021-04-26 20:07:00 +02:00
2021-11-18 20:15:37 +01:00
select t . id , pg_01 . f01 ( t . id ) as packaged_func_result from test t
order by 2 ;
'''
# gather metadta and test cript result before backup & restore
act_1 . isql ( switches = [ ' -x ' ] )
meta_before = act_1 . stdout
act_1 . reset ( )
act_1 . isql ( switches = [ ] , input = dml_test )
dml_before = act_1 . stdout
#
backup = BytesIO ( )
with act_1 . connect_server ( ) as srv :
srv . database . local_backup ( database = str ( act_1 . db . db_path ) , backup_stream = backup )
backup . seek ( 0 )
srv . database . local_restore ( database = str ( act_1 . db . db_path ) , backup_stream = backup ,
flags = SrvRestoreFlag . REPLACE )
# gather metadta and test cript result after backup & restore
act_1 . reset ( )
act_1 . isql ( switches = [ ' -x ' ] )
meta_after = act_1 . stdout
act_1 . reset ( )
act_1 . isql ( switches = [ ] , input = dml_test )
dml_after = act_1 . stdout
# check
assert list ( unified_diff ( meta_before , meta_after ) ) == [ ]
assert list ( unified_diff ( dml_before , dml_after ) ) == [ ]