Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a9614c0

Browse files
committed
Added ut_output_dbms_pipe as additional output. Added ut_output_pipe_helper.
1 parent 640806b commit a9614c0

5 files changed

Lines changed: 136 additions & 137 deletions

File tree

source/core/types/ut_output_dbms_pipe.tpb

Lines changed: 33 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,12 @@ create or replace type body ut_output_dbms_pipe as
88
end;
99

1010
overriding member procedure open(self in out nocopy ut_output_dbms_pipe) is
11-
l_buffer_size_bytes integer := 100 * 1024 * 1024;
1211
l_flag integer;
1312
begin
14-
--create an explixit private pipe
15-
--explicitly created pipes need to be removed explicitly
16-
--otherwise they stay in memmory forever
17-
--https://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_pipe.htm#CHDEICJI
18-
--to check ig there are any non-purged pipes execute:
19-
--select * from v$db_pipes where pipe_size > 0 order by name desc;
20-
l_flag := dbms_pipe.create_pipe(self.output_id, l_buffer_size_bytes);
13+
--create an explicit private pipe. Explicitly created pipes need to be removed explicitly
14+
--otherwise they stay in memmory forever https://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_pipe.htm#CHDEICJI
15+
--to check if there are any non-purged pipes execute: select * from v$db_pipes where pipe_size > 0 order by name desc;
16+
l_flag := dbms_pipe.create_pipe(self.output_id);
2117
end;
2218

2319
overriding member procedure send(self in out nocopy ut_output_dbms_pipe, a_text clob) is
@@ -26,57 +22,55 @@ create or replace type body ut_output_dbms_pipe as
2622
i integer := 0;
2723
begin
2824
--split test into pieces of a size valid for pipe and send to pipe
29-
while i <= length(a_text) loop
25+
while i < length(a_text) loop
3026
l_text_part := substr( a_text, i + 1, c_size_limit_chars );
3127
ut_output_pipe_helper.send( self.output_id, l_text_part);
3228
i := i + c_size_limit_chars;
3329
end loop;
34-
ut_output_pipe_helper.send( self.output_id, ut_utils.gc_output_eom );
30+
--SEND is closed by a EOM message
31+
ut_output_pipe_helper.send( self.output_id, ut_output_pipe_helper.gc_output_eom );
3532
end;
3633

3734
overriding member procedure close(self in out nocopy ut_output_dbms_pipe) is
3835
begin
39-
self.send(ut_utils.gc_output_eot);
36+
ut_output_pipe_helper.send(self.output_id, ut_output_pipe_helper.gc_output_eot);
37+
ut_output_pipe_helper.flush(self.output_id);
4038
end;
4139

4240
static function get_lines(a_output_id varchar2, a_timeout_sec integer := 60*60*4) return ut_output_clob_list pipelined is
4341
l_flag integer;
44-
l_text_part varchar2(4000 byte);
42+
l_text_part ut_output_pipe_helper.t_pipe_item;
4543
l_text clob;
4644
l_timeout_occured boolean;
45+
l_need_to_send boolean;
4746
begin
48-
if a_output_id is null then
49-
return;
50-
end if;
51-
loop
52-
dbms_lob.createtemporary(l_text, true);
47+
if a_output_id is not null then
48+
5349
loop
54-
l_timeout_occured := dbms_pipe.receive_message(a_output_id, a_timeout_sec) != 0;
55-
exit when l_timeout_occured;
50+
dbms_lob.createtemporary(l_text, true);
51+
l_need_to_send := false;
52+
53+
--build a row. As a rule one call to 'send' is one row.
54+
loop
55+
l_timeout_occured := dbms_pipe.receive_message(a_output_id, a_timeout_sec) != 0;
56+
exit when l_timeout_occured;
57+
dbms_pipe.unpack_message(l_text_part);
58+
exit when ( l_text_part in (ut_output_pipe_helper.gc_output_eom, ut_output_pipe_helper.gc_output_eot) );
59+
dbms_lob.writeappend(l_text, length(l_text_part), l_text_part);
60+
l_need_to_send := true;
61+
end loop;
62+
63+
if l_need_to_send then
64+
pipe row( l_text );
65+
end if;
5666

57-
dbms_pipe.unpack_message(l_text_part);
58-
exit when (l_text_part = ut_utils.gc_output_eom or l_text_part = ut_utils.gc_output_eot);
59-
dbms_lob.writeappend(l_text,length(l_text_part),l_text_part);
67+
exit when (l_timeout_occured or l_text_part = ut_output_pipe_helper.gc_output_eot);
6068
end loop;
61-
pipe row( l_text );
62-
exit when (l_text_part = ut_utils.gc_output_eot);
63-
end loop;
64-
l_flag := dbms_pipe.remove_pipe(a_output_id);
65-
return;
66-
end;
6769

