1
1
mirror of https://github.com/kanaka/mal.git synced 2024-09-21 10:37:58 +03:00
mal/plsql/io.sql
2016-05-22 00:16:01 -06:00

175 lines
5.1 KiB
SQL

BEGIN
EXECUTE IMMEDIATE 'DROP TABLE stream';
EXCEPTION
WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF;
END;
/
CREATE TABLE stream (
stream_id integer,
open number(1,0), -- stream open (1) or closed (0)
data varchar2(4000), -- queued stream data
rl_prompt varchar2(4000) -- prompt for readline input
);
-- stdin
INSERT INTO stream (stream_id, open, data, rl_prompt)
VALUES (0, 0, '', '');
-- stdout
INSERT INTO stream (stream_id, open, data, rl_prompt)
VALUES (1, 0, '', '');
-- ---------------------------------------------------------
CREATE OR REPLACE PROCEDURE stream_open(sid integer) AS
BEGIN
-- DBMS_OUTPUT.PUT_LINE('stream_open(' || sid || ') start');
UPDATE stream SET data = '', rl_prompt = '', open = 1
WHERE stream_id = sid;
COMMIT;
-- DBMS_OUTPUT.PUT_LINE('stream_open(' || sid || ') done');
END;
/
CREATE OR REPLACE PROCEDURE stream_close(sid integer) AS
BEGIN
-- DBMS_OUTPUT.PUT_LINE('stream_close(' || sid || ') start');
UPDATE stream SET rl_prompt = '', open = 0
WHERE stream_id = sid;
COMMIT;
-- DBMS_OUTPUT.PUT_LINE('stream_close(' || sid || ') done');
END;
/
-- stream_read:
-- read from stream stream_id in stream table. Waits until there is
-- either data to return or the stream closes (NULL data). Returns
-- NULL when stream is closed.
CREATE OR REPLACE FUNCTION stream_read(sid integer DEFAULT 0)
RETURN varchar IS
PRAGMA AUTONOMOUS_TRANSACTION;
input varchar(4000);
isopen integer;
sleep real;
BEGIN
sleep := 0.05;
-- poll / wait for input
WHILE true
LOOP
-- atomic get and set to empty
-- LOCK TABLE stream IN EXCLUSIVE MODE;
SELECT data, open INTO input, isopen FROM stream
WHERE stream_id = sid;
IF isopen = 1 AND input IS NOT NULL THEN
UPDATE stream SET data = '' WHERE stream_id = sid;
COMMIT;
RETURN trim(TRAILING chr(10) FROM input);
END IF;
-- '' -> no input, NULL -> stream closed
--RAISE NOTICE 'read input: [%] %', input, stream_id;
IF isopen = 0 THEN
raise_application_error(
-20000, 'stream_read: stream ''' || sid || ''' is closed', TRUE);
END IF;
SYS.DBMS_LOCK.SLEEP(sleep);
IF sleep < 0.5 THEN
sleep := sleep * 1.1; -- backoff
END IF;
END LOOP;
END;
/
-- stream_readline:
-- set prompt and wait for readline style input on the stream
CREATE OR REPLACE FUNCTION stream_readline(prompt varchar, sid integer DEFAULT 0)
RETURN varchar IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
-- set prompt / request readline style input
-- LOCK TABLE stream IN EXCLUSIVE MODE;
UPDATE stream SET rl_prompt = prompt WHERE stream_id = sid;
COMMIT;
RETURN stream_read(sid);
END;
/
CREATE OR REPLACE PROCEDURE stream_write(input varchar, sid integer DEFAULT 1) AS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
-- LOCK TABLE stream IN EXCLUSIVE MODE;
UPDATE stream SET data = data || input WHERE stream_id = sid;
COMMIT;
END;
/
CREATE OR REPLACE PROCEDURE stream_writeline(data varchar, sid integer DEFAULT 1) AS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
stream_write(data || chr(10), sid);
END;
/
-- ---------------------------------------------------------
-- wait_rl_prompt:
-- wait for rl_prompt to be set on the given stream and return the
-- rl_prompt value. Errors if stream is already closed.
CREATE OR REPLACE FUNCTION stream_wait_rl_prompt(sid integer DEFAULT 0)
RETURN varchar IS
PRAGMA AUTONOMOUS_TRANSACTION;
isopen integer;
prompt varchar(4000);
sleep real;
datas integer;
BEGIN
sleep := 0.05;
WHILE true
LOOP
LOCK TABLE stream IN EXCLUSIVE MODE;
SELECT open, rl_prompt INTO isopen, prompt
FROM stream WHERE stream_id = sid;
SELECT count(data) INTO datas FROM stream WHERE data IS NOT NULL;
IF isopen = 0 THEN
raise_application_error(
-20000, 'stream_wait_rl_prompt: stream ''' || sid || ''' is closed', TRUE);
END IF;
-- wait until all channels have flushed
IF datas = 0 AND prompt IS NOT NULL THEN
UPDATE stream SET rl_prompt = '' WHERE stream_id = sid;
COMMIT;
-- Prompt is returned single-quoted because sqlplus trims
-- trailing whitespace in select output.
RETURN '''' || prompt || '''';
END IF;
COMMIT;
DBMS_LOCK.SLEEP(sleep);
IF sleep < 0.5 THEN
sleep := sleep * 1.1; -- backoff
END IF;
END LOOP;
END;
/
CREATE OR REPLACE PROCEDURE stream_wait_flushed(sid integer DEFAULT 1) AS
PRAGMA AUTONOMOUS_TRANSACTION;
pending integer;
sleep real;
BEGIN
sleep := 0.05;
WHILE true
LOOP
SELECT count(data) INTO pending FROM stream
WHERE stream_id = sid AND data IS NOT NULL AND data <> '';
IF pending = 0 THEN RETURN; END IF;
DBMS_LOCK.SLEEP(sleep);
IF sleep < 0.5 THEN
sleep := sleep * 1.1; -- backoff
END IF;
END LOOP;
END;
/