diff --git a/.github/scripts/create_test_users.sh b/.github/scripts/create_test_users.sh index fe43b7467..fff44f1bd 100755 --- a/.github/scripts/create_test_users.sh +++ b/.github/scripts/create_test_users.sh @@ -86,7 +86,7 @@ PROMPT Grants for testing coverage outside of main $UT3_DEVELOP_SCHEMA schema. grant create any procedure, drop any procedure, execute any procedure, create any type, drop any type, execute any type, under any type, select any table, update any table, insert any table, delete any table, create any table, drop any table, alter any table, select any dictionary, create any synonym, drop any synonym, - grant any object privilege, grant any privilege, create public synonym, drop public synonym, create any trigger + grant any object privilege, grant any privilege, create public synonym, drop public synonym, create any trigger, drop any trigger to UT3_TESTER_HELPER; grant create job to UT3_TESTER_HELPER; diff --git a/source/core/output_buffers/ut_output_buffer_base.tpb b/source/core/output_buffers/ut_output_buffer_base.tpb index f92827d28..dc9b34b88 100644 --- a/source/core/output_buffers/ut_output_buffer_base.tpb +++ b/source/core/output_buffers/ut_output_buffer_base.tpb @@ -18,18 +18,20 @@ create or replace type body ut_output_buffer_base is member procedure init(self in out nocopy ut_output_buffer_base, a_output_id raw := null, a_self_type varchar2 := null) is pragma autonomous_transaction; - l_exists int; begin cleanup_buffer(); self.self_type := coalesce(a_self_type,self.self_type); self.output_id := coalesce(a_output_id, self.output_id, sys_guid()); self.start_date := coalesce(self.start_date, sysdate); - select /*+ no_parallel */ count(*) into l_exists from ut_output_buffer_info_tmp where output_id = self.output_id; - if ( l_exists > 0 ) then - update /*+ no_parallel */ ut_output_buffer_info_tmp set start_date = self.start_date where output_id = self.output_id; - else - insert /*+ no_parallel */ into ut_output_buffer_info_tmp(output_id, start_date) values (self.output_id, self.start_date); - end if; + merge /*+ no_parallel */ into ut_output_buffer_info_tmp dst + using (select 1 from dual) src + on (dst.output_id = self.output_id) + when matched then + update + set dst.start_date = self.start_date + when not matched then + insert (output_id, start_date) + values (self.output_id, self.start_date); commit; dbms_lock.allocate_unique( self.output_id, self.lock_handle); self.is_closed := 0; diff --git a/test/ut3_tester/core/test_output_buffer.pkb b/test/ut3_tester/core/test_output_buffer.pkb index 317e7e3d5..55670b8b8 100644 --- a/test/ut3_tester/core/test_output_buffer.pkb +++ b/test/ut3_tester/core/test_output_buffer.pkb @@ -1,5 +1,13 @@ create or replace package body test_output_buffer is + function long_text return varchar2 is + l_txt varchar2(32767); + begin + for i in 1 .. 100 loop + l_txt := l_txt||lpad('a text', 310, ',a text'); + end loop; + return l_txt; + end; procedure test_receive is l_actual_text clob; l_actual_item_type varchar2(1000); @@ -10,10 +18,10 @@ create or replace package body test_output_buffer is begin --Arrange l_buffer := ut3_develop.ut_output_clob_table_buffer(); - l_expected_text := to_clob(lpad('a text', 31000, ',a text')) - || chr(10) || to_clob(lpad('a text', 31000, ',a text')) - || chr(13) || to_clob(lpad('a text', 31000, ',a text')) - || chr(13) || chr(10) || to_clob(lpad('a text', 31000, ',a text')) || to_clob(lpad('a text', 31000, ',a text')); + l_expected_text := to_clob('a text') ||long_text() + || chr(10) || long_text() + || chr(13) || long_text() + || chr(13) || chr(10) || long_text() || long_text(); l_expected_item_type := lpad('some item type',1000,'-'); --Act l_buffer.lock_buffer(); @@ -196,5 +204,14 @@ create or replace package body test_output_buffer is ut.expect(lengthb(l_text)).to_be_less_or_equal(l_max_len); end; - end test_output_buffer; + procedure concurrent_calls_do_not_cause_exception is + l_consumer_buffer_instance ut3_develop.ut_output_buffer_base; + l_shared_output_id raw(32) := sys_guid(); + begin + ut3_tester_helper.run_helper.run_as_job('declare x ut3_develop.ut_output_buffer_base; begin x := ut3_develop.ut_output_table_buffer(a_output_id => '''||l_shared_output_id||'''); end;'); + dbms_session.sleep(0.5); + l_consumer_buffer_instance := ut3_develop.ut_output_table_buffer(a_output_id => l_shared_output_id); + end; + +end test_output_buffer; / diff --git a/test/ut3_tester/core/test_output_buffer.pks b/test/ut3_tester/core/test_output_buffer.pks index 5b6cd678b..aa5c830d3 100644 --- a/test/ut3_tester/core/test_output_buffer.pks +++ b/test/ut3_tester/core/test_output_buffer.pks @@ -53,5 +53,10 @@ create or replace package test_output_buffer is --%test(Successfully sends multibyte long clob line into text buffer) procedure text_buffer_send_clob_multib; + --%beforetest(ut3_tester_helper.run_helper.slow_down_insert_into_table_buffer) + --%aftertest(ut3_tester_helper.run_helper.cleanup_slow_down_insert_into_table_buffer) + --%test(concurrent calls to init() procedure do not cause errors - Issue #985) + procedure concurrent_calls_do_not_cause_exception; + end test_output_buffer; / diff --git a/test/ut3_tester_helper/run_helper.pkb b/test/ut3_tester_helper/run_helper.pkb index 9d65d6667..c56f31dc0 100644 --- a/test/ut3_tester_helper/run_helper.pkb +++ b/test/ut3_tester_helper/run_helper.pkb @@ -211,7 +211,7 @@ create or replace package body run_helper is begin execute immediate 'drop package ut3_user.test_db_link'; exception when others then null; end; end; - procedure create_suite_with_link is + procedure create_suite_with_link is pragma autonomous_transaction; begin create_db_link; @@ -250,7 +250,7 @@ create or replace package body run_helper is execute immediate 'grant execute on test_distributed_savepoint to public'; end; - procedure drop_suite_with_link is + procedure drop_suite_with_link is pragma autonomous_transaction; begin drop_db_link; @@ -926,5 +926,39 @@ create or replace package body run_helper is return l_result; end; + procedure slow_down_insert_into_table_buffer is + pragma autonomous_transaction; + begin + execute immediate q'[ + create or replace trigger ut3_develop.slow_down_buffer_insert + before insert on ut3_develop.ut_output_buffer_info_tmp + begin + dbms_session.sleep(1); + end;]'; + end; + + procedure cleanup_slow_down_insert_into_table_buffer is + pragma autonomous_transaction; + begin + execute immediate 'drop trigger ut3_develop.slow_down_buffer_insert'; + exception when others then + if sqlcode <> -4080 then raise; end if; + end; + + procedure run_as_job(a_job_action varchar2) is + l_job_name varchar2(50); + l_timestamp timestamp with time zone := current_timestamp; + pragma autonomous_transaction; + begin + l_job_name := 'utPLSQL__'||to_char(l_timestamp,'yyyymmddhh24missff'); + dbms_scheduler.create_job( + job_name => l_job_name, + job_type => 'PLSQL_BLOCK', + job_action => a_job_action, + start_date => l_timestamp, + enabled => TRUE, + auto_drop => TRUE + ); + end; end; / diff --git a/test/ut3_tester_helper/run_helper.pks b/test/ut3_tester_helper/run_helper.pks index ad0f615e5..6c5dd4a3b 100644 --- a/test/ut3_tester_helper/run_helper.pks +++ b/test/ut3_tester_helper/run_helper.pks @@ -74,5 +74,10 @@ create or replace package run_helper is a_name varchar2 := null ) return sys_refcursor; + procedure slow_down_insert_into_table_buffer; + + procedure cleanup_slow_down_insert_into_table_buffer; + + procedure run_as_job(a_job_action varchar2); end; /