68-
member procedure to_screen(self in ut_output_dbms_pipe) is
69-
begin
70-
for i in (select column_value as text_line from table( ut_output_dbms_pipe.get_lines( self.output_id ) ) ) loop
71-
dbms_output.put_line(i.text_line);
72-
end loop;
70+
l_flag := dbms_pipe.remove_pipe(a_output_id);
71+
end if;
72+
return;
7373
end;
7474

75-
static procedure to_screen(a_output_id varchar2) is
76-
begin
77-
for i in (select column_value as text_line from table( ut_output_dbms_pipe.get_lines( a_output_id ) ) ) loop
78-
dbms_output.put_line(i.text_line);
79-
end loop;
80-
end;
8175
end;
8276
/

source/core/types/ut_output_dbms_pipe.tps

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ create or replace type ut_output_dbms_pipe under ut_output (
33
overriding member procedure open(self in out nocopy ut_output_dbms_pipe),
44
overriding member procedure send(self in out nocopy ut_output_dbms_pipe, a_text clob),
55
overriding member procedure close(self in out nocopy ut_output_dbms_pipe),
6-
static function get_lines(a_output_id varchar2, a_timeout_sec integer := 60*60*4) return ut_output_clob_list pipelined,
7-
member procedure to_screen(self in ut_output_dbms_pipe),
8-
static procedure to_screen(a_output_id varchar2)
6+
static function get_lines(a_output_id varchar2, a_timeout_sec integer := 60*60*4) return ut_output_clob_list pipelined
97
) not final
108
/

source/core/ut_output_pipe_helper.pkb

Lines changed: 88 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
create or replace package body ut_output_pipe_helper is
22

3+
-- private
34

45
type tt_pipe_buffer is table of varchar2(4000);
56

67
type t_output_buffer is record(
7-
to_close boolean := false,
8+
to_flush boolean := false,
89
data tt_pipe_buffer
910
);
1011

11-
type tt_outputs_buffer is table of t_output_buffer index by varchar2(128);
12+
type tt_outputs_buffer is table of t_output_buffer index by t_output_id;
1213

1314
g_outputs_buffer tt_outputs_buffer;
1415

15-
function send_from_buffer(a_output_id varchar2, a_buffer in out nocopy tt_pipe_buffer) return boolean is
16+
--sends all data from buffer to pipe and returns false if timeout occured
17+
function send_from_buffer(a_output_id t_output_id, a_buffer in out nocopy tt_pipe_buffer, a_timeout_seconds naturaln := 0) return boolean is
1618
l_is_successful boolean := true;
1719
l_first_item integer;
1820
l_last_item integer;
@@ -25,7 +27,7 @@ create or replace package body ut_output_pipe_helper is
2527
-- exit loop on timeout
2628
while i is not null loop
2729
dbms_pipe.pack_message( a_buffer(i) );
28-
l_is_successful := dbms_pipe.send_message(a_output_id, 0) = 0;
30+
l_is_successful := dbms_pipe.send_message(a_output_id, a_timeout_seconds) = 0;
2931
exit when not l_is_successful;
3032
l_last_item := i;
3133
i := a_buffer.next(i);
@@ -35,93 +37,117 @@ create or replace package body ut_output_pipe_helper is
3537
if not l_is_successful then
3638
a_buffer.delete(l_first_item, i-1);
3739
else
38-
a_buffer.delete();
40+
a_buffer.delete;
3941
end if;
4042
dbms_pipe.reset_buffer;
4143
return l_is_successful;
4244
end if;
4345
return false;
4446
end;
4547

46-
function send_from_buffer(a_output_id varchar2) return boolean is
48+
--sends all data from an output buffer to pipe and returns false if timeout occured
49+
function send_from_buffer(a_output_id t_output_id, a_timeout_seconds naturaln := 0) return boolean is
4750
begin
4851
if g_outputs_buffer.exists(a_output_id) then
49-
return send_from_buffer(a_output_id, g_outputs_buffer(a_output_id).data);
52+
return send_from_buffer(a_output_id, g_outputs_buffer(a_output_id).data, a_timeout_seconds);
5053
end if;
5154
return false;
5255
end;
5356

