plsql: step4 basics.
[jackhill/mal.git] / plsql / io.sql
CommitLineData
97df14cd
JM
1BEGIN
2 EXECUTE IMMEDIATE 'DROP TABLE stream';
3EXCEPTION
4 WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF;
5END;
6/
7
8CREATE TABLE stream (
9 stream_id integer,
10 open number(1,0), -- stream open (1) or closed (0)
11 data varchar2(4000), -- queued stream data
12 rl_prompt varchar2(4000) -- prompt for readline input
13);
14
15-- stdin
16INSERT INTO stream (stream_id, open, data, rl_prompt)
17 VALUES (0, 0, '', '');
18-- stdout
19INSERT INTO stream (stream_id, open, data, rl_prompt)
20 VALUES (1, 0, '', '');
21
22-- ---------------------------------------------------------
23
24CREATE OR REPLACE PROCEDURE stream_open(sid integer) AS
25BEGIN
26 -- DBMS_OUTPUT.PUT_LINE('stream_open(' || sid || ') start');
27 UPDATE stream SET data = '', rl_prompt = '', open = 1
28 WHERE stream_id = sid;
29 COMMIT;
30 -- DBMS_OUTPUT.PUT_LINE('stream_open(' || sid || ') done');
31END;
32/
33
34CREATE OR REPLACE PROCEDURE stream_close(sid integer) AS
35BEGIN
36 -- DBMS_OUTPUT.PUT_LINE('stream_close(' || sid || ') start');
37 UPDATE stream SET rl_prompt = '', open = 0
38 WHERE stream_id = sid;
39 COMMIT;
40 -- DBMS_OUTPUT.PUT_LINE('stream_close(' || sid || ') done');
41END;
42/
43
44-- stream_read:
45-- read from stream stream_id in stream table. Waits until there is
46-- either data to return or the stream closes (NULL data). Returns
47-- NULL when stream is closed.
48CREATE OR REPLACE FUNCTION stream_read(sid integer DEFAULT 0)
49RETURN varchar IS
50 PRAGMA AUTONOMOUS_TRANSACTION;
51 input varchar(4000);
52 isopen integer;
53 sleep real;
54BEGIN
55 sleep := 0.05;
56 -- poll / wait for input
57 WHILE true
58 LOOP
59 -- atomic get and set to empty
60 -- LOCK TABLE stream IN EXCLUSIVE MODE;
61 SELECT data, open INTO input, isopen FROM stream
62 WHERE stream_id = sid;
63 IF isopen = 1 AND input IS NOT NULL THEN
64 UPDATE stream SET data = '' WHERE stream_id = sid;
65 COMMIT;
66 RETURN trim(TRAILING chr(10) FROM input);
67 END IF;
68 -- '' -> no input, NULL -> stream closed
69 --RAISE NOTICE 'read input: [%] %', input, stream_id;
70 IF isopen = 0 THEN
71 raise_application_error(
72 -20000, 'stream_read: stream ''' || sid || ''' is closed', TRUE);
73 END IF;
74 SYS.DBMS_LOCK.SLEEP(sleep);
75 IF sleep < 0.5 THEN
76 sleep := sleep * 1.1; -- backoff
77 END IF;
78 END LOOP;
79END;
80/
81
82-- stream_readline:
83-- set prompt and wait for readline style input on the stream
84CREATE OR REPLACE FUNCTION stream_readline(prompt varchar, sid integer DEFAULT 0)
85RETURN varchar IS
86 PRAGMA AUTONOMOUS_TRANSACTION;
87BEGIN
88 -- set prompt / request readline style input
89 -- LOCK TABLE stream IN EXCLUSIVE MODE;
90 UPDATE stream SET rl_prompt = prompt WHERE stream_id = sid;
91 COMMIT;
92
93 RETURN stream_read(sid);
94END;
95/
96
97CREATE OR REPLACE PROCEDURE stream_write(input varchar, sid integer DEFAULT 1) AS
98 PRAGMA AUTONOMOUS_TRANSACTION;
99BEGIN
100 -- LOCK TABLE stream IN EXCLUSIVE MODE;
101 UPDATE stream SET data = data || input WHERE stream_id = sid;
102 COMMIT;
103END;
104/
105
106CREATE OR REPLACE PROCEDURE stream_writeline(data varchar, sid integer DEFAULT 1) AS
107 PRAGMA AUTONOMOUS_TRANSACTION;
108BEGIN
109 stream_write(data || chr(10), sid);
110END;
111/
112
113-- ---------------------------------------------------------
114
115-- wait_rl_prompt:
116-- wait for rl_prompt to be set on the given stream and return the
117-- rl_prompt value. Errors if stream is already closed.
118CREATE OR REPLACE FUNCTION stream_wait_rl_prompt(sid integer DEFAULT 0)
119RETURN varchar IS
120 PRAGMA AUTONOMOUS_TRANSACTION;
121 isopen integer;
122 prompt varchar(4000);
123 sleep real;
124 datas integer;
125BEGIN
126 sleep := 0.05;
127 WHILE true
128 LOOP
9fc524f1 129 LOCK TABLE stream IN EXCLUSIVE MODE;
97df14cd
JM
130 SELECT open, rl_prompt INTO isopen, prompt
131 FROM stream WHERE stream_id = sid;
132 SELECT count(data) INTO datas FROM stream WHERE data IS NOT NULL;
9fc524f1
JM
133
134 IF isopen = 0 THEN
135 raise_application_error(
136 -20000, 'stream_wait_rl_prompt: stream ''' || sid || ''' is closed', TRUE);
97df14cd 137 END IF;
9fc524f1
JM
138
139 -- wait until all channels have flushed
140 IF datas = 0 AND prompt IS NOT NULL THEN
97df14cd
JM
141 UPDATE stream SET rl_prompt = '' WHERE stream_id = sid;
142 COMMIT;
143 -- Prompt is returned single-quoted because sqlplus trims
144 -- trailing whitespace in select output.
145 RETURN '''' || prompt || '''';
146 END IF;
9fc524f1 147 COMMIT;
97df14cd
JM
148
149 DBMS_LOCK.SLEEP(sleep);
150 IF sleep < 0.5 THEN
151 sleep := sleep * 1.1; -- backoff
152 END IF;
153 END LOOP;
154END;
155/
156
157CREATE OR REPLACE PROCEDURE stream_wait_flushed(sid integer DEFAULT 1) AS
158 PRAGMA AUTONOMOUS_TRANSACTION;
159 pending integer;
160 sleep real;
161BEGIN
162 sleep := 0.05;
163 WHILE true
164 LOOP
165 SELECT count(data) INTO pending FROM stream
166 WHERE stream_id = sid AND data IS NOT NULL AND data <> '';
167 IF pending = 0 THEN RETURN; END IF;
168 DBMS_LOCK.SLEEP(sleep);
169 IF sleep < 0.5 THEN
170 sleep := sleep * 1.1; -- backoff
171 END IF;
172 END LOOP;
173END;
174/