54-
--sends a message to a named pipe
55-
--and if sending fails it writes the message to the end of the buffer for pipe
56-
procedure send(a_output_id varchar2, a_text t_pipe_item) is
57-
l_is_successful boolean;
57+
--iterates through all the output buffers that were not purged
58+
--for each buffer tries to send the content of the buffer
59+
--if all content was sent, buffer is recycled (removed)
60+
function flush_buffers(a_timeout_seconds naturaln := 0) return boolean is
61+
l_output_id t_output_id;
62+
l_output_id_to_delete t_output_id;
63+
l_pipe_removal_status integer;
64+
l_timed_out boolean := false;
5865
begin
59-
buffer(a_output_id, a_text);
60-
l_is_successful := send_from_buffer(a_output_id);
66+
l_output_id := g_outputs_buffer.first;
67+
while l_output_id is not null loop
68+
l_output_id_to_delete := null;
69+
if send_from_buffer(l_output_id, a_timeout_seconds) = true then
70+
l_output_id_to_delete := l_output_id;
71+
else
72+
l_timed_out := true;
73+
end if;
74+
l_output_id := g_outputs_buffer.next(l_output_id);
75+
76+
if l_output_id_to_delete is not null then
77+
g_outputs_buffer.delete(l_output_id_to_delete);
78+
end if;
79+
80+
end loop;
81+
return not l_timed_out;
82+
end;
83+
84+
--check if all the buffers are waiting to be flushed
85+
function all_buffers_to_flush return boolean is
86+
l_output_id t_output_id;
87+
begin
88+
l_output_id := g_outputs_buffer.first;
89+
while l_output_id is not null loop
90+
if not g_outputs_buffer(l_output_id).to_flush then
91+
return false;
92+
end if;
93+
l_output_id := g_outputs_buffer.next(l_output_id);
94+
end loop;
95+
return true;
6196
end;
6297

63-
--writes the message to the end of the buffer for pipe
64-
procedure buffer(a_output_id varchar2, a_text t_pipe_item) is
98+
--remove all the pipes anf purges all the buffers
99+
procedure purge is
100+
l_pipe_removal_status integer;
101+
l_output_id t_output_id;
102+
begin
103+
l_output_id := g_outputs_buffer.first;
104+
while l_output_id is not null loop
105+
l_output_id := g_outputs_buffer.next(l_output_id);
106+
end loop;
107+
g_outputs_buffer.delete;
108+
end;
109+
110+
--writes the message to the end of the buffer
111+
procedure buffer(a_output_id t_output_id, a_text t_pipe_item) is
65112
begin
66113
if not g_outputs_buffer.exists(a_output_id) then
67114
g_outputs_buffer(a_output_id).data := tt_pipe_buffer();
115+
g_outputs_buffer(a_output_id).to_flush := false;
68116
end if;
69117
g_outputs_buffer(a_output_id).data.extend;
70118
g_outputs_buffer(a_output_id).data(g_outputs_buffer(a_output_id).data.last) := a_text;
71119
end;
72120

73-
procedure cleanup_outputs_to_close is
74-
l_output_id varchar2(128);
75-
l_output_id_to_delete varchar2(128);
121+
122+
---public
123+
124+
--adds message to pipe buffer and tries to sent all messages from the buffer
125+
--exists immediately when sending timesout (pipe full)
126+
--the messages that were sent are removed from buffer
127+
procedure send(a_output_id t_output_id, a_text t_pipe_item) is
128+
l_is_successful boolean;
76129
begin
77-
l_output_id := g_outputs_buffer.first;
78-
while i is not null loop
79-
l_output_id_to_delete := null;
80-
if g_outputs_buffer(l_output_id).to_close then
81-
if send_from_buffer(l_output_id) = true then
82-
l_output_id_to_delete := l_output_id;
83-
end if;
84-
end if;
85-
l_output_id := g_outputs_buffer.next(l_output_id);
86-
g_outputs_buffer.delete(l_output_id_to_delete);
87-
end loop;
130+
buffer(a_output_id, a_text);
131+
l_is_successful := send_from_buffer(a_output_id);
88132
end;
89133

90-
--procedure cleanup_aged_pipes() - check for pipes that are aged out.
91-
92-
93-
--registers a close request and tries to close a pipe
94-
--by first sending out all messages remaining in the buffers
95-
--the procedure holds infomrmation about closure requests
96-
--so that if close is not possble as buffer was not yet flusehd
97-
--it will proceed to other close requests
98-
--scenario with failure:
99-
-- two buffers used: output_1, output_2
100-
-- call is made to close output_1
101-
-- register the close request
102-
-- send all messaged from output_1 buffer
103-
-- if buffer not empty and timeout
104-
-- go to output_2
105-
-- if output_2 is still open, return
106-
-- so the outout_1 close request is in a pending state
107-
-- next a call should be made from framework to close output_2
108-
-- register the close request
109-
-- send all messaged from output_2 buffer
110-
-- if buffer not empty and timeout
111-
-- go to output_1
112-
-- if output_1 has a pending close
113-
-- try to send from output_1 buffer
114-
-- if timeout -> raise
115-
--scenario with success:
116-
-- two buffers used: output_1, output_2
117-
procedure request_close(a_output_id varchar2, a_text varchar2) is
134+
--marks a buffer as ready to be flushed
135+
--tries to flush all the data from all the outputs buffers to the pipes immediately
136+
--in case, all buffers outputs are to be flused, it will try with a timeout.
137+
procedure flush(a_output_id t_output_id, a_timeout_seconds naturaln := gc_flush_timeout_seconds) is
118138
begin
119-
cleanup_outputs_to_close;
120-
--if there is any output in a state to_close = false
121-
--exit
122-
--else loop with a wait time of 20 seconds for each to_close output
139+
if g_outputs_buffer.exists(a_output_id) then
140+
g_outputs_buffer(a_output_id).to_flush := true;
141+
end if;
123142

124-
--if unable to send all messages after timeout, then finish?
143+
if (flush_buffers() = false) and all_buffers_to_flush then
144+
--try as many times as there are seconds for timeout
145+
--each time try with one second delay
146+
for i in 1 .. a_timeout_seconds loop
147+
exit when flush_buffers(a_timeout_seconds => 1) = true;
148+
end loop;
149+
purge();
150+
end if;
125151

126152
end;
127153

source/core/ut_output_pipe_helper.pks

Lines changed: 14 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,26 @@ create or replace package ut_output_pipe_helper is
22

33
--the limit size for pipe message is 4096, we want to make sure we're below that limit
44
subtype t_pipe_item is varchar(4000 byte);
5+
subtype t_output_id is varchar2(128 byte);
56

6-
--we're assuming max of 4 bytes per char
7-
gc_size_limit_chars constant integer := 1000;
7+
--we're assuming max of 2 bytes per char
8+
gc_size_limit_chars constant integer := 4000/2;
9+
10+
--decides how long the process will be waiting to flush buffers
11+
gc_flush_timeout_seconds constant natural := 60;
12+
gc_output_eom constant varchar2(30) := '[{-end-of-message-}]';
13+
gc_output_eot constant varchar2(30) := '[{-end-of-transmission-}]';
814

915
--adds message to pipe buffer and tries to sent all messages from the buffer
1016
--exists immediately when sending timesout (pipe full)
1117
--the messages that were sent are removed from buffer
12-
procedure send(a_output_id varchar2, a_text t_pipe_item);
13-
14-
--writes the message to the end of the buffer for pipe
15-
procedure buffer(a_output_id varchar2, a_text t_pipe_item);
18+
procedure send(a_output_id t_output_id, a_text t_pipe_item);
1619

17-
--registers a close request and tries to close a pipe
18-
--by first sending out all messages remaining in the buffers
19-
--the procedure holds infomrmation about closure requests
20-
--so that if close is not possble as buffer was not yet flusehd
21-
--it will proceed to other close requests
22-
--scenario with failure:
23-
-- two buffers used: output_1, output_2
24-
-- call is made to close output_1
25-
-- register the close request
26-
-- send all messaged from output_1 buffer
27-
-- if buffer not empty and timeout
28-
-- go to output_2
29-
-- if output_2 is still open, return
30-
-- so the outout_1 close request is in a pending state
31-
-- next a call should be made from framework to close output_2
32-
-- register the close request
33-
-- send all messaged from output_2 buffer
34-
-- if buffer not empty and timeout
35-
-- go to output_1
36-
-- if output_1 has a pending close
37-
-- try to send from output_1 buffer
38-
-- if timeout -> raise
39-
--scenario with success:
40-
-- two buffers used: output_1, output_2
41-
procedure request_close(a_output_id varchar2, a_text varchar2);
20+
--marks a buffer as ready to be flushed and then
21+
--tries to flush all the data from all the outputs buffers to the pipes
22+
--in case, all buffers outputs are to be flushed, it will try until a timeout occurs.
23+
-- If timed out, the open pies get purged and closed
24+
procedure flush(a_output_id t_output_id, a_timeout_seconds naturaln := gc_flush_timeout_seconds);
4225

4326
end;
4427
/

source/core/ut_utils.pks

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ create or replace package ut_utils authid definer is
4040
gc_timestamp_format constant varchar2(100) := 'yyyy-mm-dd"T"hh24:mi:ssxff';
4141
gc_timestamp_tz_format constant varchar2(100) := 'yyyy-mm-dd"T"hh24:mi:ssxff tzh:tzm';
4242
gc_null_string constant varchar2(4) := 'NULL';
43-
gc_output_eom constant varchar2(30) := '[{-end-of-message-}]';
44-
gc_output_eot constant varchar2(30) := '[{-end-of-transmission-}]';
4543
/*
4644
Function: test_result_to_char
4745
returns a string representation of a test_result.

0 commit comments

Comments
 (0)